【Unity/2D】スイカゲーム風の落ち物ゲームを作ってみる話 #8 スコアの機能を実装する

記事をご覧いただき、誠にありがとうございます。
投稿主の無能です。

前回は、ゲームオーバーの機能を実装しました。

今回はスコアの機能を作りたいと思います。

得点が入るタイミング

スコアが加算されるタイミングを考えなくてはなりません。

同じ果物同士が接触したら進化するので、果物が進化するタイミングでスコアを加算すれば良さそうです。

スコアの表示

まずはスコアが表示できるように、UIを作成します。

UIsに子オブジェクトのPanelを作り、「AlwaysOnDisplay」という名前にします。

UIsに「AlwaysOnDisplay」という子オブジェクトのPanelを作成

そしてAlwaysOnDisplayパネルにアタッチしてあるImageを、右にある三点リーダーのメニュー > Remove Componentで削除します。

右にある三点リーダーのメニュー > Remove Componentで削除

コンポーネントが削除された

次にAlwaysOnDisplayパネルに「UI_Score」という名前の子オブジェクトのPanelを作成します。

AlwaysOnDisplayパネルに「UI_Score」という名前の子オブジェクトのPanelを作成

そしてUI_ScoreパネルのImageのアルファ値を0にして透明にします。

Imageのアルファ値を0にして透明にする

同じくImageのRaycast Targetのチェックを外します。

Raycast Targetのチェックを外す

これでマウスでクリックしても反応しなくなります。

UIのボタン等の操作を行った時にどうやって判定しているかというと、マウスでクリックしたりスマホの画面をタップした時に、その部分からUIオブジェクトに向かって光線がでます。
その光線がぶつかって反射して「UIのボタンが押された」等の反応が出ます。

UIオブジェクト(ボタン等)の判定の仕方

Raycast Targetはその光線の反応の対象であるかどうかのチェックになります。
チェックを外したことで、光線は反応しなくなり、光線が当たっても素通りされます。

UI_Scoreは表示だけでいいので、Raycast Targetのチェックを外し光線の反応の対象外とします。

このようにUIオブジェクトが重なって目的のオブジェクトが反応しない等の場合は、Raycast Targetを確認してみてください。

次にスコアのテキストを作成します。

メニュー > UI > Text - TextMeshProで「Text_Score」を作成し、UI_Scoreの子オブジェクトにします。

メニュー > UI > Text - TextMeshProで「Text_Score」を作成

UI_Scoreの子オブジェクトにする

そして表示するテキストを編集します。

テキストを編集

スコアの表示は左上に配置します。

Text_Scoreを配置

Gameビュー

次にText_Scoreを複製し名前を「Text_ScoreNum」にします。
スコアの数字部分になります。

Text_Scoreを複製し名前を「Text_ScoreNum」にする

適当な数字を入力しておいて、Text_Scoreの下に配置します。

適当な数字を入力しておく

Text_Scoreの下に配置

Gameビュー

ではスコアの表示ができたので、スクリプトを編集します。

スクリプトを編集

新たにスクリプトは準備せず、既存のスクリプトを編集します。

まずVariablesからです。

Variables.cs

using UnityEngine;

public class Variables : MonoBehaviour
{
    #region Var-Number
    public const float zero = 0f;                               // floatの0
    public const float half = 2f;                               // floatの2
    #endregion

    #region Var-String
    public const string tag_Fruits = "Fruits",                  // タグ:果物
                        tag_Bottom = "Bottom",                  // タグ:容器の底              
                        tag_GameManager = "GameManager";        // タグ:ゲームマネージャー
    #endregion
}

ゲームマネージャーに付けるタグ用にGameManagerという定数の文字列を追加しました。

次に果物のFruitsを編集します。

Fruits.cs

using UnityEngine;
using UnityEngine.SocialPlatforms.Impl;

public class Fruits : MonoBehaviour
{
    #region Var-Fruits
    [Header("果物の種類")]
    [SerializeField] type fruitsType;
    #endregion

    #region Var-Evolution
    [Header("進化する果物")]
    [SerializeField] Fruits evolutionFruits;
    #endregion

    #region Var-Score
    [Header("進化時の得点")]
    [SerializeField] score scoreType;
    #endregion

    #region Var-Internal
    public static int number = 0;                                           // 果物の基礎の通し番号
    int fruitsNumber = 0;                                                   // 果物の個別の通し番号
    BoxCollider2D border;                                                   // 容器の底のBoxCollider2D
    GameManager gameManager;                                                // ゲームマネージャー
    enum type                                                               // 果物の種類
    {
        cherry = 1, strawberry, grape, orange, persimmon, apple,
        pear, peach, pineapple, melon, watermelon
    }
    enum score                                                              // 進化時の得点
    {
        cherry = 0, strawberry = 1, grape = 5, orange = 10,
        persimmon = 15, apple = 20, pear = 30, peach = 50,
        pineapple = 100, melon = 150, watermelon = 500
    }
    #endregion

    #region Start
    void Start()
    {
        // 果物に個別の通し番号を付ける
        fruitsNumber = number;
        // 基礎の通し番号を加算
        number++;
        // 容器の底のBoxCollider2Dを取得
        border = GameObject.FindWithTag(Variables.tag_Bottom).GetComponent<BoxCollider2D>();
        // ゲームマネージャーを取得
        gameManager = GameObject.FindWithTag(Variables.tag_GameManager).GetComponent<GameManager>();
    }
    #endregion

    #region Update
    void Update()
    {
        // 容器の外に果物が落下した時の処理
        FallOutContainer();
    }
    #endregion

    #region OnCollisionEnter2D
    // 果物の接触判定
    void OnCollisionEnter2D(Collision2D collision)
    {
        // 接触オブジェクトのタグが果物ではない場合
        if (!collision.gameObject.CompareTag(Variables.tag_Fruits))
        {
            // 処理を中断
            return;
        }
        // 接触オブジェクトと自身のFruitsを取得
        Fruits _collisionFruits = collision.gameObject.GetComponent<Fruits>(),
               _fruits = GetComponent<Fruits>();
        // 接触した果物と自身の果物の種類が同じ場合
        if (_collisionFruits.fruitsType == _fruits.fruitsType)
        {
            // 接触した果物の通し番号より自身の果物の通し番号が大きい場合
            if (_collisionFruits.fruitsNumber < _fruits.fruitsNumber)
            {
                // 進化する果物を生成
                Evolution(collision);
            }
            // 接触した果物を破棄
            Destroy(collision.gameObject);
            // 自身を破棄
            Destroy(gameObject);
        }
    }
    #endregion

    #region Evolution
    // 果物の進化
    void Evolution(Collision2D collision)
    {
      // スコアを加算
        gameManager.AddScore((int)scoreType);
        // 進化する果物がnullの場合
        if (evolutionFruits == null)
        {
            // 処理を中断
            return;
        }
        // 接触した果物と自身の果物の中心を結んだ中心位置を算出
        Vector2 _center = (collision.transform.position + transform.position) / Variables.half;
        // 進化する果物を生成 生成物:進化する果物 位置:算出した中心位置 回転無し
        Instantiate(evolutionFruits, _center, Quaternion.identity);
    }
    #endregion

    #region FallOutContainer
    // 果物が容器の外に落下した時の処理
    void FallOutContainer()
    {
        // 容器の底の高さよりも果物の高さが下になった場合
        if (border.transform.position.y > gameObject.transform.position.y)
        {
            // ゲームマネージャーのゲームオーバーのフラグをtrue
            GameManager.isGameOver = true;
        }
    }
    #endregion
}

では追加したメンバ変数から見ていきます。

メンバ変数

まず進化時の得点となるscoreのscoreTypeになります。
果物の種類と同じで、Inspectorビューで設定するためにこのような書き方をしています。

次に内部処理用に、ゲームマネージャーを取得するためのGameManagerのgameManagerになります。

そして各果物の得点となるenumのscoreになります。
ここで列挙されている定数がそのまま得点になるので、得点は自由に設定してください。

では処理を見ていきます。

Start関数

Start関数では、ゲームマネージャーを取得しています。

Evolution関数

Evolution関数では、gameManagerの現在のスコアに設定した得点を引数で渡しています。
引数で渡す時にはint型にキャスト(型変換)しています。

次にGameManagerを編集します。

GameManager.cs

using TMPro;
using UnityEngine;

public class GameManager : MonoBehaviour
{
    #region Var-GameOverUI
    [Header("ゲームオーバーのUI")]
    [SerializeField] GameObject UI_GameOver;
    #endregion

    #region Var-ScoreUI
    [Header("得点の数字部分のオブジェクト")]
    [SerializeField] TMP_Text UI_ScoreText;
    #endregion

    #region Var-Internal
    public static bool isGameOver = false;                                      // ゲームオーバーのフラグ
    int currentScore = 0;                                                       // 現在のスコア
    #endregion

    #region Start
    void Start()
    {
        // スコアの更新
        ScoreUpdate();
    }
    #endregion

    #region Update
    void Update()
    {
        // ゲームオーバーのフラグがtrueだった場合
        if (isGameOver)
        {
            // ゲームオーバーのUIが非アクティブの場合
            if(!UI_GameOver.activeInHierarchy)
            {
                // ゲームオーバー処理
                GameOver();
            }
        }
    }
    #endregion

    #region GameOver
    // ゲームオーバー
    void GameOver()
    {
        // ゲームオーバーのUIをアクティブ
        UI_GameOver.SetActive(true);
    }
    #endregion

    #region AddScore
    // スコアの加算
    public void AddScore(int score)
    {
        // スコアを加算
        currentScore += score;
        // スコアの更新
        ScoreUpdate();
    }
    #endregion

    #region ScoreUpdate
    // スコアの更新
    void ScoreUpdate()
    {
        // 現在のスコアをUIに反映
        UI_ScoreText.SetText(currentScore.ToString());
    }
    #endregion
}

では追加したメンバ変数から見ていきます。

メンバ変数

追加したのは、スコアの数字部分となるTMP_TextのUI_ScoreTextになります。
TMP_Textを使うには、usingにTMProを追加する必要があります。

次に内部処理用として現在のスコアとなるintのcurrentScoreになります。

では処理を見ていきます。

Start関数

新たに追加したStart関数では、スコアを更新するScoreUpdate関数を呼んでいます。
初回はスコアを現在のスコアである0点に更新しています。

AddScore関数

新たに追加したAddScore関数は、外部のスクリプトからアクセスがあるので、アクセス範囲をpublicにしています。

現在のスコアとなるcurrentScoreに引数で受け取ったintのscoreを足し合わせて、その後にスコアを更新します。

ScoreUpdate関数

新たに追加したScoreUpdate関数では、スコアの数字部分のUI_ScoreTextを現在のスコアに書き換えて更新しています。

ではスクリプトが編集できたので、Unityでの作業をします。

Unityでの作業

GameManagerのInspectorビューに新たに項目が追加されているので、スコアの数字部分のUIオブジェクトであるText_ScoreNumを設定します。

Text_ScoreNumを設定

次にVariantsフォルダの果物毎に得点を設定します。

01_Cherry

02_Strawberry

03_Grape

04_Orange

05_Persimmon

06_Apple

07_Pear

08_Peach

09_Pineapple

10_Melon

11_Watermelon

では設定ができたので動作確認をします。

動作確認

今回の確認事項は
  • 果物が進化する際にスコアが加算されるか
  • スコアのUIの数字部分に現在のスコアが反映され更新されているか
  • ゲーム開始時は0点か、果物の得点はスクリプトに設定した得点か
になります。

では確認しましょう。

スコアは0点

ゲーム開始時のスコアは0点です。

ではVariantsフォルダの果物を落として果物の進化時の点数を確認します。

さくらんぼ:0点

いちご:1点

ぶどう:5点

みかん:10点

柿:15点

りんご:20点

梨:30点

桃:50点

パイナップル:100点

メロン:150点

スイカ:500点

これで果物の進化時の得点が確認できました。

あとは普通にゲームをプレイして、スコアを確認します。

Gameビュー

問題無くスコアが加算・更新されました。

これでスコアの機能が実装できました。

まとめ

今回は
  • スコアのUIを作成した
  • スコアの機能を実装した
という事をやりました。

次は、次に登場する果物を表示したいと思います。

では、また次回!

コメント