ベジタブルゲームの作り方、前回作成した野菜の素材に動きを追加していきます。
ユニティルーム サンプルの投稿ゲーム
4.野菜のプログラム
4.1 変数の宣言と初期値
ここで使用する変数を確認します。
変数名 | 型 | アクセス | 説明 |
rb | Rigidbody2D | private | Rigidbody2D を管理するための変数 |
crane | GameObject | private | クレーンのオブジェクト |
tray | GameObject | private | トレイのオブジェクト |
index | int | public | 自分自身が何番目の野菜なのかを管理する変数 |
さらにこの野菜のオブジェクトは、現状どのような状態なのかを 列挙型変数:MOVETYPE で管理したいと思います。さらにこの列挙型変数を管理するための MOVETYPE型変数:type も用意しておきます。MOVETYPEで管理するゲーム中の野菜の状態は次の4つになります。
要素名 | 状態 |
NEXT | 次に登場する野菜(生成されたときの状態) |
READY | クレーンに運ばれて、落下するのを待っている状態 |
FALL | クレーンから離れて落下中の状態 |
STAY | 落下後、他のオブジェクトに接触した後の状態 |
では VegetableManager.cs を立ち上げて、変数の宣言と初期値の指定を行います。
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を探して代入
}
4.2 MOVETYPEの振り分け
続いて、各状態のケース、それぞれの座標や重力の状態をSwitch文で振り分けます。各状態での Rigidbody2D の BodyType や重力、座標情報等は以下の通りです。
MOVETYPE | Bodytype | 重力 | 座標 |
NEXT | Static | 0 | 次の野菜が配置される場所に配置 |
READY | Kinematic | 0 | 常にクレーンの0.7だけ下側 |
FALL | Dinamic | 0.6 | |
STAY | Dinamic | 0.3 | 高さが3.5より上、または壁の外に出たらゲームオーバー |
VegetableManager.cs
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()を呼び出す
}
4.3 Typeの変化1 GameMangerの準備
ここでは野菜がクローンとしてトレイの場所に生成され、クレーンに移動し、落下するまでの流れを確認します。
上の図の様に、個々の野菜の状態と、全体の状態を管理する isReady というフラグが必要になってきます。個々の野菜のプレハブは生成されたり、削除されたりするので、全体を管理することが出来ません。
そこで野菜を生成や状態を管理するフラグを管理する役割を担う、GameManager を用意して、ゲームの進行を任せます。
また野菜のオブジェクトが生成され、クレーンで運ばれ落下した後に落下したオブジェクトを子要素としてまとめておくための Vegetables も準備しておきます。
Hierarchy上に空のオブジェクトとして、GameManager、Vegetables を作成します。
続いて同様にスクリプトファイル GameManager を作成し、作成したGameManagerオブジェクトにアタッチします。
GameManagerクラスで使用する変数、及び配列は以下の通りです。まず、上で作成した野菜のプレハブを配列で管理します。
配列名 | 型 | アクセス | 説明 |
v_Prefab | GameObject | public | 野菜のプレハブ(12個) |
また使用する変数は以下の通りです。
変数名 | 型 | アクセス | 説明 |
isReady | bool | public | 野菜生成のタイミングを管理 |
v_index | int | private | 生成する野菜の番号を管理 |
createPos | Vector3 | private | 野菜が生成される場所を管理 |
tray | GameObject | private | トレイのオブジェクト |
crane | GameObject | private | クレーンのオブジェクト |
vegetables | GameObject | private | 落下した野菜を子要素にする親オブジェクト |
GameManager.cs を立ち上げて、変数の宣言と初期値の取得を行います。
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から野菜のプレハブを以下のような順番に配置します。
4.4 Typeの変化2 野菜生成
野菜の生成は GameManagerクラスが行います。この後の展開を考えると、野菜の生成は次の2パターンが考えられます。
新たな野菜としてトレイ上に生成
2つの同じ野菜が接触したときに、次の(進化した)野菜として生成
この2パターンも考慮し、以下の3つの引数を持たせた野菜生成の関数を用意します。
❶ どちらのパターンで(int型 0:新規生成モード、1:進化生成モード) ❷ 何番目の野菜を(int型:0~11) ❸ どこに(Vector3型)
では上の内容を確認し、GameManagerクラスに野菜を生成する 関数:CreateVegetable(int t, int v, Vector3 p) を作成します。
GameManager.cs
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()関数では、変数:isReady が true でTrayの子要素が無い状態の時に、関数:CreateVegetable()を新規作成モードで作成したを呼び出します。
この時に選ばれる野菜は最初の4種類、つまり引数:v には 0~4 の値が入ります。
GameManager.cs
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つだけ野菜が生成されているのが確認できます。
4.5 Typeの変化3 Crane配置
生成された野菜は、クレーンの野菜が落下したタイミング(0.5秒後)に、空いているクレーンに自動で配置されるようにします。
この役割は、野菜のプレハブ1つ1つが行います。
VegetableManagerクラスで新たにGameManagerクラスにアクセスするための変数を用意します。
変数名 | 型 | アクセス | 説明 |
gm | GameManager | private | GameManagerクラスにアクセスするための変数 |
事前に変数の宣言と初期値の取得を行います。
VegetableManager.cs
GameManager gm; //GameManagerクラス型の変数
void Start()
{
//記載済み部分省略
gm = FindObjectOfType<GameManager>(); //GameManagerクラスを探して代入
}
待機状態からクレーンへ移動する処理には待ち時間が入るので、Coroutine()関数 を使用します。また、若干遅れて GameManagerクラスがもつ 変数:isReady を再度 true にしておきます。こちらの処理は Invoke()関数 を使って時間をずらします。
VegetableManager.cs
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に
}
4.6 Typeの変化4 初回の処理
作成した関数:SetCrane()ですが、前の野菜のオブジェクトが落下したタイミングで呼び出したいと思います。ただし初回のみは落下する野菜が無いので「スタートボタン」を作成し、そのボタンが押されたタイミングで 関数:SetCrane() を呼び出します。
Hierarchy上にUIの Canvas、及び Buttonn を生成します。
作成した Button を以下のように修正します。
❶ 名前:StartButton に指定 ❷ RectTransformコンポーネント Width:500、Height:200 ❸ Imageコンポーネント Source Image:取り込んだボタンの画像を指定
続いてButtonの子要素の Text(Legacy) を以下のように修正します。
❶ 名前:Text に指定 ❷ Textコンポーネント Text:「Game Start」と指定 Font Style:Bold Font Size:60 Alignment:縦横ともに中央寄せ Color:任意の色(サンプルでは白)
では GameManagerクラスに、このボタンを押したときの処理を作成します。
GameManager.cs
public void GameStart() //初回のみ SetCrane()を発動
{
//トレイの子要素の野菜を探して、 SetCrane()を呼出す
StartCoroutine(tray.transform.GetChild(0).GetComponent<VegetableManager>().SetCrane());
//StartButtonを探して隠す
GameObject.Find("Canvas/StartButton").gameObject.SetActive(false);
}
作成した 関数:GameStart() を StartButton の OnClick イベント にセットします。
❶ Objectに Hierarchyの GameManager をセット ❷ クラスに GameManagerクラス ❸ Fanction に GameStart()関数 を順番に指定
4.7 Typeの変化5 ループ処理
初回移行は、クレーンが野菜を放したのをきっかけに ❶ 0.8秒後、トレイからクレーンに移動 ❷ 0.2秒後、トレイに新たな野菜が生成 この処理を繰り返していきます。野菜を離すのはクレーンなので、この処理をクレーンに追加します。
事前に InputAction に新たなアクションを追加しておきます。[06_Other]フォルダの InputAction を立ち上げ、以下の手順で新たなActionを追加します。
❶ Action の右側のプラスボタンを押して、新たなActionを追加 名前を「Release」 Action Type を「Button」に指定 ❷ 作成した「Release」の右側のプラスボタンを押して新たなキーを作成 ❸ 1つ目は keyboard の [Space]を選択 ❹ 2つ目は GamePad の [Button South]を選択 (お好みに合わせてボタンを選択してください)
CraneController クラスで新たに使用する変数を確認します。
変数名 | 型 | アクセス | 説明 |
tray | GameObject | private | トレイのオブジェクト |
vegetables | GameObject | private | 落下した野菜を子要素にする親オブジェクト |
CraneController.cs を立ち上げて変数の宣言と初期値の取得を行います。
CraneController.cs
GameObject tray,vegetables; //TrayとVegatablesのオブジェクトにアクセスするための変数
void Start()
{
//記述済みコード省略
tray = GameObject.Find("Tray"); //Tray を探して代入
vegetables = GameObject.Find("Vegetables"); //Vegetables を探して代入
}
続いてInputSystemを使って呼び出す、関数:Release()を作成します。この関数では、InputSystemで登録したキーが押されたときに、次の4つの処理を行っています。
❶ クレーンから野菜のMOVETYPEをFALLに変更(野菜が落下) ❷ 野菜の当たり判定を有効にする ❸ 落下した野菜をVegetableオブジェクトの子要素に格納 ❹ そのときTrayにある野菜のSetCrean()関数を呼び出す (時間差でクレーンに移動)
なお、InputSystemでキーを押したときの信号を取得する際、True,True,False と3回の信号が出力されるため、context.performed を使って、初回の信号だけを拾います。
CraneController.cs
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());
}
}
}
作成した関数を InputSystem の InputAction に登録します。 Hierarchyの InputSystem を選択し、Inspector の Pyayer Inputコンポーネント から次の処理を実行します。
❶ Events を展開、さらに Player を展開 ❷ Object にHierarchyのCraneオブジェクトをセット ❸ ClaneController の関数:Release()を指定
これでこのフェースの課題、野菜落下のループ処理は完成しました。
「VegetablGameの作り方」その他の記事はこちらから
ファイブボックスでは、Unityの個別指導のオンラインレッスンを行っています。
ご興味のある方は当サイト、オンラインレッスンから、無料体験授業へお問い合わせ下さい。
Comments