Unity - 2D:残基、ゲームクリアなどの処理 -
初めに
GameManagerなどを作り、残機やゲームクリアなどゲームシステムを構築していきます。
GameManagerを作る
GameManagerは、現在のステージの進行状況や残機、セーブデータ、スコアなどシーンをまたいで扱いたいものを保持させることが多いです。
今回は、現在のステージ、残機を追加します。GameManagerを次のように記述しましょう。
using System.Collections; using System.Collections.Generic; using UnityEngine; public class GameManager : SingletonMonoBehaviour<GameManager> { //現在のステージ public int stageNum; //残機 public int remain = 2; public void Awake() { if (this != Instance) { Destroy(gameObject); return; } DontDestroyOnLoad(gameObject); } //以下に処理を書く }
現在のステージと残機を記述しました。ステージクリアをしたら、stageNumを変えましょう。また、プレイヤーがミスをしたら残機数であるremainを減らしましょう。
注意すべき点はシングルトンになっている点です。シングルトンについては、以前の記事をご覧ください。
GameManagerのオブジェクトはほぼ全シーンで使うため、プレハブ化しておきましょう。プレハブについての説明は次の記事をご覧ください。
fineworks-fine.hatenablog.com
ゴールを作る
ゴールの処理を作ります。今回はゴールとなるオブジェクトを作り、そのオブジェクトに触れたらゴールとしたいと思います。
- ゴールとなる画像を用意する
まず、ゴールとなる画像を用意します。私は次のドット絵をゴールとしました。
- ゴールのSpriteを生成し、BoxCollider2Dをつける
今回は、ゴールをすり抜けられるようにするため、Is Trigger にチェックを入れてください。
Spriteの使い方やBoxCollider2Dの使い方については、以下の記事をご覧ください。
fineworks-fine.hatenablog.com
fineworks-fine.hatenablog.com
- 次のスクリプトを作成し、GoalのSpriteにアタッチする
using System.Collections; using System.Collections.Generic; using UnityEngine; public class GoalScript : MonoBehaviour { public GameObject GameManager; private GameManager GMScript; void Start() { GMScript = GameManager.GetComponent<GameManager>(); } void OnTriggerEnter2D(Collider2D collision) { if(collision.gameObject.CompareTag("Player")) { GMScript.stageNum++; Debug.Log("ゴールした.NextStageNum = " + GMScript.stageNum); // ログを表示する } } }
CompareTagはオブジェクトのtagを比較して判別するものです。今回であれば、collisionで取得したゲームオブジェクトのtagが "Player" かどうかを判定しています。もし、ゴールにPlayerが触れれば、ゲームマネージャーのstageNumを1増やすという処理になっています。
- Goalをプレハブ化する
Goalも何回も使うため、プレハブ化しておきます。
- プレハブ化したGoalのスクリプトにGameManagerをアタッチする
これでゴールのオブジェクトは完成です。プレイヤーキャラのタグをPlayerに変えて実行してみましょう。
実行結果
ログを見ると、stageNumが増えているので、うまくいっていることが分かります。
ステージクリアの処理を作る
ゴールしたら、ステージクリアを表示し、次のステージへ移る処理を作りましょう。
ステージクリアテキストを表示させる
- ステージクリアのTextを作成し、Canvasごとプレハブ化する
Hierarchyで右クリック→UI→TextでTextを生成できます。また、Component→Effects→UI→Outlineで文字の輪郭に色を付けることができます。
- GameManager、GoalScriptをそれぞれ次のように修正する
GameManager
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; //Textを使うため追加 public class GameManager : SingletonMonoBehaviour<GameManager> { //現在のステージ public int stageNum; //StageClaer時に生成するCanvas public GameObject StageClearCanvas; //残機 private int remain = 2; public void Awake() { if (this != Instance) { Destroy(gameObject); return; } DontDestroyOnLoad(gameObject); } //以下に処理を書く void Start() { Debug.Log("始まったよ"); } //ステージクリア時に呼び出される関数 public void StageClaer() { //表示されるTextの取得 Text stageclearText = StageClearCanvas.transform.Find("Text").GetComponent<Text>(); //表示されるTextを入力 stageclearText.text = "Stage" + stageNum + "\nClear"; //Canvasごとクリア時のTextを生成する GameObject _clearcanvas = Instantiate(StageClearCanvas, new Vector3(0, 0, 0), Quaternion.identity); //stageNumを1増やす stageNum++; } }
GoalScript
using System.Collections; using System.Collections.Generic; using UnityEngine; public class GoalScript : MonoBehaviour { public GameObject GameManager; private GameManager GMScript; void Start() { GMScript = GameManager.GetComponent<GameManager>(); } void OnTriggerEnter2D(Collider2D collision) { if(collision.gameObject.CompareTag("Player")) { //ステージクリア時の処理をGameManagerから呼び出す //stageNumを増やす処理もGameManagerに移した GMScript.StageClaer(); Debug.Log("ゴールした.NextStageNum = " + GMScript.stageNum); // ログを表示する } } }
Textの使いかたやInstantiate関数を使ってプレハブを生成する方法については、次の記事にまとめております。
fineworks-fine.hatenablog.com
fineworks-fine.hatenablog.com
- プレハブ化した(クリア時に表示するTextを子オブジェクトにもつ)CanvasをGameManagerにアタッチする
ここまでで、ゴールするとTextが表示されるようになりました。
次のシーンへ移る
クリア演出後、次のステージへ移るようにします。
- Sceneの名前をStage1, Stage2, ...と変更する
- SceneをScene in Buildに登録する
File→Build Settings...をクリックすると、次の画面が開きます。シーンをドラッグ&ドロップし、登録します。
- GameManagerをシーンが遷移できるように修正する
GameManagerを次のように追記します。
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; //Textを使うため追加 using UnityEngine.SceneManagement; //Sceneを切り替えるために追加 public class GameManager : SingletonMonoBehaviour<GameManager> { //現在のステージ public int stageNum; //StageClaer時に生成するCanvas public GameObject StageClearCanvas; //残機 private int remain = 2; public void Awake() { if (this != Instance) { Destroy(gameObject); return; } DontDestroyOnLoad(gameObject); } //以下に処理を書く void Start() { Debug.Log("始まったよ"); } //ステージクリア時に呼び出される関数 public void StageClaer() { //表示されるTextの取得 Text stageclearText = StageClearCanvas.transform.Find("Text").GetComponent<Text>(); //表示されるTextを入力 stageclearText.text = "Stage" + stageNum + "\nClear"; //Canvasごとクリア時のTextを生成する GameObject _clearcanvas = Instantiate(StageClearCanvas, new Vector3(0, 0, 0), Quaternion.identity); //stageNumを1増やす stageNum++; //2秒後にChangeScene関数を実行 Invoke(nameof(ChangeScene), 2.0f); } //次のステージへシーンを移す関数 public void ChangeScene() { SceneManager.LoadScene("Stage" + stageNum); } }
シーンを切り替える処理を追記しました。シーンの切り替えには "SceneManager" を使います。細かいことは次の記事をご覧ください。
また、ゴールして一定時間後にシーンを移すようにInvokeを使っています。Invokeを使うことで一定秒数後に関数を呼び出すことができます。一定時間後に処理を実行する方法はほかにもコルーチンを使う方法があります。個人的にはコルーチンを使うほうが好みです。
実行して確認
分かりにくいですが、プレイヤーキャラがスタート地点に現れ、次のシーンが始まったことが分かります。
バグを修正しよう
このままでは、何回もゴールに触れることができるため、次のシーンに移るまでstageNumを増やし続けられます。そのため、1ステージにつき、1回しかゴールできないようにしましょう。
いろいろな方法がとれますが、今回はGoalScriptにGoalしたかどうかの判定を追加することで対応します。
using System.Collections; using System.Collections.Generic; using UnityEngine; public class GoalScript : MonoBehaviour { public GameObject GameManager; private GameManager GMScript; private bool Goaled = false; //追加 void Start() { GMScript = GameManager.GetComponent<GameManager>(); } void OnTriggerEnter2D(Collider2D collision) { if(collision.gameObject.CompareTag("Player") && Goaled == false) //追加 { Goaled = true; //追加 //ステージクリア時の処理をGameManagerから呼び出す //stageNumを増やす処理もGameManagerに移した GMScript.StageClaer(); Debug.Log("ゴールした.NextStageNum = " + GMScript.stageNum); // ログを表示する } } }
最後に
これで残機、ゲームクリアの処理などが作れました。残機については、ステージから落下した場合や敵に触れた場合などにプレイヤーのスクリプトからGameManagerの変数を減らせばよいでしょう。これは落下処理や敵を作った際に追加すればよいと思います。