この記事は「初心者でも作れるUnity簡単2Dゲームの作り方講座」の第5回です。
前回は敵キャラクターのスライムを左右に動くようにして、プレイヤーが上から踏んだときだけ倒せるようにしました。さらに、敵を踏んだあとにプレイヤーが少し上へバウンドする処理も作成しました。
前回の記事:

今回はゲームオーバー処理を作成します。現時点では、敵キャラを上から踏むと倒せますが、横や下から触れても何も起こりません。
そこで、敵に横や下から触れたときにゲームオーバーになる処理を作成します。
さらに、ステージから落下したときや危険な障害物に触れたときにもゲームオーバーになる仕組みを順番に作成していきます。
アクションゲームに必要な敗北処理の作り方を習得していきましょう。
ゲームオーバー用のテキストUI、GameOverText を用意する
まずは、ゲームオーバーになったときに表示するテキストを用意します。
Hierarchy で右クリックして、UI > Text – TextMeshPro を選択します。

TextMeshPro を初めて使う場合は、インポート確認の画面が表示されます。その場合は Import TMP Essentials を選択して、TextMeshPro に必要な基本データを読み込みましょう。
下側のExamples & ExtrasはインポートしなくてOKです。

作成した TextMeshPro の名前を GameOverText に変更します。

このとき、Hierarchy に Canvas というオブジェクトも自動で作成されます。
Canvasとは?
Canvas はゲーム画面に文字やボタンなどのUIを表示するための場所です。
通常の Sprite(足場・プレイヤー・敵キャラなどの画像オブジェクトがSpriteです) は Scene の中に配置しますが、UIは Canvas の中に配置します。
今回の GameOverText もゲーム内の地面やプレイヤーと同じ場所に置く文字ではなく、画面の上に表示する文字なので Canvas の中に作成されます。
Canvas の設定を変更しよう
次に、Canvas を選択します。

Inspector にある Canvas Scaler コンポーネントを確認します。UI Scale Mode を Scale With Screen Size に変更しましょう。

Canvas は Sprite とは違い、画面サイズの影響を受けます。
初期設定の Constant Pixel Size のままだと、文字の大きさが「画面に対してどれくらいの大きさか」ではなく、「何ピクセルで表示するか」で決まります。
そのため、Game ビューのサイズや画面解像度が変わったときに、GameOverText が大きすぎたり、小さすぎたりして見えることがあります。

Scale With Screen Size に変更すると、基準となる画面サイズに合わせてUI全体を拡大・縮小してくれます。
つまり、画面サイズが変わっても GameOverText が画面に対して同じくらいの大きさで表示されやすくなります。
今回のように、ゲームオーバー用の文字を画面中央に大きく表示したい場合は Scale With Screen Size にしておくと調整しやすくなります。
GameOverText の位置と大きさを調整しよう
次に、GameOverText を選択します。

Inspector の TextMeshPro – Text の入力欄に、Game Over と入力します。

そのあと、画面中央に大きく表示されるように、位置、文字サイズ、色を調整しましょう。


↑こんな画面になっていたらOKです。
GameOverText はゲーム開始時から表示されているとおかしいので非表示にします。

↑GameOverTextの名前の左側にあるチェックマークを外しておきましょう。
ゲーム全体を管理する司令塔オブジェクトを用意する
次に、ゲーム全体を管理するための”司令塔となるオブジェクト”を作成します。
ゲームオーバー処理を作るときに、Player、Enemy、Camera、UI などがそれぞれ直接やり取りしてしまうと、スクリプト同士のつながりがわかりにくくなります。
例えば、Player が Enemy に触れたときに、Enemy から Camera に関数呼び出しをしたり、Player から UI へ処理の引継ぎを行ったりすると、どのスクリプトが何を管理しているのかわかりにくくなります。

後でプログラムを再利用したり、ゲーム内のバグを見つけるときに処理が分かれていて、プログラム間のつながりの数が少ない方が便利です。左側より右側の方が望ましい状態です。
ゲームプログラミングを学んでいくと”設計”という言葉に出会うことがありますが、一度作ったプログラムを使いやすくしたり、アップデートや修正やバグ取りなどを行いやすくするための知恵です。
一目すると難しそうな設計テクニックを目にすることもありますがどれも必ず目的があります。目的に合ったプログラム設計をしていきましょう。
- ゲーム中
- ゲームオーバー
の2つで管理します。
Player が敵に触れたり、落下したり、危険な障害物に触れたりしたときは、GameManager に「ゲームオーバーになった」と伝えます。
すると、GameManager がゲームの状態をゲーム中からゲームオーバーに変更し、UIでゲームオーバー用のテキストを画面に表示するといった流れを作ります。
このようにゲームオーバーの管理を GameManager にまとめられるので、スクリプトの役割がわかりやすくなります。
GameManager オブジェクトを作成しよう
司令塔になるオブジェクトを作成します。
Hierarchy で右クリックして、Create Empty を選択します。作成した空のオブジェクトの名前を GameManager に変更しましょう。

この GameManager オブジェクト自体には見た目はありません。ゲーム全体の状態を管理するために空のオブジェクトとしてシーンに配置しました。
GameManager2D スクリプトを作成しよう
次にGameManager をスクリプトから制御できるようにします。
Project ウィンドウから Assets > Scripts フォルダを開きます。Scripts フォルダの中で右クリックして、Create > MonoBehaviour Script を選択します。作成したスクリプトの名前を GameManager2D に変更しましょう。

名前を変更できたら、GameManager2D をダブルクリックして開きます。
ゲームオーバーの状態を管理する処理を書いていきます。
GameManager2D
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
using TMPro; using UnityEngine; public class GameManager2D : MonoBehaviour { public enum GameState { Playing, GameOver } [Header("UI")] public GameObject gameOverText; public GameState currentState = GameState.Playing; public void GameOver() { currentState = GameState.GameOver; gameOverText.SetActive(true); } } |
スクリプトの解説
このスクリプトはゲーム全体の状態を管理しています。
GameManager2D が「現在ゲーム中なのか」、「ゲームオーバーなのか」を判断できるようにします。順番に見ていきましょう。
まずは enum でゲームの状態を作成しています。
|
1 2 3 4 5 |
public enum GameState { Playing, GameOver } |
今回は、ゲームの状態として Playing と GameOver の 2 つを用意しています。Playing はゲーム中、GameOver はゲームオーバーになった状態です。
enum とは?
enum は、決まった種類の中から状態を選ぶためのものです。今回であれば、ゲームの状態は「ゲーム中」か「ゲームオーバー」のどちらかなので、Playing と GameOver を enum として用意しています。
文字や数字で状態を管理することもできますが、enum を使うと「どんな状態があるのか」がコードを見ただけで分かりやすくなります。例えば、currentState が Playing ならゲーム中、GameOver ならゲームオーバーというように、ゲームの状態を分かりやすく管理できます。
そしてGameOverText を入れるための変数を作成しています。
|
1 2 |
[Header("UI")] public GameObject gameOverText; |
先ほどUnity画面で作成した GameOverText を Inspector から設定します。
次は以下の行で現在のゲーム状態を管理する変数を作成します。
|
1 |
public GameState currentState = GameState.Playing; |
currentState には、現在のゲーム状態を入れます。最初はゲーム中から始めたいので、GameState.Playing を入れています。
次に、GameOver 関数を見ていきましょう。
|
1 2 3 4 5 |
public void GameOver() { currentState = GameState.GameOver; gameOverText.SetActive(true); } |
この GameOver 関数は、Player や Enemy など、他のスクリプトから呼び出す前提で作成しています。
通常、関数は void GameOver() もしくは private void GameOver() のように書けます(どちらも意味は同じ。他のスクリプトから呼び出せない機能として定義することになります)が、今回は先頭に public を付けます。public を付けることで、他のスクリプトからこの GameOver 関数を呼び出せるようになります。
GameOver 関数の中では、まず currentState を GameState.GameOver に変更しています。
|
1 |
currentState = GameState.GameOver; |
これでゲームの状態が「ゲーム中」から「ゲームオーバー」に変わります。
次にこちらで Game Over Text を表示しています。
|
1 |
gameOverText.SetActive(true); |
アクティブオブジェクトとは?
Unity のオブジェクトには、アクティブと非アクティブの状態があります。
- アクティブのオブジェクトはゲーム中に有効になっていて、画面に表示されたり、スクリプトが動いたりします。
- 非アクティブのオブジェクトは無効になっていて、画面に表示されず、基本的に動作もしません。ただし存在はしており、アクティブにすれば動作を開始します。
Inspector の左上にあるチェックが入っている状態がアクティブ、チェックが外れている状態が非アクティブです。

今回の GameOverText は、ゲーム開始時には表示したくないので、最初は非アクティブにしておきます。そしてゲームオーバーになったときに、
|
1 |
gameOverText.SetActive(true); |
を実行して、GameOverText をアクティブに変更します。これにより、Game Over の文字が画面に表示されます。
GameManager にスクリプトをアタッチしよう
ここまでできたら、Ctrl + S で必ず保存して Unity に戻りましょう。次に、 GameManager2D スクリプトを GameManager オブジェクトにドラッグ&ドロップしてアタッチしましょう。

アタッチできると、Inspector に GameManager2D が表示されます。GameManager2D の Game Over Text の欄に、Hierarchy にある GameOverText を設定します。

ここまでで GameManager の準備はできました。ただし、まだゲームオーバーにする処理を作成していません。
ここからは Enemy に横や下から触れたときに、GameManager の GameOver 関数を呼び出す処理を作成してゲームオーバー処理を構築します。
敵に触れたらゲームオーバーにしよう
ここからは、Enemy に触れたときにゲームオーバーになる処理を作成していきます。
前回作成した「Enemy を踏んだかどうかを判定する処理」を応用すれば、かなりシンプルに作成できます。現在は、Player が Enemy を上から踏んだときだけ敵を倒せるようになっています。
なので上から踏んでいないならゲームオーバーにするという処理を追加するだけでOKです。
スクリプトを書こう
まずは、EnemyPatrol2D をダブルクリックで開きましょう。

開けたら、以下のように追記します。
EnemyPatrol2D
|
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 |
using UnityEngine; public class EnemyPatrol2D : MonoBehaviour { [Header("Move Settings")] public float moveSpeed = 2f; public float moveDistance = 3f; [Header("Stomp Settings")] public float stompHeight = 0.3f; public float playerBouncePower = 8f; private Vector2 startPosition; private int moveDirection = 1; private SpriteRenderer enemySprite; private GameManager2D GameManager; // Start is called once before the first execution of Update after the MonoBehaviour is created void Start() { //最初の位置を保存 startPosition = transform.position; //スライムのSpriteRendererを取得 enemySprite = GetComponent<SpriteRenderer>(); //GameManager2Dをシーン内から探して取得 GameManager = FindAnyObjectByType<GameManager2D>(); } // Update is called once per frame void Update() { //敵を動かす transform.Translate(Vector2.right * moveDirection * moveSpeed * Time.deltaTime); //反復移動させる if (transform.position.x > startPosition.x + moveDistance) { moveDirection = -1; enemySprite.flipX = true; } else if (transform.position.x < startPosition.x - moveDistance) { moveDirection = 1; enemySprite.flipX = false; } } void OnCollisionEnter2D(Collision2D collision) { if (collision.gameObject.CompareTag("Player")) { if (IsStompedByPlayer(collision.gameObject)) { Destroy(gameObject); Rigidbody2D playerRigidbody = collision.gameObject.GetComponent<Rigidbody2D>(); playerRigidbody.linearVelocity = new Vector2(playerRigidbody.linearVelocity.x, playerBouncePower); } else { GameManager.GameOver(); } } } bool IsStompedByPlayer(GameObject player) { Rigidbody2D playerRigidbody = player.GetComponent<Rigidbody2D>(); bool playerIsAboveEnemy = player.transform.position.y > transform.position.y + stompHeight; bool playerIsFalling = playerRigidbody.linearVelocity.y <= 0f; return playerIsAboveEnemy && playerIsFalling; } } |
スクリプトの解説
今回追加した処理を順番に見ていきましょう。
まず、GameManager2D を入れるための変数を作成しています。
|
1 |
private GameManager2D GameManager; |
これは、GameManager2D の中にある GameOver 関数を呼び出すために使います。EnemyPatrol2D はゲームオーバーになったときの処理を持っていないので、GameManager2D に知らせる必要があります。
そのために Start の中で GameManager2D を取得しています。
|
1 2 |
//GameManager2Dをシーン内から探して取得 GameManager = FindAnyObjectByType<GameManager2D>(); |
FindAnyObjectByType は、Scene の中から指定した種類のコンポーネントを探して取得する処理です。
今回は Scene の中に GameManager オブジェクトがあり、そこに GameManager2D がアタッチされています。その GameManager2D を探して、GameManager 変数に入れています。
これで、EnemyPatrol2D から GameManager.GameOver(); を呼び出せるようになります。
次に、OnCollisionEnter2D の中を見ていきましょう。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
void OnCollisionEnter2D(Collision2D collision) { if (collision.gameObject.CompareTag("Player")) { if (IsStompedByPlayer(collision.gameObject)) { Destroy(gameObject); Rigidbody2D playerRigidbody = collision.gameObject.GetComponent<Rigidbody2D>(); playerRigidbody.linearVelocity = new Vector2(playerRigidbody.linearVelocity.x, playerBouncePower); } else { GameManager.GameOver(); } } } |
まず、ぶつかった相手が Player かどうかを確認しています。
Player にぶつかった場合は、IsStompedByPlayer で「上から踏んだかどうか」を判定します。上から踏んでいる場合は、これまで通り Enemy を Destroy して、Player を上方向にバウンドさせます。
そして今回追加したのが else の部分です。else は、if の条件に当てはまらなかったときに実行される処理です。今回の場合、
- if → Player が Enemy を上から踏んだ
- else → Player が Enemy を上から踏んでいない
という意味になります。Player が Enemy に触れているのに、上から踏んでいないということは、横や下から触れたということです。
そのため、else の中で GameManager.GameOver(); を実行して Game Over にしています。
|
1 2 3 4 |
else { GameManager.GameOver(); } |
この1行で、GameManager2D の GameOver 関数が呼ばれます。
動作を確認しよう
ここまでできたら、Ctrl + S で必ず保存して Unity に戻りましょう。再生して動作を確認します。

Enemy に横から触れたときに Game Over と表示されれば成功です。また、Enemy を上から踏んだときに、これまで通り Enemy を倒せるかも確認しておきましょう。
ここで確認すると、Game Over の文字は表示されますが、Player はまだ動ける状態になっています。次は、Game Over 後に Player を動かないようにする処理を作成していきます。
ゲームオーバーになったときにプレイヤーを動かさないようにしよう
Project ウィンドウから PlayerController2D をダブルクリックで開きましょう。

開けたら、以下のように追記します。
PlayerController2D
|
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 |
using UnityEngine; public class PlayerController2D : MonoBehaviour { [Header("Move Settings")] public float moveSpeed = 5f; public float jumpPower = 8f; [Header("Ground Check")] public Transform groundCheck; public float groundCheckRadius = 0.2f; public LayerMask groundLayer; [Header("Animation")] public float walkAnimationSpeed = 1f; private Rigidbody2D playerRigidbody; private float horizontalInput; private bool isGrounded; private SpriteRenderer playerSprite; private Animator playerAnimator; private GameManager2D GameManager; // Start is called once before the first execution of Update after the MonoBehaviour is created void Start() { //プレイヤーのRigidbody2Dを取得 playerRigidbody = GetComponent<Rigidbody2D>(); //プレイヤーのSpriteRendererを取得 playerSprite = GetComponent<SpriteRenderer>(); //プレイヤーのAnimatorを取得 playerAnimator = GetComponent<Animator>(); //GameManager2Dをシーン内から探して取得 GameManager = FindAnyObjectByType<GameManager2D>(); } // Update is called once per frame void Update() { //ゲームの状態を確認 if (GameManager.currentState != GameManager2D.GameState.Playing) { //状態がPlayingでなければ処理終了 horizontalInput = 0f; playerAnimator.speed = 0f; return; } // 入力、-1:左、0:なし、1:右、のように扱います horizontalInput = 0f; playerAnimator.speed = 0f; if (Input.GetKey(KeyCode.LeftArrow)) { horizontalInput = -1f; playerSprite.flipX = true; //アニメーションを再生 playerAnimator.speed = walkAnimationSpeed; } else if (Input.GetKey(KeyCode.RightArrow)) { horizontalInput = 1f; playerSprite.flipX = false; //アニメーションを再生 playerAnimator.speed = walkAnimationSpeed; } //GroundCheckに小さな円を作成、Groundレイヤーに接触しているか確認 isGrounded = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, groundLayer); if (Input.GetKeyDown(KeyCode.Space) && isGrounded) { playerRigidbody.linearVelocity = new Vector2(playerRigidbody.linearVelocity.x, jumpPower); } //地面にいないならアニメーションを止める if (!isGrounded) { playerAnimator.speed = 0f; } } void FixedUpdate() { // Rigidbody2Dの速度を変更して、左右に移動します。 playerRigidbody.linearVelocity = new Vector2(horizontalInput * moveSpeed, playerRigidbody.linearVelocity.y); } } |
スクリプトの解説
EnemyPatrol2D と同じように、最初に GameManager2D を取得しています。
|
1 |
private GameManager2D GameManager; |
この変数を用意することで、PlayerController2D から GameManager2D の currentState を確認できるようにしています。
Start の中では、Scene 内にある GameManager2D を探して取得しています。
|
1 2 |
//GameManager2Dをシーン内から探して取得 GameManager = FindAnyObjectByType<GameManager2D>(); |
これで、PlayerController2D から GameManager.currentState を使えるようになります。
今回大事なのは、Update の一番上に追加したif文です。
|
1 2 3 4 5 6 7 8 |
//ゲームの状態を確認 if (GameManager.currentState != GameManager2D.GameState.Playing) { //状態がPlayingでなければ処理終了 horizontalInput = 0f; playerAnimator.speed = 0f; return; } |
このif文では、GameManager の currentState を確認しています。!= は「同じではない」という意味です。つまり、「現在の状態が Playing ではないなら、この中の処理を実行する」という意味になります。
ゲームオーバーになると、currentState はゲームオーバーに変わります。すると、このif文の条件に当てはまるので、Player の操作処理に進む前に処理を止めます。
次はif文の中身をみていきましょう。まず、左右の入力を 0 にしています。
|
1 |
horizontalInput = 0f; |
horizontalInput は、FixedUpdate の中で Player の横移動に使われています。そのため、ゲームオーバーになったときに horizontalInput を 0 にすることで、右や左への移動入力が残らないようにしています。
次に以下でアニメーションを止めています。
|
1 |
playerAnimator.speed = 0f; |
ゲームオーバーになった後に歩きアニメーションが動き続けると不自然なのでここでアニメーションの再生速度を 0 にしています。
最後に Update の処理を終了させています。
|
1 |
return; |
return は、その関数の処理を途中で終わらせるための命令です。今回は Update の一番上で return しているので、その下にある左右入力、ジャンプ、接地判定、アニメーションの処理には進みません。これにより、ゲームオーバー後は Player の操作を受け付けないようになります。
通常時は currentState が Playing なので、このif文には入らず、左右移動やジャンプの処理が実行されます。
動作を確認しよう
ここまでできたら Ctrl + S で必ず保存してUnityに戻りましょう。再生して、Enemyに横から触れてゲームオーバーにしてみます。

Game Over と表示されたあと、Player の左右操作やジャンプができなくなっていれば成功です。
プレイヤーが落下したときにゲームオーバーにしよう
次は、Player がステージの下に落ちたときにゲームオーバーになる処理を作成します。
Player の Y 座標を確認して、決めた位置より下に落ちたら GameManager にゲームオーバーになったことを伝えるようにします。
Project ウィンドウから PlayerController2D をダブルクリックで開きましょう。

開けたら、以下のように追記します。
PlayerController2D
|
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 |
using UnityEngine; public class PlayerController2D : MonoBehaviour { [Header("Move Settings")] public float moveSpeed = 5f; public float jumpPower = 8f; [Header("Ground Check")] public Transform groundCheck; public float groundCheckRadius = 0.2f; public LayerMask groundLayer; [Header("Animation")] public float walkAnimationSpeed = 1f; [Header("Death")] public float fallY = -10f; private Rigidbody2D playerRigidbody; private float horizontalInput; private bool isGrounded; private SpriteRenderer playerSprite; private Animator playerAnimator; private GameManager2D GameManager; // Start is called once before the first execution of Update after the MonoBehaviour is created void Start() { //プレイヤーのRigidbody2Dを取得 playerRigidbody = GetComponent<Rigidbody2D>(); //プレイヤーのSpriteRendererを取得 playerSprite = GetComponent<SpriteRenderer>(); //プレイヤーのAnimatorを取得 playerAnimator = GetComponent<Animator>(); //GameManager2Dをシーン内から探して取得 GameManager = FindAnyObjectByType<GameManager2D>(); } // Update is called once per frame void Update() { //ゲームの状態を確認 if (GameManager.currentState != GameManager2D.GameState.Playing) { //状態がPlayingでなければ処理終了 horizontalInput = 0f; playerAnimator.speed = 0f; return; } //プレイヤーの落下判定 if(transform.position.y < fallY) { GameManager.GameOver(); } // 入力、-1:左、0:なし、1:右、のように扱います horizontalInput = 0f; playerAnimator.speed = 0f; if (Input.GetKey(KeyCode.LeftArrow)) { horizontalInput = -1f; playerSprite.flipX = true; //アニメーションを再生 playerAnimator.speed = walkAnimationSpeed; } else if (Input.GetKey(KeyCode.RightArrow)) { horizontalInput = 1f; playerSprite.flipX = false; //アニメーションを再生 playerAnimator.speed = walkAnimationSpeed; } //GroundCheckに小さな円を作成、Groundレイヤーに接触しているか確認 isGrounded = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, groundLayer); if (Input.GetKeyDown(KeyCode.Space) && isGrounded) { playerRigidbody.linearVelocity = new Vector2(playerRigidbody.linearVelocity.x, jumpPower); } //地面にいないならアニメーションを止める if (!isGrounded) { playerAnimator.speed = 0f; } } void FixedUpdate() { // Rigidbody2Dの速度を変更して、左右に移動します。 playerRigidbody.linearVelocity = new Vector2(horizontalInput * moveSpeed, playerRigidbody.linearVelocity.y); } } |
スクリプトの解説
今回追加したのは、落下したときにゲームオーバーにする処理です。
まずはどの高さまで落ちたらゲームオーバーにするか決める変数を作成しています。
|
1 2 |
[Header("Death")] public float fallY = -10f; |
fallY は、ゲームオーバーと判定する Y 座標です。Y 座標は、上に行くほど大きくなり、下に行くほど小さくなります。つまり、Player の Y 座標が -10 より小さくなったら、「かなり下まで落ちた」と判断できます。
次に、Update の中で Player の位置を確認しています。
|
1 2 3 4 5 |
//プレイヤーの落下判定 if(transform.position.y < fallY) { GameManager.GameOver(); } |
transform.position.y は、現在の Player の Y 座標です。この値が fallY より小さくなったら、Player がステージの下まで落ちたと判断します。
条件に当てはまった場合は、
|
1 |
GameManager.GameOver(); |
を実行して、GameManager にゲームオーバーになったことを伝えています。
ゲームオーバー処理自体は GameManager2D にまとめてあるので、PlayerController2D では「落下したことを判定して、GameManager に知らせる」だけで済みます。
このように実装するとゲームオーバーテキストの表示や状態管理を GameManager にまとめたまま、Player 側では落下判定だけを担当できます。
動作を確認しよう
ここまでできたら、Ctrl + S で必ず保存してUnityに戻りましょう。再生して、Player を足場の下に落としてみます。

Player が一定の高さより下に落ちたときに Game Over と表示されてプレイヤーを動かせなく慣れれば成功です。
プレイヤーが触れるとゲームオーバーになる障害物を追加しよう
最後に、触れるとゲームオーバーになる危険な障害物を追加します。今回は「とげ」を配置し、Player が触れたらゲームオーバーになるようにします。
Project ウィンドウから Assets > 横スクロールアクションゲーム画像素材 を開きましょう。その中にある「とげ」の画像を Scene ビューにドラッグ&ドロップします。

配置した「とげ」を選択し、位置を調整しましょう。

次に、とげに当たり判定を追加します。Inspector の Add Component から Physics 2D > Box Collider 2D を選択します。

今回はプレイヤーがとげに触れたかどうかを、スクリプトから Tag を使って判定します。
前回敵に触れたかどうかを判定するときに、
|
1 |
if (collision.gameObject.CompareTag("Player")) |
という処理を使いました。今回はこれと同じ考え方で、触れた相手に Death の Tag が付いていたらゲームオーバーにします。
まずは障害物用の Tag を作成します。Inspector 上部にある Tag から Add Tag を選択しましょう。

Tags の一覧が表示されたら、+ ボタンを押して、新しい Tag を追加します。名前は Death にしましょう。

Tag は、オブジェクトの種類をスクリプトから判別するための目印です。今回の場合は、「このオブジェクトに触れたら Game Over にする」という目印として Death Tag を使います。
Tag を作成したら、もう一度 Hierarchy の「とげ」を選択します。

Inspector 上部の Tag を Death に変更しましょう。

これで、この「とげ」はスクリプトから Death Tag のオブジェクトとして判定できるようになります。
とげは、ステージ内にいくつも配置したいオブジェクトです。そのため、Prefab にしておきましょう。Hierarchy にある「とげ」を、Project ウィンドウの Prefabs フォルダへドラッグ&ドロップします。

これで「とげ」の Prefab が作成されます。
このあと、とげを複製して配置していきます。ただし、そのまま複製していくと Hierarchy がごちゃごちゃしてしまいます。そこで、危険な障害物をまとめるための空のオブジェクトを作成します。Hierarchy で右クリックして、Create Empty を選択します。作成した空のオブジェクトの名前を DangerousObj に変更しましょう。

次に、Dangerous Obj を選択し、Inspector の Transform から Position の X、Y、Z を 0 にしておきましょう。

親オブジェクト自体をわかりやすい基準位置に置いておきます。
次に、Hierarchy にある「とげ」を Dangerous Obj にドラッグ&ドロップして、子オブジェクトにします。

これで、危険な障害物を Dangerous Obj の中にまとめられます。
あとは、CTRL + D でとげを複製してステージ上に配置していきましょう。今回は地面の上に置くので、位置調整もしやすいと思います。

スクリプトを書こう
ここまでできたら、Player が Death Tag の付いたオブジェクトに触れたときにゲームオーバーになる処理を書いていきます。
今回も難しい処理ではありません。Player が何かにぶつかったとき、ぶつかった相手の Tag が Death なら GameManager にゲームオーバーになったことを伝えるだけです。
Project ウィンドウから Assets > Scripts にある PlayerController2D をダブルクリックで開きましょう。

開けたら、以下のように追記します。
PlayerController2D
|
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 |
using UnityEngine; public class PlayerController2D : MonoBehaviour { [Header("Move Settings")] public float moveSpeed = 5f; public float jumpPower = 8f; [Header("Ground Check")] public Transform groundCheck; public float groundCheckRadius = 0.2f; public LayerMask groundLayer; [Header("Animation")] public float walkAnimationSpeed = 1f; [Header("Death")] public float fallY = -10f; private Rigidbody2D playerRigidbody; private float horizontalInput; private bool isGrounded; private SpriteRenderer playerSprite; private Animator playerAnimator; private GameManager2D GameManager; // Start is called once before the first execution of Update after the MonoBehaviour is created void Start() { //プレイヤーのRigidbody2Dを取得 playerRigidbody = GetComponent<Rigidbody2D>(); //プレイヤーのSpriteRendererを取得 playerSprite = GetComponent<SpriteRenderer>(); //プレイヤーのAnimatorを取得 playerAnimator = GetComponent<Animator>(); //GameManager2Dをシーン内から探して取得 GameManager = FindAnyObjectByType<GameManager2D>(); } // Update is called once per frame void Update() { //ゲームの状態を確認 if (GameManager.currentState != GameManager2D.GameState.Playing) { //状態がPlayingでなければ処理終了 horizontalInput = 0f; playerAnimator.speed = 0f; return; } //プレイヤーの落下判定 if(transform.position.y < fallY) { GameManager.GameOver(); } // 入力、-1:左、0:なし、1:右、のように扱います horizontalInput = 0f; playerAnimator.speed = 0f; if (Input.GetKey(KeyCode.LeftArrow)) { horizontalInput = -1f; playerSprite.flipX = true; //アニメーションを再生 playerAnimator.speed = walkAnimationSpeed; } else if (Input.GetKey(KeyCode.RightArrow)) { horizontalInput = 1f; playerSprite.flipX = false; //アニメーションを再生 playerAnimator.speed = walkAnimationSpeed; } //GroundCheckに小さな円を作成、Groundレイヤーに接触しているか確認 isGrounded = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, groundLayer); if (Input.GetKeyDown(KeyCode.Space) && isGrounded) { playerRigidbody.linearVelocity = new Vector2(playerRigidbody.linearVelocity.x, jumpPower); } //地面にいないならアニメーションを止める if (!isGrounded) { playerAnimator.speed = 0f; } } void FixedUpdate() { // Rigidbody2Dの速度を変更して、左右に移動します。 playerRigidbody.linearVelocity = new Vector2(horizontalInput * moveSpeed, playerRigidbody.linearVelocity.y); } void OnCollisionEnter2D(Collision2D collision) { if(collision.gameObject.CompareTag("Death")) { GameManager.GameOver(); } } } |
スクリプトの解説
今回追加したのは最後のこの処理です。
|
1 2 3 4 5 6 7 |
void OnCollisionEnter2D(Collision2D collision) { if(collision.gameObject.CompareTag("Death")) { GameManager.GameOver(); } } |
OnCollisionEnter2D は、Collider 2D系統のコンポーネントを持つオブジェクト同士がぶつかったときに呼ばれる処理です。
プレイヤーが○○ Collider 2Dを持つオブジェクトにぶつかったときにこの中の処理が実行されます。
collision.gameObject には、Player がぶつかった相手のゲームオブジェクトが入っています。その相手に Death Tag が付いているかどうかを、CompareTag で確認しています。
|
1 |
if(collision.gameObject.CompareTag("Death")) |
これは、ぶつかった相手の Tag が Death ならという意味です。条件に当てはまった場合は、
|
1 |
GameManager.GameOver(); |
を実行してゲームオーバーにします。これで、Player が Death Tag の付いた「とげ」に触れたとき、GameManager にゲームオーバーになったことが伝わります。
動作を確認しよう
ここまでできたら、Ctrl + S で必ず保存してUnityに戻りましょう。再生して、プレイヤーをとげに触れさせてみましょう。

とげに触れた瞬間に Game Over と表示されれば成功です。
プロジェクトファイルのダウンロード
今回の講座で作ったプロジェクトファイルのダウンロードリンクを用意しました。
まとめ
今回は2Dジャンプアクションゲームにプレイヤーの敗北・ゲームオーバー処理を実装しました。
今回の一番大事なポイントは、GameManager を作成し、ゲームの状態をまとめて管理したことです。
敵キャラに横から触れたとき、ステージ下に落ちたとき、とげに触れたときなど、ゲームオーバーになる条件はいくつかあります。
しかし、Game Stateの状態変更や Game Over テキストの表示は GameManager にまとめています。
そのため、それぞれのスクリプトではゲームオーバーになったことを GameManager に伝えるだけでゲームオーバー処理を実装できます。
このようにゲーム全体の状態を管理する場所を用意しておくと処理のつながりをわかりやすくできます。
次回はいよいよ仕上げです。
ゲームクリアとリトライ機能を作成し、ゲームとして最後まで遊べる形にしていきます。
さらに、音も追加してゲームの完成度を上げていきましょう。
次の記事:

現場レベルのゲーム制作が、すべてここで学べます。






コメント