Unity 2Dアクションの作り方【ゲームオーバー】

make 2d action thumbnail

前回はステージを管理するスクリプトを作成しました。今回はゲームオーバーを作成していこうと思います。最後までついて来て頂ければ↑のようなゲームを作る事ができます。

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

<残機の加算・減算をしよう>

continue blink

さて、前回敵にやられたらコンティニューポイントまで戻るところまで実装しました。

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

 public class Player : MonoBehaviour
 {
     #region//インスペクターで設定する
     [Header("移動速度")] public float speed;
     [Header("重力")] public float gravity;
     [Header("ジャンプ速度")] public float jumpSpeed;
     [Header("ジャンプする高さ")] public float jumpHeight;
     [Header("ジャンプする長さ")] public float jumpLimitTime;
     [Header("接地判定")] public GroundCheck ground;
     [Header("天井判定")] public GroundCheck head;
     [Header("ダッシュの速さ表現")] public AnimationCurve dashCurve;
     [Header("ジャンプの速さ表現")] public AnimationCurve jumpCurve;
     [Header("踏みつけ判定の高さの割合(%)")] public float stepOnRate;
     #endregion

     #region//プライベート変数
     private Animator anim = null;
     private Rigidbody2D rb = null;
     private CapsuleCollider2D capcol = null;
     private bool isGround = false;
     private bool isHead = false; 
     private bool isJump = false;
     private bool isRun = false;
     private bool isDown = false;
     private bool isOtherJump = false;
     private float jumpPos = 0.0f;
     private float otherJumpHeight = 0.0f;
     private float dashTime, jumpTime;
     private float beforeKey;
     private string enemyTag = "Enemy";
     private bool isContinue = false;
     private float continueTime, blinkTime;
     private SpriteRenderer sr = null;
     #endregion

     void Start()
     {
          //コンポーネントのインスタンスを捕まえる
          anim = GetComponent<Animator>();
          rb = GetComponent<Rigidbody2D>();
          capcol = GetComponent<CapsuleCollider2D>();
          sr = GetComponent<SpriteRenderer>();
     }

     private void Update()
     {
          if (isContinue)
          {
              //明滅 ついている時に戻る
              if (blinkTime > 0.2f)
              {
                  sr.enabled = true;
                  blinkTime = 0.0f;
              }
              //明滅 消えているとき
              else if (blinkTime > 0.1f)
              {
                  sr.enabled = false;
              }
              //明滅 ついているとき
              else
              {
                  sr.enabled = true;
              }
              //1秒たったら明滅終わり
              if (continueTime > 1.0f)
              {
                  isContinue = false;
                  blinkTime = 0f;
                  continueTime = 0f;
                  sr.enabled = true;
              }
              else
              {
                  blinkTime += Time.deltaTime;
                  continueTime += Time.deltaTime;
              }
          }
     }
     
     void FixedUpdate()
     {
          if (!isDown)
          {
              //接地判定を得る
              isGround = ground.IsGround();
              isHead = head.IsGround(); 

              //各種座標軸の速度を求める
              float xSpeed = GetXSpeed();
              float ySpeed = GetYSpeed();

              //アニメーションを適用
              SetAnimation();

              //移動速度を設定
              rb.velocity = new Vector2(xSpeed, ySpeed);
          }
          else
          {
              rb.velocity = new Vector2(0, -gravity);
          }
     }

     /// <summary>
     /// Y成分で必要な計算をし、速度を返す。
     /// </summary>
     /// <returns>Y軸の速さ</returns>
     private float GetYSpeed()
     {
          float verticalKey = Input.GetAxis("Vertical");
          float ySpeed = -gravity;

           //何かを踏んだ際のジャンプ
           if (isOtherJump)
           {
             //現在の高さが飛べる高さより下か
             bool canHeight = jumpPos + otherJumpHeight > transform.position.y;
                //ジャンプ時間が長くなりすぎてないか
                bool canTime = jumpLimitTime > jumpTime;

                if (canHeight && canTime && !isHead)
                {
                     ySpeed = jumpSpeed;
                     jumpTime += Time.deltaTime;
                }
                else
                {
                     isOtherJump = false;
                     jumpTime = 0.0f;
                }
           }
          //地面にいるとき
          else if (isGround)
          {
              if (verticalKey > 0)
              {
                  ySpeed = jumpSpeed;
                  jumpPos = transform.position.y; //ジャンプした位置を記録する
                  isJump = true;
                  jumpTime = 0.0f;
              }
              else
              {
                  isJump = false;
              }
          }
          //ジャンプ中
          else if (isJump)
          {
              //上ボタンを押されている。かつ、現在の高さがジャンプした位置から自分の決めた位置より下ならジャンプを継続する
              if (verticalKey > 0 && jumpPos + jumpHeight > transform.position.y && jumpTime < jumpLimitTime && !isHead)
              {
                  ySpeed = jumpSpeed;
                  jumpTime += Time.deltaTime;
              }
              else
              {
                  isJump = false;
                  jumpTime = 0.0f;
              }
          }

          if (isJump || isOtherJump)
          {
              ySpeed *= jumpCurve.Evaluate(jumpTime);
          }
          return ySpeed;
     }

     /// <summary>
     /// X成分で必要な計算をし、速度を返す。
     /// </summary>
     /// <returns>X軸の速さ</returns>
     private float GetXSpeed()
     {
          float horizontalKey = Input.GetAxis("Horizontal");
          float xSpeed = 0.0f;
          if (horizontalKey > 0)
          {
              transform.localScale = new Vector3(1, 1, 1);
              isRun = true;
              dashTime += Time.deltaTime;
              xSpeed = speed;
          }
          else if (horizontalKey < 0)
          {
              transform.localScale = new Vector3(-1, 1, 1);
              isRun = true;
              dashTime += Time.deltaTime;
              xSpeed = -speed;
          }
          else
          {
              isRun = false;
              xSpeed = 0.0f;
              dashTime = 0.0f;
          }

          //前回の入力からダッシュの反転を判断して速度を変える
          if (horizontalKey > 0 && beforeKey < 0)
          {
              dashTime = 0.0f;
          }
          else if (horizontalKey < 0 && beforeKey > 0)
          {
              dashTime = 0.0f;
          }

          beforeKey = horizontalKey;
          xSpeed *= dashCurve.Evaluate(dashTime);
          beforeKey = horizontalKey;
          return xSpeed;
     }

     /// <summary>
     /// アニメーションを設定する
     /// </summary>
     private void SetAnimation()
     {
          anim.SetBool("jump", isJump || isOtherJump);
          anim.SetBool("ground", isGround);
          anim.SetBool("run", isRun);
     }

     /// <summary>
     /// ダウンアニメーションが終わっているかどうか
     /// </summary>
     /// <returns>終了しているかどうか</returns> 
     public bool IsDownAnimEnd()
     {
          if (isDown && anim != null)
          {
              AnimatorStateInfo currentState = anim.GetCurrentAnimatorStateInfo(0);
              if (currentState.IsName("player_down"))
              {
                  if (currentState.normalizedTime >= 1)
                  {
                      return true;
                  }
              }
          }
          return false;
     }
     
     /// <summary>
     /// コンティニューする
     /// </summary>
     public void ContinuePlayer()
     {
          isDown = false;
          anim.Play("player_stand");
          isJump = false;
          isOtherJump = false;
          isRun = false;
          isContinue = true;
     }

     #region//接触判定
     private void OnCollisionEnter2D(Collision2D collision)
     {
          if (collision.collider.tag == enemyTag)
          {
              //踏みつけ判定になる高さ
              float stepOnHeight = (capcol.size.y * (stepOnRate / 100f));
     
              //踏みつけ判定のワールド座標
              float judgePos = transform.position.y - (capcol.size.y / 2f) + stepOnHeight;
     
              foreach (ContactPoint2D p in collision.contacts)
              {
                  if (p.point.y < judgePos)
                  {
                      ObjectCollision o = collision.gameObject.GetComponent<ObjectCollision>();
                      if (o != null)
                      {
                          otherJumpHeight = o.boundHeight;    //踏んづけたものから跳ねる高さを取得する
                          o.playerStepOn = true;        //踏んづけたものに対して踏んづけた事を通知する
                          jumpPos = transform.position.y; //ジャンプした位置を記録する 
                          isOtherJump = true;
                          isJump = false;
                          jumpTime = 0.0f;
                      }
                      else
                      {
                          Debug.Log("ObjectCollisionが付いてないよ!");
                      }
                  }
                  else
                  {
                      anim.Play("player_down");
                      isDown = true;
                      break;
                  }
              }
          }
     }
     #endregion
 }

using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;

 public class StageCtrl : MonoBehaviour
 {
     [Header("プレイヤーゲームオブジェクト")] public GameObject playerObj;
     [Header("コンティニュー位置")] public GameObject[] continuePoint;

     private Player p;

     void Start()
     {
          if(playerObj != null && continuePoint != null && continuePoint.Length > 0)
          {
              playerObj.transform.position = continuePoint[0].transform.position;
              p = playerObj.GetComponent<Player>();
              if(p == null)
              {
                  Debug.Log("プレイヤーが設定されていません");
                  Destroy(this);
              }
          }
          else
          {
              Debug.Log("ステージコントローラーの設定が足りていません");
              Destroy(this);
          }
     }

     // Update is called once per frame
     void Update()
     {
          //プレイヤーがダメージを受けた
          if (p.IsDownAnimEnd())
          {
              playerObj.transform.position = continuePoint[GManager.instance.continueNum].transform.position;
              p.ContinuePlayer();
          }
     }
 }

とりあえず、今度は残機を増やしたり減らしたりする処理を実装しましょう。

今まで制作していたゲームマネージャーに残機の加算減算をするメソッドを加えます。

クリックすると展開します
 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 
 public class GManager : MonoBehaviour
 {
     public static GManager instance = null;
     public int score = 0;
     public int stageNum = 1;
     public int continueNum = 0;
     public int heartNum = 3; 
     public bool isGameOver = false;
 
     private void Awake()
     {
         if(instance == null)
         {
             instance = this;
             DontDestroyOnLoad(this.gameObject);
         }
         else
         {
             Destroy(this.gameObject);
         }
     }
     
     /// <summary>
     /// 残機を1つ増やす
     /// </summary>
     public void AddHeartNum()
     {
         if(heartNum < 99)
         {
             ++heartNum;
         }
     }
     
     /// <summary>
     /// 残機を1つ減らす
     /// </summary>
     public void SubHeartNum()
     {
         if(heartNum > 0)
         {
             --heartNum;
         }
         else
         {
             isGameOver = true;
         }
     }
 }

ゲームマネージャーに残機を増やしたり減らしたりするメソッドを追加しました。

単に減らしたり増やしたりすると無限に増えちゃったり、マイナスになってしまったりするのでこのように処理を追加します。

↓プレイヤー側も敵に当たったら残機をマイナスにする処理を入れましょう。

クリックすると展開します
     #region//接触判定
     private void OnCollisionEnter2D(Collision2D collision)
     {
         if (collision.collider.tag == enemyTag)
         {
 
             //踏みつけ判定になる高さ
             float stepOnHeight = (capcol.size.y * (stepOnRate / 100f));

             //踏みつけ判定のワールド座標
            float judgePos = transform.position.y - (capcol.size.y / 2f) + stepOnHeight;
             foreach (ContactPoint2D p in collision.contacts)
             {
                 if (p.point.y < judgePos)
                 {
                     ObjectCollision o = collision.gameObject.GetComponent<ObjectCollision>();
                     if (o != null)
                     {
                         otherJumpHeight = o.boundHeight;    //踏んづけたものから跳ねる高さを取得する
                         o.playerStepOn = true;        //踏んづけたものに対して踏んづけた事を通知する
                         jumpPos = transform.position.y; //ジャンプした位置を記録する 
                         isOtherJump = true;
                         isJump = false;
                         jumpTime = 0.0f;
                     }
                     else
                     {
                         Debug.Log("ObjectCollisionが付いてないよ!");
                     }
                 }
                 else
                 {
                     anim.Play("player_down");
                     isDown = true;
                     GManager.instance.SubHeartNum(); //New!
                     break;
                 }
             }
         }
     }
     #endregion

<ステージにやられ判定をつけよう>

さて、そろそろゲームオーバー画面を実装したいのですが、ゲームオーバーをテストするに当たっていちいち敵にぶつかっていくのはめんどくさいので先に落ちたらやられてしまうようにしましょう。

その前に、ステージコントローラーのプレイヤー位置を移動させる処理をメソッド化します。

クリックすると展開します
using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 public class StageCtrl : MonoBehaviour
 {
     [Header("プレイヤーゲームオブジェクト")] public GameObject playerObj;
     [Header("コンティニュー位置")] public GameObject[] continuePoint;

     private Player p;

     void Start()
     {
          if(playerObj != null && continuePoint != null && continuePoint.Length > 0)
          {
              playerObj.transform.position = continuePoint[0].transform.position;
              p = playerObj.GetComponent<Player>();
              if(p == null)
              {
                  Debug.Log("プレイヤーが設定されていません");
                  Destroy(this);
              }
          }
          else
          {
              Debug.Log("ステージコントローラーの設定が足りていません");
              Destroy(this);    
          }
     }

     // Update is called once per frame
     void Update()
     {
          //プレイヤーがダメージを受けた
          if (p.IsDownAnimEnd())
          {
              PlayerSetContinuePoint();
          }
     }

     /// <summary>
     /// プレイヤーをコンティニューポイントへ移動する
     /// </summary>
     public void PlayerSetContinuePoint()
     {
          playerObj.transform.position = continuePoint[GManager.instance.continueNum].transform.position;
          p.ContinuePlayer();
     }
 }

処理をpublicにしてメソッド化しています。これで外からプレイヤーの位置を元に戻せるようになりました。

そして今度はアイテムを作った時と同じような感じでやられ判定を作っていきます。

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

 public class DeadPoint : MonoBehaviour
 {
     [Header("ステージコントローラー")] public StageCtrl ctrl;

     private string playerTag = "Player";

     private void OnTriggerEnter2D(Collider2D collision)
     {
          if (collision.tag == playerTag)
          {
              if (GManager.instance != null && ctrl != null)
              {
                  GManager.instance.SubHeartNum();
                  if (!GManager.instance.isGameOver)
                  {
                      ctrl.PlayerSetContinuePoint();
                  }
              }
              else
              {
                  Debug.Log("設定が足りません");
              }
          }
     }
 }

このスクリプトは範囲内にプレイヤーが入ってきた時、残機を1マイナスしてコンティニューポイントに戻すスクリプトです。

↓この緑の枠の中に入ったらやられた判定にしようと思います。

dead zone

インスペクターからステージコントローラーを設定するのを忘れずに。そしてBox Collider 2DにIsTringgerにチェックを入れます。

まだ仮の状態なので適当でOKです。この状態で再生すると

dead point return

適当に設置したため、中途半端なところで戻ってますが、いい感じにやられ判定を置く事ができました。

スポンサーリンク

<ゲームオーバー画面を作ろう>

残機がマイナスになってしまったらゲームオーバーにしたいと思います。

まずは真っ黒な画像を用意します。

black

Imageを設置し、WidthとHeightを解像度に合わせて、↑の画像をスプライトにして突っ込みます

screen black out

Colorのアルファ値を下げてあげるとちょっと薄暗い感じにする事ができます。

そして、Textとボタンを置いてゲームオーバーっぽくすればOKです。まぁ、下書きですので適当でOKです。

Textは相変わらずfontSizeに気をつけてください。極力他のテキストと同じ大きさをしようしましょう。

fontSizeの事がよくわからない方は↓の記事を参考にしてみてください。

game over

UGUIを置く順番には注意しましょう。

canvas hierarchy

下にある方が手前に表示されるのでFadeが一番下になるようにします。

次にゲームオーバーなのですが、ゲームオーバーに関連するUIは全部最初においた薄いグレーアウトの子オブジェクトにしてしまうといいと思います。

こうする事で親をSetActiveでオンオフすることによってゲームオーバーを簡単に画面に表示する事ができるようになります。

<ゲームオーバー画面をスクリプトから表示しよう>

ゲームオーバーになってしまったら最初のステージに戻るようにしようと思います。

まずは、ゲームオーバー画面を表示する為にゲームマネージャーに最初から始めるの処理を書きましょう。

ゲームマネージャーに↓の変数を追加します。これはデフォルトでの残機の数です。

public int defaultHeartNum = 3;

最初から始める時の処理を書きます。

/// <summary>
/// 最初から始める時の処理
/// </summary>
public void RetryGame()
{
     isGameOver = false;
     heartNum = defaultHeartNum;
     score = 0; 
     stageNum = 1;
     continueNum = 0; 
}

フラグを下ろしてあげて、残機をデフォルトのものに戻しましょう。スコアも0に戻してあげます。

クリックすると展開します
using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 public class GManager : MonoBehaviour
 {
     public static GManager instance = null;
     public int score = 0;
     public int stageNum = 1;
     public int continueNum = 0;
     public int heartNum = 3;
     public bool isGameOver = false;
     public int defaultHeartNum = 3;

     private void Awake()
     {
          if (instance == null)
          {
              instance = this;
              DontDestroyOnLoad(this.gameObject);
          }
          else
          {
              Destroy(this.gameObject);
          }
     }

     /// <summary>
     /// 残機を1つ増やす
     /// </summary>
     public void AddHeartNum()
     {
          if (heartNum < 99)
          {
              ++heartNum;
          }
     }

     /// <summary>
     /// 残機を1つ減らす
     /// </summary>
     public void SubHeartNum()
     {
          if (heartNum > 0)
          {
              --heartNum;
          }
          else
          {
              isGameOver = true;
          }
     }

     /// <summary>
     /// 最初から始める時の処理
     /// </summary>
     public void RetryGame()
     {    
          isGameOver = false;
          heartNum = defaultHeartNum;
          score = 0;
          stageNum = 1;
          continueNum = 0;
     }
 }

また、ステージコントローラーを以下のように変えます。

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

 public class StageCtrl : MonoBehaviour
 {
     [Header("プレイヤーゲームオブジェクト")] public GameObject playerObj;
     [Header("コンティニュー位置")] public GameObject[] continuePoint;
     [Header("ゲームオーバー")] public GameObject gameOverObj;//New!
     [Header("フェード")] public FadeImage fade;//New!

     private Player p;
     private int nextStageNum; //New!
     private bool startFade = false; //New!
     private bool doGameOver = false; //New!

     void Start()
     {
          if(playerObj != null && continuePoint != null && continuePoint.Length > 0 && gameOverObj != null) //New!
          {
              gameOverObj.SetActive(false); //New!
              playerObj.transform.position = continuePoint[0].transform.position;
              p = playerObj.GetComponent<Player>();
              if(p == null)
              {
                  Debug.Log("プレイヤーが設定されていません");
                  Destroy(this);
              }
          }
          else
          {
              Debug.Log("ステージコントローラーの設定が足りていません");
              Destroy(this);
          }
     }

     // Update is called once per frame
     void Update()
     {
          //ゲームオーバー New
          if (GManager.instance.isGameOver && !doGameOver)
          {
              gameOverObj.SetActive(true);
              doGameOver = true;
          }
          //プレイヤーがダメージを受けた
          else if (p.IsDownAnimEnd())
          {
              PlayerSetContinuePoint();
          }
          //ステージを切り替える
          if (fade != null && startFade)
          {
              if (fade.IsFadeOutComplete())
              {
                  GManager.instance.stageNum = nextStageNum;
                  SceneManager.LoadScene("stage" + nextStageNum);
              }
          }
     }

     /// <summary>
     /// プレイヤーをコンティニューポイントへ移動する
     /// </summary>
     public void PlayerSetContinuePoint()
     {
          playerObj.transform.position = continuePoint[GManager.instance.continueNum].transform.position;
          p.ContinuePlayer();
     }

     /// <summary>
     /// 最初から始める New!
     /// </summary>
     public void Retry()
     {
          GManager.instance.RetryGame();
          ChangeScene(1); //最初のステージに戻るので1
     }

     /// <summary>
     /// ステージを切り替えます。 New!
     /// </summary>
     /// <param name="num">ステージ番号</param>
     public void ChangeScene(int num)
     {
          if (fade != null)
          {
              nextStageNum = num;
              fade.StartFadeOut();
              startFade = true;
          }
     }
 }

ゲームオーバーのオブジェクトをSetActiveでコントロールし、ゲームオーバーになったら出現するようにします。

ゲームオーバーになってしまったら最初のステージに戻りたいですが、クリアーしたら次のステージに進みたいので↓のように番号でステージ移動ができるようにしています。

/// <summary>
/// ステージを切り替えます。
/// </summary>
/// <param name="num">ステージ番号</param>
public void ChangeScene(int num)
{
    if (fade != null)
    {
        nextStageNum = num;
        fade.StartFadeOut();
        startFade = true;
    }
}

ゲームオーバーでRetryボタンを押した時は↓の最初から始める関数を呼んであげればOKです。

/// <summary>
/// 最初から始める
/// </summary>
public void Retry()
{
    GManager.instance.RetryGame();           
    ChangeScene(1);//最初のステージに戻るので1 
}

ステージ1にシーンチェンジしようとしているので結果、最初に戻ることができます。

現在のステージがステージ1だった場合ちょっと無駄な処理のように思えますが、シーンをロードし直すことによって状況が元に戻る為安全です。

ロードし直さないと敵が消えたままになっていたり、アイテムが消えたままになってしまうのでシーンをロードし直すといいと思います。

また、プレイヤーがゲームオーバーになった時動かないように移動を制限します。

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

 public class Player : MonoBehaviour
 {
     #region//インスペクターで設定する
     [Header("移動速度")] public float speed;
     [Header("重力")] public float gravity;
     [Header("ジャンプ速度")] public float jumpSpeed;
     [Header("ジャンプする高さ")] public float jumpHeight;
     [Header("ジャンプする長さ")] public float jumpLimitTime;
     [Header("接地判定")] public GroundCheck ground;
     [Header("天井判定")] public GroundCheck head;
     [Header("ダッシュの速さ表現")] public AnimationCurve dashCurve;
     [Header("ジャンプの速さ表現")] public AnimationCurve jumpCurve;
     [Header("踏みつけ判定の高さの割合(%)")] public float stepOnRate;
     #endregion

     #region//プライベート変数
     private Animator anim = null;
     private Rigidbody2D rb = null;
     private CapsuleCollider2D capcol = null;
     private bool isGround = false;
     private bool isHead = false; 
     private bool isJump = false;
     private bool isRun = false;
     private bool isDown = false;
     private bool isOtherJump = false;
     private float jumpPos = 0.0f;
     private float otherJumpHeight = 0.0f;
     private float dashTime, jumpTime;
     private float beforeKey;
     private string enemyTag = "Enemy";
     private bool isContinue = false;
     private float continueTime, blinkTime;
     private SpriteRenderer sr = null;
     #endregion

     void Start()
     {
          //コンポーネントのインスタンスを捕まえる
          anim = GetComponent<Animator>();
          rb = GetComponent<Rigidbody2D>();
          capcol = GetComponent<CapsuleCollider2D>();
          sr = GetComponent<SpriteRenderer>();
     }

     private void Update()
     {
          if (isContinue)
          {
              //明滅 ついている時に戻る
              if (blinkTime > 0.2f)
              {
                  sr.enabled = true;
                  blinkTime = 0.0f;
              }
              //明滅 消えているとき
              else if (blinkTime > 0.1f)
              {
                  sr.enabled = false;
              }
              //明滅 ついているとき
              else
              {
                  sr.enabled = true;
              }
              //1秒たったら明滅終わり
              if (continueTime > 1.0f)
              {
                  isContinue = false;
                  blinkTime = 0f;
                  continueTime = 0f;
                  sr.enabled = true;
              }
              else
              {
                  blinkTime += Time.deltaTime;
                  continueTime += Time.deltaTime;
              }
          }
     }
     
     void FixedUpdate()
     {
         if (!isDown && !GManager.instance.isGameOver)
          {
              //接地判定を得る
              isGround = ground.IsGround();
              isHead = head.IsGround(); 

              //各種座標軸の速度を求める
              float xSpeed = GetXSpeed();
              float ySpeed = GetYSpeed();

              //アニメーションを適用
              SetAnimation();

              //移動速度を設定
              rb.velocity = new Vector2(xSpeed, ySpeed);
          }
          else
          {
              rb.velocity = new Vector2(0, -gravity);
          }
     }

     /// <summary>
     /// Y成分で必要な計算をし、速度を返す。
     /// </summary>
     /// <returns>Y軸の速さ</returns>
     private float GetYSpeed()
     {
          float verticalKey = Input.GetAxis("Vertical");
          float ySpeed = -gravity;

           //何かを踏んだ際のジャンプ
           if (isOtherJump)
           {
             //現在の高さが飛べる高さより下か
             bool canHeight = jumpPos + otherJumpHeight > transform.position.y;
                //ジャンプ時間が長くなりすぎてないか
                bool canTime = jumpLimitTime > jumpTime;

                if (canHeight && canTime && !isHead)
                {
                     ySpeed = jumpSpeed;
                     jumpTime += Time.deltaTime;
                }
                else
                {
                     isOtherJump = false;
                     jumpTime = 0.0f;
                }
           }
          //地面にいるとき
          else if (isGround)
          {
              if (verticalKey > 0)
              {
                  ySpeed = jumpSpeed;
                  jumpPos = transform.position.y; //ジャンプした位置を記録する
                  isJump = true;
                  jumpTime = 0.0f;
              }
              else
              {
                  isJump = false;
              }
          }
          //ジャンプ中
          else if (isJump)
          {
              //上ボタンを押されている。かつ、現在の高さがジャンプした位置から自分の決めた位置より下ならジャンプを継続する
              if (verticalKey > 0 && jumpPos + jumpHeight > transform.position.y && jumpTime < jumpLimitTime && !isHead)
              {
                  ySpeed = jumpSpeed;
                  jumpTime += Time.deltaTime;
              }
              else
              {
                  isJump = false;
                  jumpTime = 0.0f;
              }
          }

          if (isJump || isOtherJump)
          {
              ySpeed *= jumpCurve.Evaluate(jumpTime);
          }
          return ySpeed;
     }

     /// <summary>
     /// X成分で必要な計算をし、速度を返す。
     /// </summary>
     /// <returns>X軸の速さ</returns>
     private float GetXSpeed()
     {
          float horizontalKey = Input.GetAxis("Horizontal");
          float xSpeed = 0.0f;
          if (horizontalKey > 0)
          {
              transform.localScale = new Vector3(1, 1, 1);
              isRun = true;
              dashTime += Time.deltaTime;
              xSpeed = speed;
          }
          else if (horizontalKey < 0)
          {
              transform.localScale = new Vector3(-1, 1, 1);
              isRun = true;
              dashTime += Time.deltaTime;
              xSpeed = -speed;
          }
          else
          {
              isRun = false;
              xSpeed = 0.0f;
              dashTime = 0.0f;
          }

          //前回の入力からダッシュの反転を判断して速度を変える
          if (horizontalKey > 0 && beforeKey < 0)
          {
              dashTime = 0.0f;
          }
          else if (horizontalKey < 0 && beforeKey > 0)
          {
              dashTime = 0.0f;
          }

          beforeKey = horizontalKey;
          xSpeed *= dashCurve.Evaluate(dashTime);
          beforeKey = horizontalKey;
          return xSpeed;
     }

     /// <summary>
     /// アニメーションを設定する
     /// </summary>
     private void SetAnimation()
     {
          anim.SetBool("jump", isJump || isOtherJump);
          anim.SetBool("ground", isGround);
          anim.SetBool("run", isRun);
     }

     /// <summary>
     /// ダウンアニメーションが終わっているかどうか
     /// </summary>
     /// <returns>終了しているかどうか</returns> 
     public bool IsDownAnimEnd()
     {
          if (isDown && anim != null)
          {
              AnimatorStateInfo currentState = anim.GetCurrentAnimatorStateInfo(0);
              if (currentState.IsName("player_down"))
              {
                  if (currentState.normalizedTime >= 1)
                  {
                      return true;
                  }
              }
          }
          return false;
     }
     
     /// <summary>
     /// コンティニューする
     /// </summary>
     public void ContinuePlayer()
     {
          isDown = false;
          anim.Play("player_stand");
          isJump = false;
          isOtherJump = false;
          isRun = false;
          isContinue = true;
     }

     #region//接触判定
     private void OnCollisionEnter2D(Collision2D collision)
     {
             if (!isDown && !GManager.instance.isGameOver)
           { 
                if (collision.collider.tag == enemyTag)
                {
                    //踏みつけ判定になる高さ
                    float stepOnHeight = (capcol.size.y * (stepOnRate / 100f));
     
                    //踏みつけ判定のワールド座標
                    float judgePos = transform.position.y - (capcol.size.y / 2f) + stepOnHeight;
     
                    foreach (ContactPoint2D p in collision.contacts)
                    {
                        if (p.point.y < judgePos)
                        {
                            ObjectCollision o = collision.gameObject.GetComponent<ObjectCollision>();
                            if (o != null)
                            {
                                otherJumpHeight = o.boundHeight;    //踏んづけたものから跳ねる高さを取得する
                                o.playerStepOn = true;        //踏んづけたものに対して踏んづけた事を通知する
                                    jumpPos = transform.position.y; //ジャンプした位置を記録する 
                                isOtherJump = true;
                                isJump = false;
                                jumpTime = 0.0f;
                            }
                            else
                            {
                                Debug.Log("ObjectCollisionが付いてないよ!");
                            }
                        }
                        else
                        {
                            anim.Play("player_down");
                            isDown = true;
                            break;
                        }
                    }
                }
              }
     }
     #endregion
 }

各種インスペクターでの設定を忘れずに。

stage control inspector game over fade

↓リトライボタンを押したらStageCtrlのRetryのメソッドを呼ぶようにしましょう。

この状態で再生すると

retry game

これでゲームオーバーを作成する事ができました。

<まとめ>

今回実装したスクリプトは↓です。

クリックすると展開します
 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 
 public class GManager : MonoBehaviour
 {
     public static GManager instance = null;
 
     public int score = 0;
     public int stageNum = 1;
     public int continueNum = 0;
     public int heartNum = 3;
     public bool isGameOver = false;
     public int defaultHeartNum = 3;
 
     private void Awake()
     {
         if(instance == null)
         {
             instance = this;
             DontDestroyOnLoad(this.gameObject);
         }
         else
         {
             Destroy(this.gameObject);
         }
     }
     
     /// <summary>
     /// 残機を1つ増やす
     /// </summary>
     public void AddHeartNum()
     {
         if(heartNum < 99)
         {
             ++heartNum;
         }
     }
     
     /// <summary>
     /// 残機を1つ減らす
     /// </summary>
     public void SubHeartNum()
     {
         if(heartNum > 0)
         {
             --heartNum;
         }
         else
         {
             isGameOver = true;
         }
     }

     /// <summary>
     /// 最初から始める時の処理
     /// </summary>
     public void RetryGame()
     {
          isGameOver = false;
          heartNum = defaultHeartNum;
          score = 0;
          stageNum = 1;
          continueNum = 0;
     }
 }

using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 public class Player : MonoBehaviour
 {
     #region//インスペクターで設定する
     [Header("移動速度")] public float speed;
     [Header("重力")] public float gravity;
     [Header("ジャンプ速度")] public float jumpSpeed;
     [Header("ジャンプする高さ")] public float jumpHeight;
     [Header("ジャンプする長さ")] public float jumpLimitTime;
     [Header("接地判定")] public GroundCheck ground;
     [Header("天井判定")] public GroundCheck head;
     [Header("ダッシュの速さ表現")] public AnimationCurve dashCurve;
     [Header("ジャンプの速さ表現")] public AnimationCurve jumpCurve;
     [Header("踏みつけ判定の高さの割合(%)")] public float stepOnRate;
     #endregion

     #region//プライベート変数
     private Animator anim = null;
     private Rigidbody2D rb = null;
     private CapsuleCollider2D capcol = null;
     private bool isGround = false;
   private bool isHead = false; 
     private bool isJump = false;
     private bool isRun = false;
     private bool isDown = false;
     private bool isOtherJump = false;
     private float jumpPos = 0.0f;
     private float otherJumpHeight = 0.0f;
     private float dashTime, jumpTime;
     private float beforeKey;
     private string enemyTag = "Enemy";
     private bool isContinue = false;
     private float continueTime, blinkTime;
     private SpriteRenderer sr = null;
     #endregion

     void Start()
     {
          //コンポーネントのインスタンスを捕まえる
          anim = GetComponent<Animator>();
          rb = GetComponent<Rigidbody2D>();
          capcol = GetComponent<CapsuleCollider2D>();
          sr = GetComponent<SpriteRenderer>();
     }

     private void Update()
     {
          if (isContinue)
          {
              //明滅 ついている時に戻る
              if (blinkTime > 0.2f)
              {
                  sr.enabled = true;
                  blinkTime = 0.0f;
              }
              //明滅 消えているとき
              else if (blinkTime > 0.1f)
              {
                  sr.enabled = false;
              }
              //明滅 ついているとき
              else
              {
                  sr.enabled = true;
              }
              //1秒たったら明滅終わり
              if (continueTime > 1.0f)
              {
                  isContinue = false;
                  blinkTime = 0f;
                  continueTime = 0f;
                  sr.enabled = true;
              }
              else
              {
                  blinkTime += Time.deltaTime;
                  continueTime += Time.deltaTime;
              }
          }
     }
     
     void FixedUpdate()
     {
         if (!isDown && !GManager.instance.isGameOver)
          {
              //接地判定を得る
              isGround = ground.IsGround();
              isHead = head.IsGround(); 

              //各種座標軸の速度を求める
              float xSpeed = GetXSpeed();
              float ySpeed = GetYSpeed();

              //アニメーションを適用
              SetAnimation();

              //移動速度を設定
              rb.velocity = new Vector2(xSpeed, ySpeed);
          }
          else
          {
              rb.velocity = new Vector2(0, -gravity);
          }
     }

     /// <summary>
     /// Y成分で必要な計算をし、速度を返す。
     /// </summary>
     /// <returns>Y軸の速さ</returns>
     private float GetYSpeed()
     {
          float verticalKey = Input.GetAxis("Vertical");
          float ySpeed = -gravity;

           //何かを踏んだ際のジャンプ
           if (isOtherJump)
           {
             //現在の高さが飛べる高さより下か
             bool canHeight = jumpPos + otherJumpHeight > transform.position.y;
                //ジャンプ時間が長くなりすぎてないか
                bool canTime = jumpLimitTime > jumpTime;

                if (canHeight && canTime && !isHead)
                {
                      ySpeed = jumpSpeed;
                      jumpTime += Time.deltaTime;
                }
                else
                {
                      isOtherJump = false;
                      jumpTime = 0.0f;
                }
           }
          //地面にいるとき
          else if (isGround)
          {
              if (verticalKey > 0)
              {
                  ySpeed = jumpSpeed;
                  jumpPos = transform.position.y; //ジャンプした位置を記録する
                  isJump = true;
                  jumpTime = 0.0f;
              }
              else
              {
                  isJump = false;
              }
          }
          //ジャンプ中
          else if (isJump)
          {
              //上ボタンを押されている。かつ、現在の高さがジャンプした位置から自分の決めた位置より下ならジャンプを継続する
              if (verticalKey > 0 && jumpPos + jumpHeight > transform.position.y && jumpTime < jumpLimitTime && !isHead)
              {
                  ySpeed = jumpSpeed;
                  jumpTime += Time.deltaTime;
              }
              else
              {
                  isJump = false;
                  jumpTime = 0.0f;
              }
          }

          if (isJump || isOtherJump)
          {
              ySpeed *= jumpCurve.Evaluate(jumpTime);
          }
          return ySpeed;
     }

     /// <summary>
     /// X成分で必要な計算をし、速度を返す。
     /// </summary>
     /// <returns>X軸の速さ</returns>
     private float GetXSpeed()
     {
          float horizontalKey = Input.GetAxis("Horizontal");
          float xSpeed = 0.0f;
          if (horizontalKey > 0)
          {
              transform.localScale = new Vector3(1, 1, 1);
              isRun = true;
              dashTime += Time.deltaTime;
              xSpeed = speed;
          }
          else if (horizontalKey < 0)
          {
              transform.localScale = new Vector3(-1, 1, 1);
              isRun = true;
              dashTime += Time.deltaTime;
              xSpeed = -speed;
          }
          else
          {
              isRun = false;
              xSpeed = 0.0f;
              dashTime = 0.0f;
          }

          //前回の入力からダッシュの反転を判断して速度を変える
          if (horizontalKey > 0 && beforeKey < 0)
          {
              dashTime = 0.0f;
          }
          else if (horizontalKey < 0 && beforeKey > 0)
          {
              dashTime = 0.0f;
          }

          beforeKey = horizontalKey;
          xSpeed *= dashCurve.Evaluate(dashTime);
          beforeKey = horizontalKey;
          return xSpeed;
     }

     /// <summary>
     /// アニメーションを設定する
     /// </summary>
     private void SetAnimation()
     {
          anim.SetBool("jump", isJump || isOtherJump);
          anim.SetBool("ground", isGround);
          anim.SetBool("run", isRun);
     }

     /// <summary>
     /// ダウンアニメーションが終わっているかどうか
     /// </summary>
     /// <returns>終了しているかどうか</returns> 
     public bool IsDownAnimEnd()
     {
          if (isDown && anim != null)
          {
              AnimatorStateInfo currentState = anim.GetCurrentAnimatorStateInfo(0);
              if (currentState.IsName("player_down"))
              {
                  if (currentState.normalizedTime >= 1)
                  {
                      return true;
                  }
              }
          }
          return false;
     }
     
     /// <summary>
     /// コンティニューする
     /// </summary>
     public void ContinuePlayer()
     {
          isDown = false;
          anim.Play("player_stand");
          isJump = false;
          isOtherJump = false;
          isRun = false;
          isContinue = true;
     }

     #region//接触判定
     private void OnCollisionEnter2D(Collision2D collision)
     {
              if (!isDown && !GManager.instance.isGameOver)
              {  
                if (collision.collider.tag == enemyTag)
                {
                    //踏みつけ判定になる高さ
                    float stepOnHeight = (capcol.size.y * (stepOnRate / 100f));
     
                    //踏みつけ判定のワールド座標
                    float judgePos = transform.position.y - (capcol.size.y / 2f) + stepOnHeight;
     
                    foreach (ContactPoint2D p in collision.contacts)
                    {
                        if (p.point.y < judgePos)
                        {
                            ObjectCollision o = collision.gameObject.GetComponent<ObjectCollision>();
                            if (o != null)
                            {
                                otherJumpHeight = o.boundHeight;    //踏んづけたものから跳ねる高さを取得する
                                o.playerStepOn = true;        //踏んづけたものに対して踏んづけた事を通知する
                                  jumpPos = transform.position.y; //ジャンプした位置を記録する  
                                isOtherJump = true;
                                isJump = false;
                                jumpTime = 0.0f;
                            }
                            else
                            {
                                Debug.Log("ObjectCollisionが付いてないよ!");
                            }
                        }
                        else
                        {
                            anim.Play("player_down");
                            isDown = true;
                             GManager.instance.SubHeartNum();
                            break;
                        }
                    }
                }
              }
     }
     #endregion
 }

using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;

 public class DeadPoint : MonoBehaviour
 {
     [Header("ステージコントローラー")] public StageCtrl ctrl;

     private string playerTag = "Player";

     private void OnTriggerEnter2D(Collider2D collision)
     {
          if (collision.tag == playerTag)
          {
              if (GManager.instance != null && ctrl != null)
              {
                  GManager.instance.SubHeartNum();
                  if (!GManager.instance.isGameOver)
                  {
                      ctrl.PlayerSetContinuePoint();
                  }
              }
              else
              {
                  Debug.Log("設定が足りません");
              }
          }
     }
 }

using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 using UnityEngine.SceneManagement;
 public class StageCtrl : MonoBehaviour
 {
       [Header("プレイヤーゲームオブジェクト")] public GameObject playerObj;
       [Header("コンティニュー位置")] public GameObject[] continuePoint;
       [Header("ゲームオーバー")] public GameObject gameOverObj;
       [Header("フェード")] public FadeImage fade;

       private Player p;
       private int nextStageNum;
       private bool doGameOver = false;
       private bool startFade = false;

       void Start()
       {
            if (playerObj != null && continuePoint != null && continuePoint.Length > 0 && gameOverObj != null)
            {
                gameOverObj.SetActive(false);
                playerObj.transform.position = continuePoint[0].transform.position;
                p = playerObj.GetComponent<Player>();
                if (p == null)
                {
                    Debug.Log("プレイヤーが設定されていません");
                    Destroy(this);
                }
            }
            else
            {
                Debug.Log("ステージコントローラーの設定が足りていません");
                Destroy(this);
            }
       }

       // Update is called once per frame
       void Update()
       {
            //ゲームオーバー
            if (GManager.instance.isGameOver && !doGameOver)
            {
                gameOverObj.SetActive(true);
                doGameOver = true;
            }
            //プレイヤーがダメージを受けた
            else if (p.IsDownAnimEnd() && !doGameOver)
            {
                PlayerSetContinuePoint();
            }
            //ステージを切り替える
            if (fade != null && startFade)
            {
                if (fade.IsFadeOutComplete())
                {
                    GManager.instance.stageNum = nextStageNum;
                    SceneManager.LoadScene("stage" + nextStageNum);
                }
            }
       }

       /// <summary>
       /// プレイヤーをコンティニューポイントへ移動する
       /// </summary>
       public void PlayerSetContinuePoint()
       {
            playerObj.transform.position = continuePoint[GManager.instance.continueNum].transform.position;
            p.ContinuePlayer();
       }
       
       /// <summary>
       /// 最初から始める
       /// </summary>
       public void Retry()
       {
            GManager.instance.RetryGame();
            ChangeScene(1); //最初のステージに戻るので1
       }

       /// <summary>
       /// ステージを切り替えます。
       /// </summary>
       /// <param name="num">ステージ番号</param>
       public void ChangeScene(int num)
       {
            if (fade != null)
            {
                nextStageNum = num;
                fade.StartFadeOut();
                startFade = true;
            }
       }
 }

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

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

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

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



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