【Unity】2Dシューティングゲームの作り方⑨:ライフとスコアを表示する

Unity

こんにちは、ともくんのゲーム作り部屋にようこそ!

このページでは、Unityを使った2Dシューティングゲームの作り方9回目として、ゲームマネージャーを置いてスコアやライフを表示する処理について紹介しています。

前回、敵を複製してステージを作っていきました。

今はゲーム内で敵からダメージを受けても、ただリトライされるようになっていますが、今度はゲームらしさを出すためにライフを作りそれを減らす処理を作っていきます。

また、敵を倒したタイミングでスコアをカウントして、スコアを自動的に加算していく仕組みも作りたいです。

そこで、ゲームマネージャーをおいてスコアライフを管理、それをUIで表示させる処理までをまとめていきます。

この記事を書いた人

ゲーム作りを学び始めた一児のパパです。
このブログは、子供から「ゲームを作ってみたい!」と言われ、非プログラマーでゲーム作りをしたことない僕が、ゲーム作りの本を読んで独学でゲーム開発を学んでいるブログです。
同じように初めてゲーム作りをしている方と一緒に学んでいけるようなブログに出来たらいいなと思っています。
また、「このコードはおかしい」とか「もっと良い書き方があるよ!」などあれば、どんどん指摘して頂けると助かります。

ゲームマネージャーを置いてライフとスコアの変数を作成する

まずは、ゲーム内でデータを管理するためのオブジェクトとして「ゲームマネージャー」を作っていきます。

このゲームマネージャーの中で、ゲーム内でのライフスコアなどのデータ(変数)を管理していくことになります。

まず、空のオブジェクトを作成して、そこにアタッチする形で新しいスクリプトを作っていきます。

今回は、「GameManager」という名前でどちらも作成しています。

using UnityEngine;

public class GameManager : MonoBehaviour
{
    public static GameManager instance; // 静的なGameManagerのインスタンスを作成
    public int lifeCount;   // ライフの数を計算する変数
    public int scorePoint;  // スコアの数を計算する変数

    void Awake()    // Startメソッドよりも早く処理される
    {
        if (instance == null)   // インスタンスの中身が無い場合
        {
            instance = this;    // インスタンスの中身を入れる
            DontDestroyOnLoad(this.gameObject); // シーンが読み込まれても削除されないようにする
        }
        else
        {
            Destroy(gameObject);    // GameManagerを削除する
        }
    }

}

5行目で、staticを付けてGameManagerのインスタンスを作成しています。

staticにしておくことで、GameManagerが静的な動かないものとなり、他のクラス(スクリプト)から非常にアクセスしやすくなります。

6~7行目で、ライフの数スコアの数を計算するための変数を作成していて、これらはpublicアクセス修飾子を付けておくことで、他のスクリプトから書き換えられるようにしています。

次に9行目で、Awakeメソッドを使っています。

このAwakeメソッドは、Startメソッドよりも早く処理されて、GameManagerのインスタンス作成直後に呼ばれることになります。

このAwakeメソッドの中で、インスタンスの中身が無い場合は、13行目で「this」を指定してそのGameManager自身を入れてあげるという処理を行い、すでにインスタンスの中身がある場合は、18行目でそのGameManagerのオブジェクトをDestroyメソッドで削除して重複させないようにしています。

こう書いてあげることでGameManagerは、常に一つだけのものとなり、その中でデータの管理を行うことができます。

なお、このスクリプトの書き方をシングルトンと呼び、GameManagerでよく使われる方法です。

そして、14行目でDontDestroyOnLoadメソッドを使っています。

このDontDestroyOnLoadというメソッドは、シーンが切り替わってもそのオブジェクトを破壊しないという処理を行ってくれます。

そのため、一度作成されたGameManagerが、シーンを切り替えてもそのまま残ってくれるので、ライフを引き継いだり、スコアを別のシーンで表示させたりということができるようになります。

また、先ほどGameManagerが複製されないようにチェックしたのも、シーンが再度読み込まれた際に、別のGameManagerが作成されてしまうのを防ぎ、常に同じGameManagerでデータを管理していくことができるようにするためです。

これで、ライフやデータの変数を管理するためのゲームマネージャーを作ることができました。

ライフをUIに表示させていく

まずは、ゲーム内にライフを表示させてダメージを受けたら、減っていく処理を作っていきます。

UIにライフマークを表示させる

最初に、UIに表示させるライフマークを配置していきます。

まず、ライフマークに使う画像をUnityにインポートしておきましょう。

次に、ヒエラルキーウィンドウから「UI」を押して「Image」を選択します。

すると、Canvasという親オブジェクトと、imageという子オブジェクトができます。

このCanvasのオブジェクトを選択して、インスペクターウィンドウのRender Modeから「Screen Space – Camera」を選択します。

そして、Render Cameraの項目に「Main Camera」のオブジェクトをドラッグ&ドロップしてあげることで、カメラの描画範囲内にUIを表示させることができます。

なお、ここで一旦、Canvasの名前をUI、imageの名前をlifeに変更して分かりやすくしています。

次に、lifeを選択してSource Imageの欄に、先ほどインポートしたハートマークをドラッグ&ドロップします。

インスペクターウィンドウの上に表示されているRect Transform内の以下の赤枠を選択します。

Anchor Presetsが開き、UIを画面のどの部分に表示したいかを選択することができます。

これは、ゲームを遊ぶデバイスによって画面の大きさが変わってきてしまい、UIが表示されないのを防ぐための項目です。

今回左下を選択しますが、こうすることで左下を座標の軸として、そこからどのくらい離してUIを表示させるかということを決めることで、どのゲーム画面でも同じ場所にUIを表示させることができます。

最後に、大きさや座標の位置を編集してライフマークの配置を決めていきます。

今回は、以下のような感じで設定してみました。

ライフのカウント数を表示させる

次に、ライフマークの右側に「× 4」といった形で、残りのライフ数を表示させるようにしてみます。

このライフ数を表示させるためのオブジェクトとして、ヒエラルキーウィンドウからUIを押して「Text」の中にある「TextMeshPro」を選択します。

ゲームプロジェクト内で初めてTextMeshProを使う際は、以下のようなインポート画面が表示されるので、「Import TMP Essentials」を押します。

そして、このオブジェクトをここでは「lifeCount」という名前に変更しておきます。

インスペクターウィンドウから、先ほどのライフマークでも行ったUIの表示位置を決める画面から、同じく左下を選択します。

ライフのカウント数を表示させたい場所に移動させて、インスペクターウィンドウのTextMeshProのNew Textと書かれている箇所に、カウント数として「× 4」と一旦記載しておきます。

その下の部分で、文字の大きさや色などの指定ができるので、設定を変更しておきます。

これで以下のように、右下にライフ数を表示させることができました。

ライフを変数から表示させる処理

ライフのUIが作成できましたが、そのままだと敵からダメージを受けても減りません。

ライフを減らすためには、ゲームマネージャーのライフの変数を使ってUIに表示させる必要があります。

まず「LifeDisplay」というスクリプトを作り、UIのlifeCountにアタッチしておきます。

using TMPro;    // TextMeshProコンポーネントを扱う際に必要
using UnityEngine;

public class LifeDisplay : MonoBehaviour
{
    void Start()
    {
        GetComponent<TextMeshProUGUI>().text = "× " + GameManager.instance.lifeCount.ToString();    // ライフ数を表示させる処理
    }
}

TextMeshProのコンポーネントを扱う際は、最初に1行目のように「using TMPro;」を宣言しておく必要があります。

ライフカウントを表示させる場合は、GetComponentメソッドでTextMeshProUGUIコンポーネントを取得して、その中のtextに代入していきます。

lifeCountの変数は、staticで作成したGameManager上に保管されているので、

GameManager.instance.lifeCount

と書いてあげることで、変数のデータを取得することができます。

ただ、lifeCountはint型の整数の変数なので、ToStringメソッドを使って文字列に変更する必要があります。

ToStringメソッドは、このように数字を文字列に変更する処理を行ってくれるメソッドです。

ライフカウントは、ダメージを受けたら減る処理になるので、Startメソッドでゲームシーンが読み込まれる際に、1度表示させるという処理を行っています。

これでライフカウントを表示させる処理ができたので、次はライフを減らす処理を書いていきます。

ライフを減らす処理

ライフを減らすのは、プレイヤーがダメージを受けた際になるので、PlayerControllerのスクリプトに書いていきます。

using UnityEngine;

public class PlayerController : MonoBehaviour
{

// 一部省略 //

    void OnTriggerEnter2D(Collider2D collision) // 他のオブジェクトとの当たり判定の処理
    {
        if (collision.gameObject.name == "EnemyBullet" ||   // 敵の弾に当たった場合
            collision.gameObject.tag == "Enemy")            // 敵のオブジェクトに触れた場合
        {
            GameManager.instance.lifeCount--;   // ライフを1減らす処理
            this.gameObject.SetActive(false);   // オブジェクトを消去する
        }
    }
}

OnTriggerEnter2Dメソッド内で当たり判定の処理を書いているので、プレイヤーのオブジェクトを非表示にする前の13行目でライフを1減らすようにしています。

最後に、lifeCountはpublic修飾子を付けて宣言しているので、ゲームマネージャーのオブジェクトを選択して、初期のライフ数をインスペクターウィンドウで設定しておきます。

今回は、初期のライフの値を4に設定しています。

これで実際にゲームを実行してみましょう。

敵からダメージを受けたら、ちゃんとライフを減らす処理ができています。

スコアをUIに表示させていく

ライフと同じように、スコアもUIに表示させていきましょう。

スコアを表示させるUIを作る

ヒエラルキーウィンドウからUIを押して、「Text」から「TextMeshPro」を選択して、「score」という名前に変更します。

スコアは、ライフと反対の右下に表示させたいので、Anchor Presetsを右下に設定します。

そして、テキストやフォントの編集をしてスコアのUIを作っていきます。

ここでは、とりあえず「000000」と6桁でスコアを表示させてみました。

スコアのUIを表示させる処理を作る

スコアのUIを表示させるためのスクリプト「ScoreDisplay」を作成して、scoreにアタッチしておきます。

using TMPro;    // TextMeshProコンポーネントを扱う際に必要
using UnityEngine;

public class ScoreDisplay : MonoBehaviour
{
    int preScorePoint = 0;  // 前のスコアを取得する変数

    void Start()
    {
        GameManager.instance.scorePoint = 0;    // スコアをリセットさせたいので0を代入
    }

    void Update()
    {
        if (GameManager.instance.scorePoint - preScorePoint > 0)    // スコアが変動した場合
        {
            preScorePoint = GameManager.instance.scorePoint;    // スコア数を代入する
            GetComponent<TextMeshProUGUI>().text = GameManager.instance.scorePoint.ToString("d6");  // スコア数を表示させる処理
        }
    }
}

ライフと同じく、1行目で「using TMPro;」を書いて、TextMeshProを使う宣言をしておきます。

スコアは、ゲーム開始とともに0から開始するので、Startメソッド内の10行目でscorePointの変数を0にする処理をしています。

スコアが変わるたびにスコア表示を変えたいので、6行目でpreScorePointとして前のスコアを取得する変数を作り、15行目でif文を使って変動した場合のみにスコアの書き換えの処理が行われるようにしています。

書き換えの処理は、18行目でscorePointを文字列にしてTextMeshProのTextに代入しています。

数字を文字列に変更するのはToStringメソッドですが、このToStringの引数で数字の桁を指定することができるので、「d6」としてあげることで6桁表示で文字列として渡すことができます。

これでスコアを表示させる処理ができました。

スコアを加算する処理

次は、スコアを加算していく処理を書いていきます。

スコアは、敵を倒したタイミングで加算させたいので、EnemyBaseに処理を書いていきます。

using UnityEngine;

public class EnemyBase : MonoBehaviour
{
    public int hp; // HPを管理する変数を宣言
    public int enemyPoint;  // 敵を倒した際のポイントを決める変数

    void Update()
    {
        if (this.transform.position.y < -5)
        {
            this.gameObject.SetActive(false);    // 敵オブジェクトを非表示にする
        }
    }

    void OnTriggerEnter2D(Collider2D collision) // 当たり判定のあるオブジェクトと当たった場合
    {
        if (collision.gameObject.name == "PlayerBullet")   // 当たったオブジェクトがプレイヤーの弾の場合
        {
            hp--;   // HPを1減らす処理

            if (hp <= 0)    // HPが0になった場合
            {
                GameManager.instance.scorePoint += enemyPoint;
                this.gameObject.SetActive(false);    // 敵オブジェクトを非表示にする
            }
        }
    }
}

6行目で敵を倒した際のポイントを決める変数として、enemyPointを宣言しています。

敵のHPが0以下になったらスコアが加算されるように、24行目でGameManagerのscorePointの変数に、enemyPointの数値が加算されるように計算する処理を行っています。

ここまでできたら、EnemyのそれぞれのPrefabオブジェクトを選択して、インスペクターウィンドウにenemyPointの数値を入れていきます。

今回は、Enemy01は10、Enemy02は25、Enemy03は40に設定しています。

これで実際にゲームを実行してみましょう。

敵を倒したタイミングでスコアがしっかりと加算されていきます。

まとめ

このページでは、ゲームマネージャーを使ってライフとスコアを管理していく処理を作っていきました。

ゲームマネージャーは、ゲームの大元となる変数などを管理してあげるためのインスタンスです。

このゲームマネージャーをDontDestroyOnLoadメソッドで別のシーンを読み込んでも消さないことで、ライフやスコアのデータを次のシーンに引き継ぐことができます。

データを次のシーンに引き継ぐことができたので、次はリトライの際の新しいシーン作りと、リトライポイントを作っていきます。

最後までお読みいただきまして、ありがとうございました!

コメント