スイカゲーム風「ベジタブルゲーム」の作り方 Vol.04

前回作成した野菜の素材にプログラムを追加していきます。

その他のベジタブルゲームの作り方の記事

野菜のプログラム

変数の宣言と初期値

ここで使用する変数を確認します。

変数名アクセス説明
rbRigidbody2DprivateRigidbody2D を管理するための変数
craneGameObjectprivateクレーンのオブジェクト
trayGameObjectprivateトレイのオブジェクト
indexintpublic自分自身が何番目の野菜なのかを管理する変数

さらにこの野菜のオブジェクトは、現状どのような状態なのかを 列挙型変数:MOVETYPE で管理したいと思います。さらにこの列挙型変数を管理するための MOVETYPE型変数:type も用意しておきます。MOVETYPEで管理するゲーム中の野菜の状態は次の4つになります。

要素名状態
NEXT次に登場する野菜(生成されたときの状態)
READYクレーンに運ばれて、落下するのを待っている状態
FALLクレーンから離れて落下中の状態
STAY落下後、他のオブジェクトに接触した後の状態

では VegetableManager.cs を立ち上げて、変数の宣言と初期値の指定を行います。

Rigidbody2D rb;          //Rigidbody2D を管理するための変数
GameObject crane, Tray;  //CraneとTrayオブジェクト
public int index;        //オブジェクト自身の野菜の管理番号(0~11) 
public enum MOVETYPE { NEXT,READY,FALL,STAY};  //野菜の動きの状態を管理する列挙型変数  
public MOVETYPE type = MOVETYPE.NEXT;   //初期状態はNEXT 

void Start()
{
    rb = GetComponent<Rigidbody2D>();   //Rigidbody2Dを取得し代入
    crane = GameObject.Find("Crane");   //Craneを探して代入
    tray = GameObject.Find("Tray");     //Craneを探して代入
}

MOVETYPEの振り分け

続いて、各状態のケース、それぞれの座標や重力の状態を Switch()文で振り分けます。
各状態での Rigidbody2D の BodyType や重力、座標情報等は以下の通りです。

MOVETYPEBodytype重力座標
NEXTStatic0次の野菜が配置される場所に配置
READYKinematic0常にクレーンの0.7だけ下側
FALLDinamic0.6
STAYDinamic0.3高さが3.5より上、または壁の外に出たらゲームオーバー
void StateSet()
{ 
    switch (type)               //type の値によって振分け 
    { 
        case MOVETYPE.NEXT:     //NEXT のケース 
            //BodyType を Static(Rigidbody2Dの影響なし)に 
            rb.bodyType = RigidbodyType2D.Static;
            transform.position = tray.transform.position;
            break; 
        case MOVETYPE.READY:    //READY のケース 
            //BodyType を Kinematic(スクリプト文の影響のみ)に 
            rb.bodyType = RigidbodyType2D.Kinematic; //座標を常にクレーンの0.7下側に配置
            transform.position = crane.transform.position - Vector3.up * 0.7f;
            break; 
        case MOVETYPE.FALL:     //FALL のケース 
            //BodyType を Dynamic(Rigidbody2Dの影響を受ける)に 
            rb.bodyType = RigidbodyType2D.Dynamic;
            rb.gravityScale = 0.6f;     //重力を0.6に 
            break; 
        case MOVETYPE.STAY:     //STAY のケース 
            rb.gravityScale = 0.3f;     //重力を0.3に
            //もし高さが3.5より大きい、または壁の外に出たら 
            if (transform.position.y > 3.5f || Mathf.Abs(transform.position.x) > 5) 
            { 
                Debug.Log("GameOver"); //GameOver と標示 
            } break; 
     } 
} 

void Update() 
{ 
    StateSet(); //StateSet()を呼び出す 
}

Typeの変化1 GameMangerの準備

ここでは野菜がクローンとしてトレイの場所に生成され、クレーンに移動し、落下するまでの流れを確認します。

スイカゲーム風ベジタブルゲーム 野菜の進化のフロー

上の図の様に、個々の野菜の状態と、全体の状態を管理する isReady というフラグが必要になってきます。個々の野菜のプレハブは生成されたり、削除されたりするので、全体を管理することが出来ません。

そこで野菜を生成や状態を管理するフラグを管理する役割を担う、GameManager を用意して、ゲームの進行を任せます。

また野菜のオブジェクトが生成され、クレーンで運ばれ落下した後に、落下したオブジェクトを子要素としてまとめておくための Vegetables も準備しておきます。

Hierarchy上に空のオブジェクトとして、GameManager、Vegetables を作成します。

スイカゲーム風ベジタブルゲームの作り方 オブジェクト配置

続いて同様にスクリプトファイル GameManager を作成し、作成したGameManagerオブジェクトにアタッチします。

GameManagerクラスで使用する変数、及び配列は以下の通りです。まず、上で作成した野菜のプレハブを配列で管理します。

配列名アクセス説明
v_PrefabGameObjectpublic野菜のプレハブ(12個)

また使用する変数は以下の通りです。

変数名アクセス説明
isReadyboolpublic野菜生成のタイミングを管理
v_indexintprivate生成する野菜の番号を管理
createPosVector3private野菜が生成される場所を管理
trayGameObjectprivateトレイのオブジェクト
craneGameObjectprivateクレーンのオブジェクト
vegetablesGameObjectprivate落下した野菜を子要素にする親オブジェクト

GameManager.cs を立ち上げて、変数の宣言と初期値の取得を行います。

public GameObject[] v_Prefab= new GameObject[12];   //野菜プレハブ
public bool isReady;                    //生成を管理するフラグ 
int v_index;                            //生成する野菜の番号 
Vector3 createPos;                      //生成する座標 
GameObject tray, crane, vegetables;     //各オブジェクト 

void Start() 
{
    isReady = true;                     //最初は生成できる状態
    tray = GameObject.Find("Tray");     //Tray を探して代入 
    crane = GameObject.Find("Crane");   //Crane を探して代入
    vegetables = GameObject.Find("Vegetables"); //Vegetablesを探して代入 
}

作成した配列:v_Prefab に、GameManager の Inspector から野菜のプレハブを以下のような順番に配置します。

スイカゲーム風ベジタブルゲームの作り方 配列へのプレハブ配置

Typeの変化2 野菜生成

野菜の生成は GameManagerクラスが行います。
この後の展開を考えると、野菜の生成は次の2パターンが考えられます。

  1. 新たな野菜としてトレイ上に生成
  2. 2つの同じ野菜が接触したときに、次の(進化した)野菜として生成

この2パターンも考慮し、以下の3つの引数を持たせた野菜生成の関数を用意します。

  1. どちらのパターンで(int型 0:新規生成モード、1:進化生成モード)
  2. 何番目の野菜を(int型:0~11)
  3. どこに(Vector3型)

では上の内容を確認し、GameManagerクラスに野菜を生成する 関数:CreateVegetable(int t, int v, Vector3 p) を作成します。

public void CreateVegetable(int t, int v, Vector3 p) 
{
    //引数受け取った引数番目の野菜を、受け取った場所に生成 
    GameObject vege = Instantiate(v_Prefab[v],p,Quaternion.identity);
    //生成したオブジェクトからVegetableManagerクラスを取得
    VegetableManager vm = vege.GetComponent<VegetableManager>();
    vm.index = v;       //生成されたオブジェクトの index を受け取った引数 v に指定 
    if(t==0)            //新規生成モードなら 
    { 
        vm.type = VegetableManager.MOVETYPE.NEXT;   //type をMOVETYPE.NEXT に指定
        vege.GetComponent<CircleCollider2D>().enabled = false;  //当たり判定を無効に
        vege.transform.SetParent(tray.transform);   //vege をTrayの子要素に指定 
    } 
}

Update()関数では、変数:isReadytrue でTrayの子要素が無い状態の時に、関数:CreateVegetable()を新規作成モードで作成したを呼び出します。

この時に選ばれる野菜は最初の4種類、つまり引数:v には 0~4 の値が入ります。

void Update() 
{ 
    //もしisReadyがtrueで、Trayの子要素が何もない状態なら 
    if(isReady && tray.transform.childCount == 0) 
    {
        v_index = Random.Range(0, 4);       //0番から3番のいずれかの番号を取得
        //取得した番号番目の野菜のインスタンスを、Trayの座標に、生成モードで生成 
        CreateVegetable(0, v_index, tray.transform.position);
        isReady = false;                    //isReady をfalseにし、連続生成を防ぐ 
    }
}

この段階で起動すると、以下の図の様にTrayに1つだけ野菜が生成されているのが確認できます。

スイカゲーム風ベジタブルゲームの作り方 これまでの完成イメージ図

Typeの変化3 Crane配置

生成された野菜は、クレーンの野菜が落下したタイミング(0.5秒後)に、空いているクレーンに自動で配置されるようにします。

この役割は、野菜のプレハブ1つ1つが行います。

VegetableManagerクラスで新たにGameManagerクラスにアクセスするための変数を用意します。

変数名アクセス説明
gmGameManagerprivateGameManagerクラスにアクセスするための変数

事前に変数の宣言と初期値の取得を行います。

GameManager gm;     //GameManagerクラス型の変数 

void Start()
{
    //記載済み部分省略
    gm = FindObjectOfType<GameManager>();   //GameManagerクラスを探して代入 
}

待機状態からクレーンへ移動する処理には待ち時間が入るので、Coroutine()関数 を使用します。

また、若干遅れて GameManager クラスがもつ 変数:isReady を再度 true にしておきます。こちらの処理は Invoke()関数 を使って時間をずらします。

public IEnumerator SetCrane()    //クレーンに移動する処理 
{ 
    if (type == MOVETYPE.NEXT)   //TypeがNEXTなら 
    {
        yield return new WaitForSeconds(0.8f);  //0.8秒待機 
        type = MOVETYPE.READY;                  //TypeをREADYに 
        transform.SetParent(crane.transform);   //クレーンの子要素に 
        Invoke("SetIsReady", 0.2f);     //0.2秒後にisReadyをtrueに 
    } 
} 

void SetIsReady() 
{
    gm.isReady = true;  //GameManagerクラスの isReady をtrueに 
}

Typeの変化4 初回の処理

作成した 関数:SetCrane()ですが、前の野菜のオブジェクトが落下したタイミングで呼び出したいと思います。ただし初回のみは落下する野菜が無いので「スタートボタン」を作成し、そのボタンが押されたタイミングで 関数:SetCrane() を呼び出します。

Hierarchy上にUIの Canvas、及び Buttonn を生成します。

スイカゲーム風ベジタブルゲームの作り方 ボタンの配置

作成した Button を以下のように修正します。

  1. 名前:StartButton に指定
  2. RectTransformコンポーネント
       Width:500、Height:200
  3. Imageコンポーネント
        Source Image:取り込んだボタンの画像を指定
スイカゲーム風ベジタブルゲームの作り方 歩タンの設定

続いてButtonの子要素の Text(Legacy) を以下のように修正します。

  1. 名前:Text に指定
  2. Textコンポーネント
      Text:「Game Start」と指定
      Font Style:Bold
      Font Size:60
      Alignment:縦横ともに中央寄せ
      Color:任意の色(サンプルでは白)
スイカゲーム風ベジタブルゲームの作り方 ボタンの設定2

では GameManagerクラスに、このボタンを押したときの処理を作成します。

public void GameStart() //初回のみ 

SetCrane()を発動 
{
     //トレイの子要素の野菜を探して、 SetCrane()を呼出す 
    StartCoroutine(tray.transform.GetChild(0).GetComponent<VegetableManager>().SetCrane());
    //StartButtonを探して隠す 
    GameObject.Find("Canvas/StartButton").gameObject.SetActive(false); 
}

作成した 関数:GameStart()StartButtonOnClick イベント にセットします。

  1. Objectに Hierarchyの GameManager をセット
  2. クラスに GameManagerクラス
  3. Fanction に GameStart()関数 を順番に指定
スイカゲーム風ベジタブルゲームの作り方 関数の指定

Typeの変化5 ループ処理

初回移行は、クレーンが野菜を放したのをきっかけに
 ❶ 0.8秒後、トレイからクレーンに移動
 ❷ 0.2秒後、トレイに新たな野菜が生成
この処理を繰り返していきます。

野菜を離すのはクレーンなので、この処理をクレーンに追加します。

事前に InputAction に新たなアクションを追加しておきます。[06_Other]フォルダの InputAction を立ち上げ、以下の手順で新たなActionを追加します。

  1. Action の右側のプラスボタンを押して、新たなActionを追加  名前を「Release」  Action Type を「Button」に指定
  2. 作成した「Release」の右側のプラスボタンを押して新たなキーを作成
  3. 1つ目は keyboard の [Space]を選択
  4. 2つ目は GamePad の [Button South]を選択  (お好みに合わせてボタンを選択してください)
スイカゲーム風ベジタブルゲームの作り方 InputActionの設定

CraneController クラスで新たに使用する変数を確認します。

変数名アクセス説明
trayGameObjectprivateトレイのオブジェクト
vegetablesGameObjectprivate落下した野菜を子要素にする親オブジェクト

CraneController.cs を立ち上げて変数の宣言と初期値の取得を行います。

GameObject tray,vegetables;  //TrayとVegatablesのオブジェクトにアクセスするための変数 

void Start() 
{ 
    //記述済みコード省略 
    tray = GameObject.Find("Tray");             //Tray を探して代入 
    vegetables = GameObject.Find("Vegetables"); //Vegetables を探して代入 
}

続いてInputSystem を使って呼び出す、関数:Release()を作成します。この関数では、InputSystem で登録したキーが押されたときに、次の4つの処理を行っています。

  1. クレーンから野菜のMOVETYPEをFALLに変更(野菜が落下)
  2. 野菜の当たり判定を有効にする
  3. 落下した野菜をVegetableオブジェクトの子要素に格納
  4. そのときTrayにある野菜のSetCrean()関数を呼び出す  (時間差でクレーンに移動)

なお、InputSystem でキーを押したときの信号を取得する際、True,True,False と3回の信号が出力されるため、context.performed を使って、初回の信号だけを拾います。

public void Release(InputAction.CallbackContext context) 
{
    //(初回の信号のみ取得)クレーンが野菜をつかんでいれば 
    if (context.performed && transform.childCount == 1)
    {
       //つかんでいる野菜から VegetableManagar のスクリプトファイルを取得し、vmという名前で変数化
       VegetableManager vm = transform.GetChild(0).GetComponent<VegetableManager>();
       //vmのtypeがREADYで、かつTrayの子要素として野菜が存在していれば 
       if(vm.type==VegetableManager.MOVETYPE.READY && tray.transform.childCount == 1) 
       { 
            vm.type=VegetableManager.MOVETYPE.FALL;         //mvのtypeをFALLに指定 
            vm.gameObject.GetComponent<CircleCollider2D>().enabled = true;   //当たり判定を有効に
            vm.transform.SetParent(vegetables.transform);   //mvをVegetablesの子要素に 
            //Trayの子要素の野菜からVegetableManagerクラスを取得、SetCrean()を発動 
            StartCoroutine(tray.transform.GetChild(0).GetComponent<VegetableManager>().SetCrane()); 
        }
    } 
}

作成した関数を InputSystemInputAction に登録します。 Hierarchy の InputSystem を選択し、Inspector の Pyayer Inputコンポーネント から次の処理を実行します。

  1. Events を展開、さらに Player を展開
  2. Object にHierarchyのCraneオブジェクトをセット
  3. ClaneController の関数:Release()を指定
スイカゲーム風ベジタブルゲームの作り方 PlayerInputの設定

これでこのフェースの課題、野菜落下のループ処理は完成しました。

ファイブボックスでは、Unityの個別指導のオンラインレッスンを行っています。

ご興味のある方は当サイト、オンラインレッスンから、無料体験授業へお問い合わせ下さい。

TOP