シンプルなアクションゲームを作ってみようの第12回です。
今回はキャラクターがゲームのクリアエリアに到達したらゲームを終了する機能を作成していきます。
前回はゲームの舞台のゲームオブジェクトにPhysic Materialを設定しました。

シンプルなアクションゲームを作ってみようの他の記事は

シンプルなアクションゲームを作るのを通してUnityの使い方を学ぶカテゴリです。
から参照出来ます。
ゲームのクリアエリアの作成
キャラクターが到達したらゲームクリアとするエリアを作成します。
Stage1シーンのEnvironmentゲームオブジェクトを選択した状態で右クリックから3D Object→Cubeを選択し、名前をGoalAreaとします。
ヒエラルキーでGoalAreaを選択し、インスペクタで以下のように設定します。
Transformの値はゴールエリアの位置やサイズの調整の為の変更です。
またBox ColliderのIs Triggerにチェックを入れます。
Is Triggerにチェックを入れると他のゲームオブジェクトとの衝突が起きなくなり、スクリプトで他のゲームオブジェクトのコライダの検知に使用することが出来ます。
ゴールエリアはゴールの空間として使うので物理的に衝突をせず検知エリアとして使います。
GoalArea用のマテリアルの作成
ゴールエリアはゴールとする空間なので、半透明の表示にしたいと思います。
Assets/Materialsフォルダ内に右クリックからCreate→Materialを選択し、名前をGoalAreaとします。
GoalAreaマテリアルを選択し、インスペクタで色の変更と透明度の変更をします。
Rendering ModeをTransparentにし透明可能なモードにします。
次にAlbedoの横の白色部分を押し、新しく出てきたウインドウでR(赤)に255、G(緑)に0、B(青)に0、A(255で完全に見え、0で完全に見えない)に100を設定します。
設定する値は0~255の値を設定します。
これで少し透明な赤色のマテリアルが出来ます。
マテリアルが出来たら、ヒエラルキーのGoalAreaゲームオブジェクトにドラッグ&ドロップして設定します。
シーンビューで確認すると、以下のような位置とサイズと色合いになっているのを確認出来ます。
ゲームオーバーエリアの作成
次にゲームオーバーするエリアを作成していきます。
今回作成したゲームの舞台は横に壁がないので、床を降りると永遠に落下するだけでゲームを進行できなくなります。
そこでゲームの舞台から落下した場合はゲームオーバーとします。
単純にキャラクターのYの位置が一定の値に達したらゲームオーバーとしてもいいんですが、今回はゲームオーバーエリアを作成し、その範囲にキャラクターが侵入したらゲームオーバーとすることにします。
Environmentゲームオブジェクトの子にあるWholeFloorゲームオブジェクトを選択した状態で右クリックから3D Object→Cubeを選択し、名前をGameOverAreaとします。
GameOverAreaを選択し、インスペクタでTransformの値の変更とBox ColliderのIs Triggerにチェックを入れます。
GameOverAreaはキャラクターが侵入したかどうかの検知範囲に使用するので、Box ColliderのIs Triggerにチェックを入れています。
Transformの値は、ゲームの全体の床よりも大きいサイズにし、位置を少し下げる為の設定です。
GameOverAreaはキャラクターを検知するためだけのゲームオブジェクトなので見た目は必要ありません。
そこでインスペクタのCube(Mesh Filter)とMesh Rendererコンポーネントの右の3つの丸を押し、Remove Componentを選択し、削除します。
GameOverAreaゲームオブジェクトのインスペクタは以下のようになり、
シーンビューで確認するとWholeFloorの下にそれよりも大きいサイズのGameOverAreaがあるのを確認出来ます。
ゲームマネージャーの作成
ゲームのクリアやゲームに関する事を管理するGameManagerゲームオブジェクトを作成し、それにGameManagerスクリプトを取り付けて管理出来るようにします。
ヒエラルキーで右クリックからCreate Emptyを選択し、名前をGameManagerとします。
ゲームマネージャースクリプトの作成
次にAssets/ScriptsフォルダにGameManagerスクリプトを作成し、GameManagerゲームオブジェクトに取り付けます。
GameManagerという名前のスクリプトを作ると、他のスクリプトと違って歯車のアイコンになりますが、これは通常のスクリプトと変わりません。
UnityではGameManager(という名前)は重要なスクリプトなので、アイコンを別にしているのかもしれません。
GameManagerスクリプトに記述していきます。
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 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class GameManager : MonoBehaviour { // ゲームオーバーかどうか public bool GameOver { get; set; } // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { } // ゲームクリア時に実行する public void ClearGame() { GameOver = true; } // ゲームをクリア出来なかった時に実行する public void EndGame() { GameOver = true; } } |
GameOverプロパティはゲームオーバーかどうかを設定したり取得出来ます。
ClearGame、EndGameメソッドはそれぞれクリアした時、ゲームオーバーになった時に外部から呼び出します。
現時点ではGameOverプロパティにtrueを入れているだけです。
GameManagerスクリプトは、他の機能を追加していく過程で処理を追加していきます。
キャラクターを検知するスクリプトの作成
GoalAreaゲームオブジェクトとGameOverAreaゲームオブジェクトが出来たので、それぞれにキャラクターを検知するスクリプトを作っていきます。
GoalAreaスクリプトの作成
Assets/ScriptsフォルダにGoalAreaスクリプトを作成し、GoalAreaゲームオブジェクトに取り付けます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class GoalArea : MonoBehaviour { // ゲームマネージャー private GameManager gameManager; // ゲームクリアフラグ private bool gameClear; void Start() { // ゲームマネージャーの取得 gameManager = GameObject.Find("GameManager").GetComponent<GameManager>(); } // エリア内にプレイヤーが侵入したらゲームクリア private void OnTriggerEnter(Collider other) { if (!gameClear && other.CompareTag("Player")) { gameClear = true; gameManager.ClearGame(); } } } |
gameManagerはGameManagerスクリプトを入れるフィールドです。
gameClearは一旦ゲームクリアと判定されたらtrueにし、次に同じ条件になっても再度クリアと判定しない為のフラグです。
StartメソッドではGameObject.Findを使ってGameManagerという名前のゲームオブジェクトを探し、そこからGetComponentでGameManagerスクリプトを取得しています。
GameObject.Findメソッドは非常に時間がかかる処理なのでStartメソッドで1回だけ実行しています。
もし処理が遅いようならばgameManagerフィールドに[SerializeField]アトリビュートを取り付けて、GoalAreaゲームオブジェクトのインスペクタのGoalAreaコンポーネントのgameManagerに直接GameManagerゲームオブジェクトをドラッグ&ドロップして設定しておくことも出来ます。
OnTriggerEnterメソッドは、GoalAreaスクリプトが取り付けられているゲームオブジェクトのコライダに、他のコライダを持つゲームオブジェクトが侵入した時に呼ばれるメソッドです。
説明が長くて分かり辛いかもしれませんが、要はゴールエリア内に他のコライダが入ってきたら呼ばれます。
Collider型の引数を受け取ります。
Collider型はCapsule ColliderやSphere Collider等のコンポーネント(スクリプト)の継承元になっている型で、親の型を引数として指定しておけば、そこから継承して作成したクラスの型は引数として受け取れるようになっています。
つまり、親の型にはそれを継承したクラスの型であれば代入できるという事です。
なので、どんなコライダであれ(Collider型を継承して作成されたコライダであれば)OnTriggerEnterメソッドの引数のColliderに値が渡されます。
!gameClearはゲームクリアをしていない状態。
other.CompareTag(“Player”)は、侵入したコライダのゲームオブジェクトのタグがPlayerかどうかで
&&記号(日本語だと かつ)でこれら二つの条件が両方成立していればtrueになります。
この処理は最初にgameClearかどうか判定し、そこでreturnしてそれ以降の処理をしない条件にし、その後コライダのタグを判定するのでも構いません。
&&記号
&&は左右の条件が両方成立した場合にtrueを返しますが、
1 2 3 | if (!gameClear & other.CompareTag("Player")) { |
上のように&ひとつでも出来ます。
上の場合は!gameClearの条件が成立しなくてもother.CompareTag(“Player”)の条件が成立するかも実行します。
&&の場合は最初の条件!gameClearがfalseになった時点で次のother.CompareTag(“Player”)は実行しません(通常はする必要がないので)。
なので、通常は&&で、他の条件の処理も実行する必要がある場合は&にします。
ゲームクリアしていない かつ 侵入したゲームオブジェクトのコライダのタグがPlayerだった場合はgameClearにtrueを入れ、GameManagerスクリプトのClearGameメソッドを呼び出しています。
ここでは&&(かつ)についてやりましたが||(または)の場合も同じで、||の場合は左右の条件のどちらかひとつがtrueになれば全体としてtrueになるので、||の左の条件がtrueになれば右の条件の処理は実行しません。
実行したい場合は|ひとつを使います。
GameOverAreaスクリプトの作成
Assets/ScriptsフォルダにGameOverAreaスクリプトを作成し、ヒエラルキーのGameOverAreaゲームオブジェクトに取り付けます。
GameOverAreaスクリプトを作成していきます。
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; public class GameOverArea : MonoBehaviour { private GameManager gameManager; // Start is called before the first frame update void Start() { gameManager = GameObject.Find("GameManager").GetComponent<GameManager>(); } // 何らかのコライダが進入してきたら実行 private void OnTriggerEnter(Collider other) { if (other.CompareTag("Player")) { gameManager.EndGame(); } } } |
ほぼGoalAreaスクリプトと同じなので説明は割愛させて頂きます。
違う部分はGameManagerのEndGameメソッドを呼んでいる部分だけです。
GoalAreaスクリプトとGameOverAreaスクリプトはプレイヤーを検知し、何らかの処理を実行するという目的は同じなので、インスペクタで「ゴールにするのか」、「ゲームオーバーにするのか」の設定を出来るようにし、その設定によって処理をif文で分岐して、実行したい処理を分けるというやり方も出来ます。
今回は設定するスクリプトを分けました。
タグを設定しよう
GoalAreaスクリプトとGameOverAreaスクリプトではキャラクターにPlayerタグが設定されているかどうかで判定しています。
そこでEthanにPlayerタグを設定したいと思います。
ヒエラルキーでEthanを選択し、インスペクタでTagの部分を押しPlayerタグを選択します。
Playerタグは最初からあります。
他のタグを作りたい場合はAdd Tag…を選択し、Tagsの+を押して新しいタグを追加出来ます。
キャラクタ移動スクリプトに処理を追加する
ゲームクリア、ゲームオーバーになったら、GameManagerゲームオブジェクトのGameManagerスクリプトでGameOverプロパティをtrueにしました。
GameOverプロパティがtrueの場合はキャラクターの操作も出来ないようにします。
PlayerControllerスクリプトを開き、処理を追加します。
まずはフィールドにgameManagerとcanControlを用意します。
1 2 3 4 5 6 | // ゲームマネージャー private GameManager gameManager; // キャラクターを操作可能かどうか private bool canControl; |
gameManagerはGameManagerスクリプトを入れるフィールドでcanControlはキャラクターが操作可能かどうかのフィールドです。
次にStartメソッドでGameManagerスクリプトの取得をします。
1 2 3 4 5 6 7 8 9 10 11 | void Start() { gameManager = GameObject.Find("GameManager").GetComponent<GameManager>(); rigidBody = GetComponent<Rigidbody>(); myCollider = GetComponent<CapsuleCollider>(); animator = GetComponent<Animator>(); canControl = true; } |
Rigidbodyの取得等の前にGameManagerを取得する処理を追加しています。
GameManagerスクリプトの取得方法はGoalAreaスクリプト等で行った方法と同じです。
またcanControlをtrueにし、キャラクターを操作可能であると設定します。
次にUpdateメソッドでCheckGroundメソッドを呼ぶ前に処理を追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | void Update() { // 操作不可能の時は以降何もしない if (!canControl) { return; } // ゲームオーバー時はリセットしそれ以降は何もしない if (gameManager.GameOver) { velocity = Vector3.zero; animator.SetFloat("Speed", 0f); rigidBody.isKinematic = true; canControl = false; return; } // 接地確認 CheckGround(); // 移動速度の計算 Move(); } |
まず最初に、!canControlで操作可能でない時はreturnを使ってそれ以降の処理を実行しません。
GameManagerのGameOverプロパティがtrueの時は既にゲームクリアかゲームオーバーの状態なので、キャラクターを移動させたくありません。
そこでキャラクターの移動速度を0にし、アニメーションパラメーターのSpeedを0にしてアニメーションをIdleにし、RigidbodyのisKinematicをtrueにし、canControlをfalseにしたあとreturnでそれ以降の処理をせず終了します。
RigidbodyのisKinematicをtrueにすると、スクリプトでしかRigidbodyを操作出来なくなるので力を加える事も力を受ける事もなくなります。
今回は2段階の条件でreturnでそれ以降の処理を回避していますが、これはgameManager.GameOverだけで判断すると毎回velocityやアニメーションパラメータ―のSpeedを設定し直す必要が出てくるので、canControlフィールドを用意し、一度値をリセットしたらそれ以降は同じ処理をしないようにしています。
!canControlの条件のところをrigidBody.isKinematicに変えても出来ますが、それだと後からスクリプトを見た時になんでその条件を付けたのか忘れる可能性もあるのでcanControlを新たに作って条件に設定しました。
実行して確認する
これで機能が出来たのでキャラクターを移動させて床を外れて落下させたり、ゴールエリアに接触してキャラクターを操作出来なくなるか確認してみましょう。
上のようになりました。
終わりに
キャラクターが侵入したことをコライダで検知し、ゲームマネージャーでゲームの管理をするということをやりました。
次回はゲームのタイマーを作成し、ゴールするまでの時間を計測出来るようにしていきます。