「上から落ちてくるものをキャラクターが避けるゲーム」をUnityで作成する。第2回目です。
はじめに
Part1は下記です。
今回やること
パート1では簡単な画面(枠)を作成しプレイヤーの移動、障害物を落とすところまで作成しました。
今回は障害物をランダムな位置に生成し、スコア表示、ゲームオーバーを作成していきます。
作成開始
前回作成したPlayerとGroundに同じ名前でTagをセットします。
スクリプトの作成
GameManagerのスクリプトを下記に変更します。障害物をランダムな位置にゲームオーバーになるまで発生させます。
using System.Collections; using UnityEngine; public class GameManager : MonoBehaviour { public static GameManager Instance { get; private set; } public bool isGameOver = false; [SerializeField] private GameObject _enemyPrefab; void Start() { Instance = this; StartCoroutine(CreateEnemy()); } private IEnumerator CreateEnemy() { while (!isGameOver) { Vector3 enemyPos = new Vector3(Random.Range(-2.4f, 2.4f), 5.5f, 0); Instantiate(_enemyPrefab, enemyPos, Quaternion.identity); yield return new WaitForSeconds(1.0f); } } }
障害物のスクリプトを下記に変更します。プレイヤーにぶつかるとゲームオーバー、地面にぶつかると消える様にしています。
using UnityEngine; public class Enemy : MonoBehaviour { private void OnCollisionEnter2D(Collision2D collision) { GameObject col = collision.gameObject; if (col.gameObject.CompareTag("Player")) { GameManager.Instance.isGameOver = true; } if (col.gameObject.CompareTag("Ground")) { Destroy(gameObject); } } }
オブジェクトプールを利用
障害物が地面に落ちるとDestroyしていますが、プレイヤーが避け続けたら大量のオブジェクトをDestroyすることになるので、オブジェクトプールを利用するようにスクリプトを下記に変更します。
using System.Collections; using UnityEngine; using UnityEngine.Pool; public class GameManager : MonoBehaviour { public static GameManager Instance { get; private set; } public bool isGameOver = false; [SerializeField] private GameObject _enemyPrefab; private ObjectPool<GameObject> _pool; private void Awake() { _pool = new ObjectPool<GameObject>( createFunc: () => Instantiate(_enemyPrefab), actionOnGet: obj => obj.SetActive(true), actionOnRelease: obj => obj.SetActive(false), actionOnDestroy: obj => Destroy(obj), collectionCheck: true, defaultCapacity: 10, maxSize: 10); } void Start() { Instance = this; StartCoroutine(CreateEnemy()); } private IEnumerator CreateEnemy() { while (!isGameOver) { Vector3 enemyPos = new Vector3(Random.Range(-2.4f, 2.4f), 5.5f, 0); GameObject obj = _pool.Get(); obj.transform.position = enemyPos; yield return new WaitForSeconds(1.0f); } } public void DeleteEnemy(GameObject obj) { _pool.Release(obj); } }
using UnityEngine; public class Enemy : MonoBehaviour { private void OnCollisionEnter2D(Collision2D collision) { GameObject col = collision.gameObject; if (col.gameObject.CompareTag("Player")) { GameManager.Instance.isGameOver = true; } if (col.gameObject.CompareTag("Ground")) { GameManager.Instance.DeleteEnemy(gameObject); } } }
オブジェクトプールに関しては下記にて詳しく書いています。
スコア表示
次に画面上にスコアを表示していきます。
Hierarchyで右クリックして「UI」→「Legacy」→「Text」を2つ追加。
一つはScoreLable、もう一つはScoreとし下記の様にセットします。
ゲーム画面で右下に表示されます。どこでも好きな位置でOK。
スクリプトに下記を追加します。障害物が地面に当たり消えたタイミングでスコアを加算しています。
[SerializeField] private Text _scoreText; private int _score = 0; public void DeleteEnemy(GameObject obj) { _pool.Release(obj); _score++; _scoreText.text = _score.ToString(); }
パラメータにテキストをセット。これでスコアが画面に表示されます。
ゲームオーバー画面(パネル)
次にゲームオーバー画面(パネル)を作成していきます。
Hierarchyで右クリック、「UI」→「Panel」を追加。
パネルは画面全体にして、色は透明度を変更しておきます。
Hierarchyで右クリックして「UI」→「Legacy」→「Text」を2つ追加。
TitleとScoreTextとし、位置やテキストを変更します。
Hierarchyで右クリックして「UI」→「Legacy」→「Button」を追加して下記の様な感じに。
ゲーム画面では下記の様な感じに表示されます。
最終的なスクリプト
最終的なスクリプト全体は下記の様な感じに。
using System.Collections; using UnityEngine; using UnityEngine.Pool; using UnityEngine.SceneManagement; using UnityEngine.UI; public class GameManager : MonoBehaviour { public static GameManager Instance { get; private set; } [SerializeField] private GameObject _enemyPrefab; [SerializeField] private Text _scoreText; [SerializeField] private GameObject _EndPanel; [SerializeField] private Text _EndscoreText; private bool isGameOver = false; private ObjectPool<GameObject> _pool; private int _score = 0; private void Awake() { _scoreText.text = _score.ToString(); _pool = new ObjectPool<GameObject>( createFunc: () => Instantiate(_enemyPrefab), actionOnGet: obj => obj.SetActive(true), actionOnRelease: obj => obj.SetActive(false), actionOnDestroy: obj => Destroy(obj), collectionCheck: true, defaultCapacity: 10, maxSize: 10); } void Start() { Instance = this; _EndPanel.SetActive(false); StartCoroutine(CreateEnemy()); } private IEnumerator CreateEnemy() { while (!isGameOver) { Vector3 enemyPos = new Vector3(Random.Range(-2.4f, 2.4f), 5.5f, 0); GameObject obj = _pool.Get(); obj.transform.position = enemyPos; yield return new WaitForSeconds(1.0f); } } public void DeleteEnemy(GameObject obj) { _pool.Release(obj); _score++; _scoreText.text = _score.ToString(); } public void GameOver() { isGameOver = true; _EndPanel.SetActive(true); _EndscoreText.text = "SCORE:" + _scoreText.text; } public void Reset() { SceneManager.LoadScene(0); } }
using UnityEngine; public class Enemy : MonoBehaviour { private void OnCollisionEnter2D(Collision2D collision) { GameObject col = collision.gameObject; if (col.gameObject.CompareTag("Player")) { GameManager.Instance.GameOver(); } if (col.gameObject.CompareTag("Ground")) { GameManager.Instance.DeleteEnemy(gameObject); } } }
パラメータをセット。
ボタンのOnClickにGameManager.Resetを割り当て。
試しに動かしてみると、下記の様な感じになります。