【Unity初心者向け】作りながら覚えるゲーム制作講座 #7 (前編) ゴール作成とスコア加算

今回は、ステージの右端にゴールを作成し、自機が通過するとスコアが加算されるようにしていきます。
そして、スコアが 5 以上になったらゲームクリアとなるようにしたいと思います!

今回は内容が多くなってしまいましたので、前後編に分けさせていただきます。
前半では実際のゴール作成とスコア加算を行い、後半ではゲームクリア表示やスコア表示といった表示部分を実装します。

それでは始めていきましょう!

自機のスタート位置を変更する

現状ではゲームを開始すると自機がステージの中央からスタートします。

今回はゴールを右端に作成しますので、これを左端からのスタートにしたいと思います。
スタート位置を変更するには、 Player.cs スクリプトの x の初期値を変更してください。
ここでは -10.0 からのスタートにしてみます。

public class Player : MonoBehaviour
{
    float x = -10.0f;

これで実行してみてください。
左端からのスタートになりましたね!

ゴールのオブジェクトを作成する

では、ゴールとなるオブジェクトを作成します。

今回も分かりやすく Cube を使って作成しましょう。
いつもどおり Hierarchy の右クリックから Cube を作成してください。
そして、名前を「Goal」にしてください。

位置とサイズを設定

これをステージの右端に良い感じに設置したいと思います。
試しに Position を (10, 0.5, 0) 、Scale を (1, 2. 1) としてください。

これで地面の上にゴールが設置されているような見た目になりましたね!
もし好みの位置やサイズがあれば設定を変えてみてください。

見た目を半透明で光らせるように

もう少しゴールっぽくなるよう、半透明で光っているような見た目にしたいと思います。
見た目を変えるにはマテリアルを使うんでしたね。

マテリアルを作成して適用

Project ウィンドウを右クリックして Material を作成し、名前を「GoalMaterial」としてください。

これを Hierarchy の Goal にドラッグ&ドロップすることで適用されます!

Rendering Mode を Transparent に

まずは Rendering Mode を見てください。
デフォルトでは Opaque(不透明) になっていますね。
これを Transparent(半透明) にしてください。

まだ実際には半透明に見えませんが、内部的には半透明に切り替わっています。

Albedo のアルファ値を 0 に

次に、 Albedo の色を編集モードにして、 A(アルファ値)0 にしてください。
これでオブジェクトが透過されましたね!

Emission を ON に

次に光らせるための設定です。
Emission のチェックボックスを ON にしてください。

これだけではまだ光りませんが、新たに色を設定するところが増えました。

Color を光らせたい色に

Emission の下に現れた Color を編集し、好きな色にしてみてください。
ここでは 水色 ( 0, 128, 255 ) にしてみます。

半透明に光っているような見た目になり、ゴールっぽい見た目になったのではないでしょうか!

自機とゴールの当たり判定を実装する

ここからは、自機がゴールに接触したときの当たり判定を実装していきます。

ポイントとしては、弾との当たり判定の処理と共存させる必要があるという点です。
これには、接触したオブジェクトがゴールなのか弾なのかを判別して処理を分岐させる必要があります。

ゴールオブジェクトが当たり判定で検出できるように

当たり判定には、弾の時と同じく OnTriggerEnter メソッドを使います。

まずは、Player スクリプトの OnTriggerEnter で当たり判定を検出できるように Goal オブジェクトの設定を変更します。
これは弾の時と同じ設定です。

Box Collider の Is Trigger を ON に

Goal オブジェクトの Inspector 内にある BoxCollider を確認してください。
そして、弾と同じように Is Trigger を ON にしてください。

この状態でゴールに接触してみてください。
自機が消滅してしまいましたね!
これは、弾に当たった時と同じ処理が発動しているためです。

なので、当たったものがゴールなのか弾なのかを判別して処理を分岐させる必要があります。

自機のスクリプト内からゴールを参照できるように

接触したオブジェクトがゴールかを判別する前準備として、自機のスクリプト(Player.cs)の中からゴールを参照できるようにする必要があります。

というのも、デフォルトではスクリプトの中からはそのスクリプトがアタッチされたオブジェクト(Player.cs の場合は自機)の情報にしかアクセスできません。

他のオブジェクトの情報にアクセスする方法はいくつかありますが、今回は下記のようにアクセス用の変数を使用する方法を使います。

Player.cs に変数を追加

それにはまず、 Player.cs の変数宣言のところに public GameObject goal; という行を追加してください。

public class Player : MonoBehaviour
{
    float x = -10.0f;           // X座標

    public GameObject goal;     // ゴールオブジェクト

いきなり初めて見る書き方ですが、実はこれは float x; などの変数宣言の発展形です。
つまり、「GameObject」という型の「goal」という名前の変数を宣言してるということです。

「GameObject」型の変数には、今回作ったゴールなどのオブジェクト全般を入れることができます。

その手前の「public」という部分ですが、これはアクセス修飾子と呼ばれるもので、これを書くことで変数が外部からアクセスできるようになります。

今はまだ詳細が分からなくても大丈夫ですが、このように書くことで変数を使って外部のオブジェクトにアクセスできるのだと思っておいてください。

Inspector の追加項目 Goal オブジェクトをドラッグ&ドロップ

自機の Inspector を見てみると、 Player スクリプトの部分に「Goal」という項目が追加されているはずです。

ここに、 Hierarchy 上の Goal オブジェクトをドラッグ&ドロップしてください。
これで、 先ほどの goal 変数の中に Goal オブジェクトが入ることになります。

これで、 Player スクリプトの中から Goal オブジェクトを参照する準備が整いました!

ゴールと接触したらスコアを加算するように

では、スコアを加算する部分を作成していきます!

スコアを数えるための変数を追加

まず、スコアを扱うためにスコア用の変数「int score」を追加してください。
0 点から開始するので、初期値は 0 にしてください。

public class Player : MonoBehaviour
{
    float x = -10.0f;           // X座標
    int score = 0;              // スコア

OnTriggerEnter で接触したオブジェクトがゴールかを判別する

現状では、 OnTriggerEnter の中は弾に当たった時の処理だけになっています。
これをゴールか弾かで処理分岐させるには、先ほど作った goal 変数を使います。

結論から言うと、「if (other.gameObject == goal)」と書くことで「接触したものがゴールかどうか」を判定する if 文が作れます。

ここで使っている other.gameObject には、当たった対象のオブジェクトが入っています。
それと goal 変数を比較演算子==」で同じかどうかをチェックすることで、当たったものがゴールかどうかを判定できます。

そして、 if 文の後に else{} を続けて書くことで、「ゴール “以外" と接触した場合」の処理が作れます。
これを「else 文」とって、 if 文の条件を満たさなかった場合に別の処理をさせたいときに使います!

まず、ここまでをプログラムに書いてみます。

private void OnTriggerEnter(Collider other)
{
    if (other.gameObject == goal)
    {
        // ゴールと接触した場合の処理
    }
    else
    {
        // ゴール "以外" と接触した場合の処理
    }
}

ゴール “以外" と接触した場合」というのは「弾と接触した場合」なので、元々あった処理を入れれば大丈夫ですね。

private void OnTriggerEnter(Collider other)
{
    if (other.gameObject == goal)
    {
        // ゴールと接触した場合の処理
    }
    else
    {
        // ゲームオーバー(自機を消す)
        Destroy(gameObject);
    }
}

そして、「ゴールと接触した場合の処理」は下記の2つになります。

・スコアを加算する
・自機をスタート地点に戻す

では、それぞれについて処理を追加していきます。

スコアを加算する

スコアを加算するということは、「変数 score に 1 を加算する」ということです。
つまり、 score += 1; になります。

これを「ゴールと接触した場合の処理」の部分に入れてみましょう。

private void OnTriggerEnter(Collider other)
{
    if (other.gameObject == goal)
    {
        // スコアを加算
        score += 1;
    }
    else
    {
        // ゲームオーバー(自機を消す)
        Destroy(gameObject);
    }
}

これで内部的にはスコアが加算されているはずですが、まだスコア表示が無いので実際に加算されている様子は見えませんね。

スコアが加算されていることをデバッグ表示で確認する

実際のスコア表示は次回(後編)で行う予定ですが、今回はUnityのデバッグ表示を使ってスコアを表示させてみます。

下記のような行をスコア加算の後に追加してください。
これは、変数の中身を Console ウィンドウに表示させるための処理になります。

private void OnTriggerEnter(Collider other)
{
    if (other.gameObject == goal)
    {
        // スコアを加算
        score += 1;
        Debug.Log($"{score}");
    }
    else
    {
        // ゲームオーバー(自機を消す)
        Destroy(gameObject);
    }
}

この状態でゲームを実行し、 Console ウィンドウを表示させた状態でゴールを通過してみてください。
通るたびに変数の値が増えていっていることが確認できます!

変数の中身を表示するには、このようにUnityのデバッグ表示を使うほか、VisualStudioのブレークポイントという機能を使う方法もあります。
こちらのほうがより発展的なデバッグが可能になりますので、ぜひ楽しみにしておいてください!

自機をスタート地点に戻す

スタート地点に戻すということは、「x を初期値に戻す」ということです。
先ほど、 x の初期値を -10.0 に設定しましたね。
なので、ここでも -10.0 に戻す処理を入れます。

private void OnTriggerEnter(Collider other)
{
    if (other.gameObject == goal)
    {
        // スコアを加算
        score += 1;

        // スタート地点に戻る
        x = -10.0f;
    }
    else
    {
        // ゲームオーバー(自機を消す)
        Destroy(gameObject);
    }
}

では、実際にゲームを実行してゴールを通過してみてください。
自機がスタート地点に戻ることが確認できます!

スコアが 5 以上になったらクリアになるように

最後に、スコアが 5 以上になったらゲームクリアとなるようにしましょう。
ゲームクリア時の処理は、次のようにしたいと思います。

・自機の当たり判定を無効化する
・画面に「QUALIFIED!!」と表示させ

このうち、画面表示の方は次回(後編)で行いますので、今回は「自機の当たり判定を無効化する」をやっていきましょう。

スコアが 5 以上になったかどうかで処理を分岐させる

出ました、ある値と他の値を比較して処理を分岐させるというということは「条件分岐」です。
これは、第5回で行った弾が地面より下に行ったときに上に戻す処理に似ていますね。

そうです、「if 文」と「比較演算子」を使います!

score 変数と 5 という値を比較し、「5 以上だったら」という if 文は次のように書きます。
また、条件を満たさなかった場合の処理も必要になるので、上で説明した else 文も合わせて使用します。

if (score >= 5)
{
    // score が 5 以上だった場合の処理
}
else
{
    // score が 5 以上 "でなかった" 場合の処理(score が 5 未満だった場合の処理)
}

まず、先に else 文の方ですが「スコアが 5 未満」つまりクリアしていない場合なので、今までどおり「スタート地点に戻る」という処理で大丈夫です。

if (score >= 5)
{
}
else
{
    // スタート地点に戻る
    x = -10.0f;
}

そして、問題の if 文の方に「自機の当たり判定を無効化する」という処理を追加することになります。

自機の当たり判定を無効化する

自分自身の当たり判定を設定するには、このように書きます。

GetComponent<Collider>().enabled = /* 設定値(true または false) */;

イコールがあるので変数の代入のように見えますが、左側は見慣れない表記ですね。
ひとまず左側は気にせず、右側の設定値だけを見てください。

ここに「true」を設定すると有効化され、「false」を設定すると無効化されます。
この「true」と「false」は真偽値といって、「ON」と「OFF」のようなものです。

今回は無効化するので false を設定しましょう。

では、実際に5回ゴールを通過してクリアしてみてください。
クリア後は当たり判定が消えて、弾に当たっても大丈夫なはずです!

まとめと次回予告

お疲れさまでした!

今回(前編)ではゲームを完成させる最終段階として、ゴールの作成とスコアの加算を実装しました。

後編では、スコアを表示したりクリアしたときの文字を表示したりといった表示関係を実装して、ついにゲームを完成させたいと思います!