Unity 2Dアクションを作ろう【スコア・残機・アイテム作成】

スコア・残機・アイテム作成【Unity2Dアクションの作り方】【初心者入門講座】【ゲームの作り方】#49

さて、前回ゲームマネージャーを作成しました。ゲームマネージャーを作成したことにより、多くのパラメーターを全体で管理できるようになりました。今回はゲーム全体で使用するパラメーターを実装していきましょう。わからない、うまくいかない事があったら質問される前に、一回、動画の方で手順を確認してください

この記事は本のように順を追って解説しています。この記事は途中のページになります。
この記事を見ていて、現在の状況がわからない場合や忘れてしまった事などが出てきたら↓のリンクから目次ページへ飛べますので立ち戻って見てください。

<画面に表示するUIを作成しよう>

ゲームマネージャーの作成がまだの人は↓の記事を参考にしてください。

ゲームマネージャーを使用して、画面に情報を表示させましょう。

今回作ろうと思う情報は

・スコア表示
・残機表示
・現在のステージ表示

この3つを実装していきます。

UGUIを使って実装していこうと思います。

UGUIの使い方を忘れてしまった人、わからない人は↓の記事を参考にしてみてください。

また、Canvasの設定を忘れてしまった方は↓の記事を参考にしてみてください。

<スコア表示>

UGUIで画面にスコアを表示しよう

UGUIのTextを使用してスコアを画面に表示していきます。ステージのシーンに移動してください。

可能な限りTextのfontSizeは同じにした方がいいので、タイトルで作成したTextのfontSizeと同じ値を使いましょう。

なぜfontSizeを同じにした方がいいのかという解説は↓の記事で行っています。

fontSizeを設定し、Alignmentを右詰、上詰にして、Horizontal OverflowとVertical OverflowをOverflowに変更して、Colorを黄色にし、Outlineのコンポーネントを追加しました。

右詰にする事でスコアがいくつになっても定位置になるようにしています。

set score text

はい、これでスコアの表示を作る事ができました。ここに正しい値を入れるようにスクリプトを作っていきましょう。

UGUIにスクリプトから値を渡そう

クリックすると展開します
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Score : MonoBehaviour
{
   private Text scoreText = null;
   private int oldScore = 0;

  // Start is called before the first frame update
  void Start()
  {
      scoreText = GetComponent<Text>();
      if(GManager.instance != null)
      {
             scoreText.text = "Score " + GManager.instance.score;
      }
      else
      {
            Debug.Log("ゲームマネージャー置き忘れてるよ!");
            Destroy(this);
      }
  }

   // Update is called once per frame
   void Update()
   {
       if(oldScore != GManager.instance.score)
       {
            scoreText.text = "Score " + GManager.instance.score;
            oldScore = GManager.instance.score;
       }
   }
}   

はい、これで完成です。ゲームマネージャーを作っていたおかげで非常に簡単に作る事ができました。

UGUIを使うので

 using UnityEngine.UI;

と宣言するのを忘れずに。

Updateで毎フレームTextの中身を変更するのはよろしくないので、

if(oldScore != GManager.instance.score)
{
    scoreText.text = "Score " + GManager.instance.score;
    oldScore = GManager.instance.score;
}

oldScoreという前のスコアを記録しておいて、現在のスコアと違った場合のみテキストに書き込みに行くようにします。

このように差分があった時のみ更新するようになっている為、Start内で普通に値を入れて初期化しています。

このスクリプトをUGUIのTextと同じゲームオブジェクトにくっつけましょう。

ゲームマネージャーをうまく使おう

さて、ここで問題があります。ゲームマネージャーがないとスコアが反映されません。しかしながらゲームマネージャーはタイトルシーンに置いてあります。

ゲームのテストプレイを行うのにいちいちタイトル画面に戻るのはめんどくさいです。

ゲームマネージャーをプレハブにしてしまってステージシーンに置いてしまいましょう。

prefab game manager

こんな事をしてしまうと各シーンにゲームマネージャーが置かれてしまって「ただ一つの」という条件を破ってしまうように思えますが、2つ目が登場した際にDestroyするようにしているので問題ないです。

まぁ、処理的に無駄なものが発生してしまいますが、これくらいなら開発しやすさを取ってもいいかなと思います。

スコアを加算しよう

敵が踏まれてやられたらスコアを加算するようにしましょう。

ゲームマネージャーのところで紹介したように敵が踏まれた判定のところでスコアを加算してあげればOKです。

クリックすると展開します
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Enemy_Zako1 : MonoBehaviour
{
    #region//インスペクターで設定する
    [Header("加算スコア")] public int myScore; //New!
    [Header("移動速度")] public float speed;
    [Header("重力")] public float gravity;
    [Header("画面外でも行動する")] public bool nonVisibleAct;
    [Header("接触判定")] public EnemyCollisionCheck checkCollision;
    #endregion

    #region//プライベート変数
    private Rigidbody2D rb = null;
    private SpriteRenderer sr = null;
    private Animator anim = null;
    private ObjectCollision oc = null;
    private BoxCollider2D col = null;
    private bool rightTleftF = false;
    private bool isDead = false;
    #endregion

    // Start is called before the first frame update
    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
        sr = GetComponent<SpriteRenderer>();
        anim = GetComponent<Animator>();
        oc = GetComponent<ObjectCollision>();
        col = GetComponent<BoxCollider2D>();
    }

    void FixedUpdate()
    {
        if (!oc.playerStepOn)
        {
            if (sr.isVisible || nonVisibleAct)
            {
                if (checkCollision.isOn)
                {
                    rightTleftF = !rightTleftF;
                }

                int xVector = -1;
                if (rightTleftF)
                {
                    xVector = 1;

                    transform.localScale = new Vector3(-1, 1, 1);
                }
                else
                {

                    transform.localScale = new Vector3(1, 1, 1);

                }
                rb.velocity = new Vector2(xVector * speed, -gravity);
            }
            else
            {
                rb.Sleep();
            }
        }
        else
        {
            if (!isDead)
            {
                //New!
                if (GManager.instance != null)
                {

                    GManager.instance.score += myScore;
                }

                anim.Play("dead");
                rb.velocity = new Vector2(0, -gravity);
                isDead = true;
                col.enabled = false;
                Destroy(gameObject, 3f);
            }
            else
            {
                transform.Rotate(new Vector3(0, 0, 5));
            }
        }
    }
}

インスペクターで加算するスコアを設定できるようにしています。

スコアを加算するアイテムを作ろう

ついでにマリオでいうコインのようなアイテムを作りましょう。

star

例によって下書きなので適当でOKです。

次にプレイヤーがアイテムに触れたかどうかの判定を取ります。アイテムのスクリプトでひとまとめにしてもいいのですが、後々「プレイヤーが触れたかどうか」という機能はいろいろなところで使いそうなので、別々に分離して汎用プログラムを作っていきましょう。

まず、下準備として、アイテムにBox Collider 2Dをアタッチし、Is Triggerにチェックを入れます。

set score item

このBox Collider2Dの中にプレイヤーが入ったらアイテムに触れたと判定します。

では判定内に入ったらフラグをオンにするスクリプトを書いていきましょう。Is Triggerの判定の取り方は以前に解説した時と同じです。

クリックすると展開します
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerTriggerCheck : MonoBehaviour
{
   // 判定内にプレイヤーがいる
   [HideInInspector] public bool isOn = false;
   private string playerTag = "Player";

   #region//接触判定
    private void OnTriggerEnter2D(Collider2D collision)
   {
       if (collision.tag == playerTag)
       {
          isOn = true;
       }
    }

    private void OnTriggerExit2D(Collider2D collision)
    {
        if (collision.tag == playerTag)
        {
           isOn = false;
        }
    }
    #endregion
}

これでプレイヤーが判定内に入ったフラグを受け取れるので、今度はアイテム側でスコアを加算して消える処理を書いていきます

クリックすると展開します
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ScoreItem : MonoBehaviour
{
   [Header("加算するスコア")] public int myScore;
   [Header("プレイヤーの判定")] public PlayerTriggerCheck playerCheck;

    // Update is called once per frame
   void Update()
    {
      //プレイヤーが判定内に入ったら
      if (playerCheck.isOn)
      {
        if(GManager.instance != null)
        {
          GManager.instance.score += myScore;
          Destroy(this.gameObject);
        }
       }
    }
}

これらのスクリプトをアイテムに張り付けて設定しましょう

item inspector

また、プレイヤーのタグを変更するのを忘れずに

player tag

アイテムのスコアを10、敵のスコアを20に設定しました。この状態で再生すると

score-add-action

こんな感じでスコアが加算されている事がわかります。

スポンサーリンク

<残機表示・ステージ数表示>

さて、ここまでくれば残機表示も、現在のステージを表示するのも簡単だと思います。

heart

また適当に残機アイコンを描いてみました。相変わらず下書きです

同じようにテキストを駆使すると

game ugui set text

割とゲームっぽくなってきましたね。

テキストの配置ポイントとして、Scoreは右詰でしたが、ステージ表記と残機数は左詰にしましょう。

スコアの時と同じようにゲームマネージャーを見て各種値をセットするようにしましょう。

クリックすると展開します
 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 
 public class GManager : MonoBehaviour
 {
     public static GManager instance = null;
 
     public int score;
     public int stageNum;
     public int continueNum;
     public int heartNum;
 
     private void Awake()
     {
         if(instance == null)
         {
             instance = this;
             DontDestroyOnLoad(this.gameObject);
         }
         else
         {
             Destroy(this.gameObject);
         }
     }
 }
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class StageNum : MonoBehaviour
{
   private Text stageText = null;
   private int oldStageNum = 0;

   // Start is called before the first frame update
   void Start()
   {
      stageText = GetComponent<Text>();
      if (GManager.instance != null)
      {
         stageText.text = "Stage " + GManager.instance.stageNum;
      }
      else
      {
         Debug.Log("ゲームマネージャー置き忘れてるよ!");
         Destroy(this);
      }
   }

   // Update is called once per frame
   void Update()
   {
      if (oldStageNum != GManager.instance.stageNum)
      {
         stageText.text = "Stage " + GManager.instance.stageNum;
         oldStageNum = GManager.instance.stageNum;
      }
   }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Heart : MonoBehaviour
{
   private Text heartText = null;
   private int oldHeartNum = 0;
   
   // Start is called before the first frame update
   void Start()
   {
      heartText = GetComponent<Text>();
      if (GManager.instance != null)
      {
         heartText.text = "× " + GManager.instance.heartNum;
      }
      else
      {
         Debug.Log("ゲームマネージャー置き忘れてるよ!");
         Destroy(this); }
      }

   // Update is called once per frame
   void Update()
   {
      if (oldHeartNum != GManager.instance.heartNum)
      {
         heartText.text = "× " + GManager.instance.heartNum;
         oldHeartNum = GManager.instance.heartNum;
      }
   }
}

スクリプトが書けたらゲームマネージャーのインスペクターを設定しましょう

game manager inspector

はい。ものすごく簡単にステージ数と残機数の表示が完了しました。

game test play

もし、0になってしまう方がいたらインスペクターの値を疑ってください。

ここをインスペクターから触れるようにしておく事で、デバッグ中に残機無限にしたり、コンティニュー場所を最初から指定できたりできるようになるのでこのように外に出しておくと便利です。

何かうまくいかない事があった場合は↓の記事を参考にしてみてください

最低限↓の動画の要件を満たしていない質問は受けかねるので、ご理解ください。

デバッグのやり方【Unity初心者入門講座】【ゲームの作り方】#31

また、筆者も間違えることはありますので、何か間違っている点などありましたら、動画コメント欄にでも書いていただけるとありがたいです。


タイトルとURLをコピーしました