解説

Unityでブロック崩しを作成する:その2

前回に引き続き、ブロック崩しの作成を行います。今回はゲームクリア/ゲームオーバー処理を実装します。

Unityプログラミング講座一覧はこちら

Unityプログラミング講座
ゲームエンジンUnityを使ってゲーム制作を始めようと思っている人へ向けた解説記事です。初心者がUnityをインストールするところから始めてゲームを制作するのに必要な知識をできる限りステップバイステップでまとめるつもりです。

ゲームオーバーを実装する

ブロック崩しはボールが下に行ってしまったらミスとなります。そこで、下の壁に当たったときにボールを削除することにしましょう。

以下のスクリプトを作成し、下の壁にアタッチしてください。これで、下の壁に当たったときにボールが消えるようになります。

using UnityEngine;

class GameOver : MonoBehaviour
{
    // 衝突時に呼ばれる
    void OnCollisionEnter(Collision collision)
    {
        // 当たったゲームオブジェクトを削除する
        Destroy(collision.gameObject);   
    }
}

衝突処理は、前回のブロックの処理と同様に衝突時に呼ばれるOnCollisionEnterメソッドを使用します。

削除する処理も前回同様にDestroyメソッドを使えば問題ありません。ただし、ブロックの処理では削除するものがブロック自身だったので自分自身のゲームオブジェクトを表すgameObjectを指定していましたが、今回は衝突した相手(この場合はボール)を削除するため、相手のゲームオブジェクトを指定しなければなりません。

相手の情報はOnCollisionEnterメソッドの引数であるCollision型の変数collisionに含まれており、collision.gameObjectとすれば、衝突した相手のゲームオブジェクトが得られます。

※OnCollisitonEnterメソッドの引数の変数名がcollisionでない場合(括弧で括られた部分)は、必要に応じてcollisionを変数名で読み替えてください。

参考:自分自身へのアクセスと衝突相手へのアクセスの比較

対象自分自身衝突相手
ゲームオブジェクトgameObjectcollision.gameObject
コンポーネント TGetComponet<T>()collision.GetComponent<T>()

ゲームクリアを実装する

すべてのブロックを壊すことができれば、ゲームクリアです。ゲームクリアしたらどうなるのかという点でいえば、「次のステージに進む」、「リザルト画面を表示する」といったいくつかの案が考えられますが、ここでは簡単に「ゲームをストップする」処理を行うことにします。

すべてのブロックが破壊されたことを判定する方法はいくつかありますが、今回は親子関係を利用して実装してみます。

親子関係とはゲームオブジェクト同士の関係を表すものです。具体的には以下の特徴があります。

  • 親のゲームオブジェクトが動くと、子供のゲームオブジェクトも一緒に動く
  • 子供のゲームオブジェクトが動いても、親のゲームオブジェクトは動かない

この性質から、親のゲームオブジェクトの移動に対して一緒に動いて欲しい場合によく使用されます。例えば、プレイヤーキャラクターが身に着ける装備はプレイヤーキャラクターの子供にしておけばいいわけです。

今回の使用目的はそうではなく、ゲームオブジェクトの整理や管理のために親子関係を用います。フォルダのように階層分けすると言えば、イメージが伝わるでしょうか。

親子関係を作成するためには、Hierarchyビュー上で、子供にしたいゲームオブジェクトを親にしたいゲームオブジェクトにドラッグ&ドロップします。

今回はブロックを管理する親としてBlocksゲームオブジェクトを作成します。管理に使用するだけで、画面上に表示する必要がないので、Hierarchyビューで右クリック、Create Emptyを選択して空のゲームオブジェクトを作成してください。そして、作成した空のゲームオブジェクトにBlockゲームオブジェクトをドラッグしてBlockが子供になるようにします。

親となるゲームオブジェクトに以下のスクリプトをアタッチしてください。

using UnityEngine;

class GameClear : MonoBehaviour
{
    Transform myTransform;

    void Start()
    {
        // Transformコンポーネントを保持しておく
        myTransform = transform;
    }

    void Update()
    {
        // 子供がいなくなったらゲームを停止する
        if (myTransform.childCount == 0)
        {
            Time.timeScale = 0f;
        }
    }
}

子供のゲームオブジェクトの数を調べるには、TransformコンポーネントのchildCountを使用します。Transformコンポーネントに何度もアクセスするため、Startメソッドで変数に保持(キャッシュ)しておきます。

「ブロックが全て無くなった」=「子供のゲームオブジェクトの数が0になった」ですので、Updateメソッド内で子供のゲームオブジェクトの数をチェックして0の時にクリアの処理を行います。

今回はクリア時に「ゲームを停止する」ということなので、ゲーム内時間を設定するTime.timeScaleの値を変更することにします。Time.timeScaleは動画の再生速度を考えるとわかりやすいと思います。つまり、1の場合には通常と同じ速さでゲームが進み、1より大きい場合には早送りのようになります。2にすれば2倍速に、1より小さくすればスロー再生ということですね。

クリア時にはTime.timeScaleを0にしてゲームを停止します。これで、ブロックを全て壊した際に、ゲームがストップするはずです。

Time.timeScaleの影響範囲

timeScaleにより影響を受けるのは主に物理演算処理やアニメーション機能を使用した場合です。そのためtimeScaleを0にした場合、Rigidbodyを用いて物理演算で動かしている場合にはそのゲームオブジェクトは停止します。

一方で、停止しないものもあり、例えば、UpdateメソッドはtimeScaleが0の場合でも毎フレーム(通常1秒に60回)実行されます。

timeScaleを使用する際には、何が影響を受けるのか/受けないのかという点に注意してください。

文字を表示してプレイヤーに伝える

処理自体は書けましたが、このままではゲームオーバー/ゲームクリアしたことがプレイヤーに伝わりません。そこで、「Game Over」、「Game Clear」と画面に表示するようにします。

テキストの作成

Canvasの設定

画面に文字を表示したり、プレイヤーが押せるボタンといったUI要素を使用する場合はCanvasを作成します。CanvasはHierarchyビューのメニューでUI->Canvasから作成できますが、他のUI要素を作成した場合にも自動で作成されます。

UI->Textを選択して作成してください。以下の画像のように、Canvasの子としてTextゲームオブジェクトが作成されます。また、同時にUIのイベント処理を行うEventSystemが作成されますが、今回はUIのイベント処理は使用しないため、ここでは説明しません。(今回のゲームでは、EventSystemを消しても問題なく動くはずです。)

作成されたCanvasの設定を行います。項目がいくつかありますが、最初に気にするのはRender ModeとUI Scale Modeの2つです。

CanvasコンポーネントのRender ModeはUIをどのように表示するかのオプションです。今回は、文字を常に表示するようにしたいので、Screen Space – Overlayを選択します。

Render Mode機能
Screen Space – Overlay常に画面手前に描画する。カメラがなくても表示される。
Screen Space – Camera指定したカメラの前に描画する。カメラが無いと表示されない。指定した距離よりも近くにあるゲームオブジェクトはUIより手前に表示される。
World Spaceシーン内の平面オブジェクトであるかのように描画する。ゲーム内スクリーンや看板などに使用する。

Canvas ScalerのUI Scale ModeはUIの大きさをどう制御するかのオプションです。デフォルトではConstant Pixel Sizeになっていると思いますが、これだとウィンドウ画面の大きさによって大きさが変わってしまうので、Screen With Screen Sizeにしましょう。画面解像度を固定するようなゲーム(ドット絵をつかうようなゲーム)以外にはこの設定にしておくことをお勧めします。

※画面比率の変化まで考慮する場合にはScreen Match ModeをExpandにすると良いと思います。

Textの設定

UI要素は通常のゲームオブジェクトとは異なる座標計算をするので注意が必要です。Textをダブルクリックしてみると、BlockやPlayerが置いてある位置とは別のところにあることがわかります。Canvasを表す枠線がゲーム画面に対応するので、画面中央に文字が来るように位置を調整しましょう。

上の画像のように設定しました。文字の色は背景に埋もれないように白色にしています。また、画面中央に表示するということなので、中央揃えにしています。

フォントサイズがRect TransformのWidthとHeightの値に比べて大きい場合には文字が表示されないので注意しましょう。

スクリプトからTextの内容を変更する

最初は何も表示しないようにしておく

最初は文字を表示しないように、Textの文字を消すことにします。スクリプトを書かずにTextコンポーネントのTextの項目を無しにしてもいいですが、あまりお勧めしません。なぜなら、表示する場所や大きさを変更する場合に、

  1. Textに文字を入力してSceneビューで文字を確認できるようにする。
  2. 位置や大きさを変更する。
  3. Textの文字を消す。

というように手順が増えるからです。ゲームスタート時に自動でTextの文字を消す処理を入れておけば、この手間を省くことができます。

以下のスクリプトを作成してTextにアタッチしてください。

using UnityEngine;
// Textコンポーネントを使用する場合に必要
using UnityEngine.UI;

class ResetText : MonoBehaviour
{
    void Start()
    {
        // アクセスは1回きりなので、フィールド変数を用意しなくてもいい
        Text myText = GetComponent<Text>();
        // textに空の文字列を設定する
        myText.text = "";
    }
}

TextコンポーネントはUnityEngine.UI名前空間に存在するコンポーネントですので、使用する場合は、usingを使って宣言する必要があります。文字を設定するにはTextコンポーネントのtextが使用できます。空の文字列を代入することで何も表示しないようにします。

ゲームオーバー時には「Game Over」に変更する

GameOverスクリプトを以下のように変更してください。

using UnityEngine;
// 追加
using UnityEngine.UI;

class GameOver : MonoBehaviour
{
    // publicにしてInspectorから設定できるようにする
    public Text gameOverMessage;

    void OnCollisionEnter(Collision collision)
    {
        // Game Overと表示する
        gameOverMessage.text = "Game Over";
        Destroy(collision.gameObject);   
    }
}

GameOverスクリプトがアタッチされているゲームオブジェクトはTextゲームオブジェクトとは異なるため、今までのようにアクセスすることができません。異なるゲームオブジェクトにアタッチされているコンポーネントにアクセスする最も簡単な方法はpublic 変数を用意してInspector上で設定することです。ただし、設定し忘れるとエラーが起こるので注意してください。

設定はHierarchyビューまたはSceneビューで下の壁を選択して、InspectorビューのGame Over Messageとなっている箇所にTextゲームオブジェクトをドラッグ&ドロップすることで可能です。他にも右側にある丸のマークをクリックして選択してもよいです。Textゲームオブジェクトを指定しましょう。

ゲームクリア時には「Game Clear」に変更する

ゲームクリア時も同様にTextゲームオブジェクトのTextコンポーネントにアクセスして文字列を設定しましょう。

GameClearスクリプトを以下のように変更してください。

using UnityEngine;
using UnityEngine.UI; // 追加

class GameClear : MonoBehaviour
{
    public Text gameClearMessage; // 追加
    Transform myTransform;

    void Start()
    {
        myTransform = transform;
    }

    void Update()
    {
        if (myTransform.childCount == 0)
        {
            gameClearMessage.text = "Game Clear"; // 追加
            Time.timeScale = 0f;
        }
    }
}

こちらもGameOverスクリプト同様、Inspector上でTextゲームオブジェクトを設定することを忘れないようにしましょう。

以上で、ゲームクリア/ゲームオーバー処理の最低限の実装が完了です。

まとめ

今回はゲームクリア/ゲームオーバー処理を実装しました。これで、最低限ゲームの形になったと思います。ただし、現状だと1度しかプレイできない、スタートと同時にボールが動いてしまう、などの不便な点があります。次回以降はそこを改善していきます。

Unityプログラミング講座一覧はこちら

Unityプログラミング講座
ゲームエンジンUnityを使ってゲーム制作を始めようと思っている人へ向けた解説記事です。初心者がUnityをインストールするところから始めてゲームを制作するのに必要な知識をできる限りステップバイステップでまとめるつもりです。
タイトルとURLをコピーしました