前回までの記事で敵を登場させたり、プレイヤーの体力を減らしたり、弾を撃って敵を倒せるようにしました。
前回の記事:
ここまでできるとあとはゲームクリアとゲームオーバー処理を作成するとシューティングゲームの出来上がりです!
スコア処理や地面に激突したときの処理などもここで書いていきましょう。
今回の記事ではゲームの流れとなる最後の部分を作成していきましょう。
シーンを管理するSceneManagerを作ろう!
先にシーンを管理するスクリプトを作成します。
次の手順を行なってください。
- シーンに空のGameObjectを作成し、名前を「SceneManager」にする
- 「SceneManager」スクリプトを新しく作成する
「SceneManager」スクリプトの内容はひとまず空でOKです。
1 2 3 4 5 6 7 |
using System.Collections; using System.Collections.Generic; using UnityEngine; public class SceneManager : MonoBehaviour { } |
シューティングゲームのゲームオーバー処理を作る
プレイヤーの体力が0になったらゲームオーバーと画面に表示するようにしましょう。
次の手順でゲームオーバー用のGameObjectを追加してください。
- 「Canvas」の子GameObjectにUI > Textを作成し、名前を「GameOver」にする
- 「GameOver」のパラメータを任意で設定する(画面に大きく出てくるように配置)
「SceneManager」スクリプトを使って作成した「GameOver」の表示を管理します。
「SceneManager」スクリプトに次の内容を追加してください。
またコンパイル後に「GameOver」を「SceneManager」コンポーネントにアタッチするのを忘れないでください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; //using文追加 public class SceneManager : MonoBehaviour { //SceneManagerスクリプトに次のものを追加 // フィールドの追加 public Text GameOverText; //メソッドの追加 private void Start() { GameOverText.gameObject.SetActive(false); } public void ShowGameOver() { GameOverText.gameObject.SetActive(true); } } |
体力は「Player」コンポーネントで管理しているので、その中の体力を減らしている部分に0以下になったか判定する処理を追加し、「SceneManager」コンポーネントのメソッドを呼び出すようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
//Playerコンポーネント //OnTriggerEnterメソッドを次のものに変更 private void OnTriggerEnter(Collider other) { if(other.gameObject.tag == "RoutePoint") { other.gameObject.SetActive(false); _isHitRoutePoint = true; } else if(other.gameObject.tag == "Enemy") { Life -= 10f; LifeGage.fillAmount = Life / _initialLife; other.gameObject.SetActive(false); Object.Destroy(other.gameObject); //当たった敵は削除する if(Life <= 0) { Camera.main.transform.SetParent(null); gameObject.SetActive(false); var sceneManager = Object.FindObjectOfType<SceneManager>(); sceneManager.ShowGameOver(); } } } |
コンパイルに成功したら、プレイヤーのライフが0になった時「GameOver」のテキストが画面に表示されるようになります。
ライフがなくなった時にプレイヤーだけを非表示にしたいので、子GameObjectにしていたカメラを「Player」の親子関係から外しています。
シューティングゲームのクリア処理を作る
次にゲームクリアできるようにしましょう!
先に次の手順でゲームクリア用のGameObjectを追加してください。
- 「Canvas」の子GameObjectにUI > Textを作成し、名前を「Clear」にする
- 「Clear」のパラメータを任意で設定する(画面に大きく出てくるように配置)
「Clear」は「SceneManager」コンポーネントを使って表示を管理します。
「SceneManager」スクリプトに次の内容を追加してください。
またコンパイル後に「Clear」を「SceneManager」コンポーネントにアタッチするのを忘れないでください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class SceneManager : MonoBehaviour { public Text GameOverText; public Text ClearText; private void Start() { GameOverText.gameObject.SetActive(false); ClearText.gameObject.SetActive(false); } public void ShowGameOver() { GameOverText.gameObject.SetActive(true); } public void ShowClear() { ClearText.gameObject.SetActive(true); } } |
下準備ができましたので、次はクリア判定処理を作ります。
この講座では到達したらクリアとするルート上のポイントを作り、それに当たったらクリアとします。
クリア判定に使用するポイントは基本的に「RoutePoint」タグを指定したものの中から選びます。一番最後のPointをClearPointにすればよいでしょう。
ゲームクリア地点にしたいGameObjectのタグには「ClearRoutePoint」を指定しましょう。
それに合わせて「Player」コンポーネントに「ClearRoutePoint」タグを設定されたGameObjectと当たったか判定する処理を追加します。
その中で「SceneManager」コンポーネントのクリア用のテキストを表示するメソッドを呼び出すようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
//Playerスクリプト //OnTriggerEnterメソッドを次のものに変更 private void OnTriggerEnter(Collider other) { if(other.gameObject.tag == "RoutePoint") { other.gameObject.SetActive(false); _isHitRoutePoint = true; } else if(other.gameObject.tag == "Enemy") { Life -= 10f; LifeGage.fillAmount = Life / _initialLife; other.gameObject.SetActive(false); Object.Destroy(other.gameObject); //当たった敵は削除する if(Life <= 0) { Camera.main.transform.SetParent(null); gameObject.SetActive(false); var sceneManager = Object.FindObjectOfType<SceneManager>(); sceneManager.ShowGameOver(); } } else if(other.gameObject.tag == "ClearRoutePoint") { var sceneManager = Object.FindObjectOfType<SceneManager>(); sceneManager.ShowClear(); _isHitRoutePoint = true; } } |
地形に当たったら体力を0にする処理を追加 Terrainとの当たり判定の作り方
次は地形にプレイヤーが当たったら即座に体力を0にするようにしましょう!
シーン上の「Background」オブジェクトのタグを「Background」と設定してください。
設定したら「Player」スクリプトのOnCollisionEnterメソッドを追加してください。
基本的に敵に当たった時と同じ感じになります。
ただし、Terrainには専用のTerrainColliderが用意されており、「Background」には作成された時にそれがアタッチされています。
TerrainColliderは「Is Trigger」を設定できないので、回避策として「Player」のRigidbodyコンポーネントの「Is Kinematic」を無効にし、OnCollisionEnterメソッドで「Background」との当たり判定を行います。
「Is Kinematic」を有効にしているとOnCollisionEnterメソッドは機能しなくなるので注意してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
//Playerスクリプト //OnCollisionEnterメソッドを追加 private void OnCollisionEnter(Collision collision) { if (collision.gameObject.tag == "Background") { Life = 0; LifeGage.fillAmount = Life / _initialLife; Camera.main.transform.SetParent(null); gameObject.SetActive(false); var sceneManager = Object.FindObjectOfType<SceneManager>(); sceneManager.ShowGameOver(); } } |
コンパイルに成功したら再生してみてください。「Background」と当たった時にゲームオーバーになったら成功です。
ゲームスコアを記録してみよう 敵を撃破したら加点する処理
最後に敵を倒したら、スコアを加算するようにしてみましょう!
スコアの加算処理は「SceneManager」スクリプトの方で行います。
スコア表示の方は先に「ScoreText」として作成していますのでそれを使用します。
次のサンプルコードのコンパイル後に「ScoreText」を「SceneManager」コンポーネントにアタッチするのを忘れないでください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class SceneManager : MonoBehaviour { public Text GameOverText; public Text ClearText; public Text ScoreText; int _currentScore = 0; private void Start() { GameOverText.gameObject.SetActive(false); ClearText.gameObject.SetActive(false); ScoreText.text = _currentScore.ToString(); } public void ShowGameOver() { GameOverText.gameObject.SetActive(true); } public void ShowClear() { ClearText.gameObject.SetActive(true); } public void AddScore(int score) { _currentScore += score; ScoreText.text = _currentScore.ToString(); } } |
「SceneManager」コンポーネントを変更したら、次は「Enemy」コンポーネントの方を変更します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
//Enemyスクリプトに次のものを追加 //OnTriggerEnterメソッドを次のように変更 private void OnTriggerEnter(Collider other) { if(other.gameObject.tag == "Bullet") { Life -= 10; Object.Destroy(other.gameObject); if(Life <= 0) { Object.Destroy(gameObject); var sceneManager = Object.FindObjectOfType<SceneManager>(); sceneManager.AddScore(1000); } } } |
コンパイルに成功したら、再生して敵を倒してみてください。
スコアが増えたら成功です。
シューティングゲームの作り方まとめ
第五回では、
- ゲームオーバー処理
- ゲームクリア処理
- Terrainとの当たり判定処理
- 敵撃破時のスコア加算処理
を実装することができました。
今回でシューティングゲームを一通り完成させることができました。
この講座でのシューティングゲームは基本的な処理のみをまとめたものになります。
そのため見た目が地味だったり、細かいところで融通が効かない部分がありますが、今回の講座内容をベースにあなたが自由に改良していってもらえたらと思います。
また、Unityではバージョンによって使用できる機能が異なるのでここで説明した以外の機能があったり、もしかしたら一部なくなるようなこともあるかもしれません。
基本的な部分につきましては大体似たものになっていると思いますので、適時読み替えて作業を進めてみてください。
それでは次からはまた別のゲームを作っていきましょう!
参考用:スクリプトの完成図
ちなみに今回の記事で作成したスクリプトの完成図は次のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class SceneManager : MonoBehaviour { public Text GameOverText; public Text ClearText; public Text ScoreText; int _currentScore = 0; private void Start() { GameOverText.gameObject.SetActive(false); ClearText.gameObject.SetActive(false); ScoreText.text = _currentScore.ToString(); } public void ShowGameOver() { GameOverText.gameObject.SetActive(true); } public void ShowClear() { ClearText.gameObject.SetActive(true); } public void AddScore(int score) { _currentScore += score; ScoreText.text = _currentScore.ToString(); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class Player : MonoBehaviour { public Transform[] RoutePoints; [Range(0, 200)] public float Speed = 10f; [Range(0, 50)] public float MoveSpeed = 10f; public float MoveRange = 40f; public float _initialLife = 100; public float Life = 100; public Image LifeGage; public Bullet BulletPrefab; bool _isHitRoutePoint; IEnumerator Move() { var prevPointPos = transform.position; var basePosition = transform.position; var movedPos = Vector2.zero; foreach (var nextPoint in RoutePoints) { _isHitRoutePoint = false; //必ずfalseにする while (!_isHitRoutePoint) { //進行方向の計算 var vec = nextPoint.position - prevPointPos; vec.Normalize(); // プレイヤーの移動 basePosition += vec * Speed * Time.deltaTime; //上下左右に移動する処理 // 行列によるベクトルの変換に関係する知識を利用しています。 movedPos.x += Input.GetAxis("Horizontal") * MoveSpeed * Time.deltaTime; movedPos.y += Input.GetAxis("Vertical") * MoveSpeed * Time.deltaTime; movedPos = Vector2.ClampMagnitude(movedPos, MoveRange); var worldMovedPos = Matrix4x4.Rotate(transform.rotation).MultiplyVector(movedPos); //ルート上の位置に上下左右の移動量を加えている transform.position = basePosition + worldMovedPos; //次の処理は進行方向を向くように計算している transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(vec, Vector3.up), 0.5f); yield return null; } prevPointPos = nextPoint.position; } } private void OnTriggerEnter(Collider other) { if (other.gameObject.tag == "RoutePoint") { other.gameObject.SetActive(false); _isHitRoutePoint = true; } else if (other.gameObject.tag == "Enemy") { Life -= 10f; LifeGage.fillAmount = Life / _initialLife; other.gameObject.SetActive(false); Object.Destroy(other.gameObject); //当たった敵は削除する if (Life <= 0) { Camera.main.transform.SetParent(null); gameObject.SetActive(false); var sceneManager = Object.FindObjectOfType<SceneManager>(); sceneManager.ShowGameOver(); } } else if (other.gameObject.tag == "ClearRoutePoint") { var sceneManager = Object.FindObjectOfType<SceneManager>(); sceneManager.ShowClear(); _isHitRoutePoint = true; } } void Start() { StartCoroutine(Move()); } public void ShotBullet(Vector3 targetPos) { var bullet = Object.Instantiate(BulletPrefab, transform.position, Quaternion.identity); bullet.Init(transform.position, targetPos); } private void OnCollisionEnter(Collision collision) { if (collision.gameObject.tag == "Background") { Life = 0; LifeGage.fillAmount = Life / _initialLife; Camera.main.transform.SetParent(null); gameObject.SetActive(false); var sceneManager = Object.FindObjectOfType<SceneManager>(); sceneManager.ShowGameOver(); } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Enemy : MonoBehaviour { [Range(0, 100)] public float Speed = 10; public float DeadSecond = 10f; public float Life = 10; float _time; Player _player; void Start() { _time = 0f; _player = Object.FindObjectOfType<Player>(); } void Update() { _time += Time.deltaTime; if (_time >= DeadSecond) { Object.Destroy(gameObject); } else { var vec = _player.transform.position - transform.position; transform.position += vec.normalized * Speed * Time.deltaTime; } } private void OnMouseUpAsButton() { _player.ShotBullet(transform.position); } private void OnTriggerEnter(Collider other) { if (other.gameObject.tag == "Bullet") { Life -= 10; Object.Destroy(other.gameObject); if (Life <= 0) { Object.Destroy(gameObject); var sceneManager = Object.FindObjectOfType<SceneManager>(); sceneManager.AddScore(1000); } } } } |
コメント