Unity 2Dアクションの作り方【ステージ管理】【元の位置に戻る処理】

今まで様々なものを制作してきました。今回はステージを管理するためのスクリプトを書いていこうと思います。↑の動画でも解説しています。わからない、うまくいかない事があったら質問される前に、一回、動画の方で手順を確認してください

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

<ステージを管理するスクリプトを作ろう>

スタート地点を決めよう

さて、ゲームをスタートするに当たって、スタートする地点を決めたいと思います。

最初からプレイヤーをスタート地点に置けばいいのでは?と思うかもしれませんが、プレイヤーがやられてしまった時にスタート地点に戻ってこないといけないのでスタート地点を決めます。

まずはカラのゲームオブジェクトを作成してください。適当にスタート地点とわかる名前に変えるといいと思います。

自分はStartPosという名前にしました。

そして、ゲームオブジェクトの左上の灰色の箱みたいな奴をクリックしてください。

↓のようなメニューが出てきたと思います。

適当に好きな色の奴を選んでください。できたら上2段の横長の物がいいと思います。

そうすると、カラだったゲームオブジェクトがシーンビュー上で映るようになります。

これは何かというとただの目印で、ゲームとは何の関係もありません。

ゲームとは関係ないただの目印であるという点が重要で、要は編集する上で「場所」を示すのに便利です。

これをスタート地点にしたいところに置きましょう。

ポイントとしては、キャラクターの中心座標分高い位置に置くことです。

このポジションを取得してキャラクターを配置します。

ステージコントローラーを作成しよう

それではステージをコントロールするスクリプトを書きましょう。

クリックすると展開します
 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 
 public class StageCtrl : MonoBehaviour
 {
 
     [Header("プレイヤーゲームオブジェクト")] public GameObject playerObj;
     [Header("コンティニュー位置")] public GameObject[] continuePoint;
 
     void Start()
     {
         if (playerObj != null && continuePoint != null && continuePoint.Length > 0)
         {
             playerObj.transform.position = continuePoint[0].transform.position;
         }
    else
    {
      Debug.Log("設定が足りてないよ!");
    }
     }
 
     void Update()
     {
         
     }
 }

さて、このスクリプトでポイントとなっているのはpublicに配列を持ってきている点です。

[Header("コンティニュー位置")] public GameObject[] continuePoint;

コンティニュー位置としていますが、0番目がスタート地点になります。

if (playerObj != null && continuePoint != null && continuePoint.Length > 0)

これは、インスペクターで設定し忘れた時用の対策です。

配列.Lengthというのは配列の中身が何個用意されているのかを知る事ができます。要は中身が1個も無かったら処理は行わないという事です。

playerObj.transform.position = continuePoint[0].transform.position;

そして、Startでプレイヤーの位置をスタート地点と同じ位置にするようにします。

これをカラのゲームオブジェクトを作って貼り付けると↓のようになります。

配列のところにSizeとなっていますね。ここに数字を入れると

このように中身を入れる箱が出来上がります。Sizeというのは配列の大きさになります。publicにすることによってインスペクターで大きさを指定できるようになったわけです。

これでコンティニューの作り方もだいたいわかったと思いますが、コンティニューポイントを作成するにはサイズを2以上にして、コンティニューポイントを通過した時、プレイヤーが戻ってくる位置を0から1に変えてあげればOKになります。

もちろんSizeの数は自由にできるのでステージによってコンティニューポイントを増やすことも可能になります。

あとはインスペクターにプレイヤーとスタート地点のゲームオブジェクトをはめ込んであげればOKになります。

2以上にしてステージの途中からコンティニューするのはコンティニュー用のアイテムを作成する解説でやるので、今回はスタートのみで考えていきます。

スポンサーリンク

<スタート地点に戻ってこよう>

アニメーション終了判定を得よう

さて、では敵にやられたらスタート地点に戻ってくる処理を作ろうと思います。

今までのプレイヤーのスクリプトは↓のようになっています。

クリックすると展開します
 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 = 0.0f;
     private float jumpTime = 0.0f; 
     private float beforeKey = 0.0f; 
     private string enemyTag = "Enemy";
     #endregion 

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

     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)
          {
              //上方向キーを押しているか
         bool pushUpKey = verticalKey > 0;
        //現在の高さが飛べる高さより下か
        bool canHeight = jumpPos + jumpHeight > transform.position.y;
        //ジャンプ時間が長くなりすぎてないか
        bool canTime = jumpLimitTime > jumpTime;

        if (pushUpKey && canHeight && canTime && !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); 
     } 

     #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
 }

スタート地点に戻る処理を作る前に、やられた時のアニメーションが終わっているかどうかの判定をとりたいと思います。いきなりスタート地点に戻ってしまっては何が起こったかわからなくなってしまいます。

プレイヤーのスクリプトに↓のメソッドを追加します。

クリックすると展開します
/// <summary>
/// コンティニュー待機状態か
/// </summary>
/// <returns></returns>
public bool IsContinueWaiting()
{
  return IsDownAnimEnd();
}

//ダウンアニメーションが完了しているかどうか
private bool IsDownAnimEnd()
{
  if(isDown && anim != null)
  {
    AnimatorStateInfo currentState = anim.GetCurrentAnimatorStateInfo(0);
    if (currentState.IsName("player_down"))
    {
      if(currentState.normalizedTime >= 1)
      {
        return true;
      }
    }
  }
  return false;
}

プレイヤーのアニメーションの情報を拾ってくるには↓のようにする必要があります。

AnimatorStateInfo currentState = anim.GetCurrentAnimatorStateInfo(0);

このAnimatorStateInfoというのが現在再生中のアニメーション情報になります。

if (currentState.IsName("player_down"))

これが、プレイヤーが再生中のアニメーションがplayer_downかどうかを判定しています。

if (currentState.normalizedTime >= 1)

これでアニメーションの終了判定をしています。normalizedTimeというのはアニメーションの全体の再生時間を1とした場合の数値になります。

1で100%再生、0.1で10%再生が終わったみたいな感じです。正規化と言います。

1以上を指定しているのでアニメーションの再生が完全に終わっていることを意味しています。「1」で完全に終了し、ループすると1より上になります。

ここで注意して欲しいのは、この「1」という数字になるのはアニメーションが他に矢印が設定されていない場合に限ります。

このようにplayer_downが他から孤立している為、この判定を取る事ができます。

矢印を設定していても遷移さえしなければ判定を取る事ができますが、ちょっとプログラムとしてわかりづらくなってしまうので注意が必要です。

これでダウンアニメーションが完了しているかどうかの判定を取ることができるようになりました。

そして、publicなメソッドを使い、外から読める用にしています。

/// <summary>
/// コンティニュー待機状態か
/// </summary>
/// <returns></returns>
public bool IsContinueWaiting()
{
  return IsDownAnimEnd();
}

さて、今現在、ダウンアニメーションが完了しているかどうかの判定をそのまま返しているだけの意味のないメソッドになっていますが、後々条件を追加していくので、下準備のため、このようにしています。

やられたらスタート地点に戻ってこよう

今度はステージコントローラーからプレイヤーのスクリプトを見て、ダウンアニメーションが終わっていればスタート地点に戻す処理を入れていきます。

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

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

  private Player p;

  // Start is called before the first frame update
  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("プレイヤーじゃない物がアタッチされているよ!");
      }
    }
    else
    {
      Debug.Log("設定が足りてないよ!");
    }
  }

  // Update is called once per frame
  void Update()
  {
    if(p != null && p.IsContinueWaiting())
    {
      if(continuePoint.Length > GManager.instance.continueNum)
      {
        playerObj.transform.position = continuePoint[GManager.instance.continueNum].transform.position;
      }
      else
      {
        Debug.Log("コンティニューポイントの設定が足りてないよ!");
      }
    }
  }
}

プレイヤーのオブジェクトからプレイヤーのスクリプトを取得します。

p = playerObj.GetComponent<Player>();
if(p == null)
 {
    Debug.Log("プレイヤーじゃない物がアタッチされているよ!");
 }

そして、アップデート内でプレイヤーを監視して、コンティニュー待機状態ならコンティニュー位置に戻すようにします。

現在はコンティニュー待機状態というのはダウンアニメーションが完了しているかどうかという形になっています。

if(p != null && p.IsContinueWaiting())
{
   if(continuePoint.Length > GManager.instance.continueNum)
   {
      playerObj.transform.position = continuePoint[GManager.instance.continueNum].transform.position;
   }
   else
   {
      Debug.Log("コンティニューポイントの設定が足りてないよ!");
   }
}

GManager.instance.continuNumを変える事で、コンティニューする配列の位置を変える事ができます。コンティニュー位置を変更するのはコンティニュー用アイテムを作るときに解説するので今回は0固定です。

0はスタート地点なのでやられたら、スタート地点に戻ってくることができます。

今回はスタート地点にしか戻れませんが、コンティニュー位置を変更する仕組みは作ることができました。

さて、戻ってきたはいいものの、ダウンしぱなっしなので、フラグを解除できるようにしましょう。

プレイヤーに↓のようなメソッドを追加します。

/// <summary>
/// コンティニューする
/// </summary>
public void ContinuePlayer()
{
    isDown = false;
    anim.Play("player_stand");
    isJump = false;
    isOtherJump = false;
    isRun = false;
}

ステージコントローラー側のスクリプトからコンティニューした事をプレイヤーに伝えるようにしましょう。

if(p != null && p.IsContinueWaiting())
{
   if(continuePoint.Length > GManager.instance.continueNum)
   {
      playerObj.transform.position = continuePoint[GManager.instance.continueNum].transform.position;
       p.ContinuePlayer(); //New
   }
   else
   {
      Debug.Log("コンティニューポイントの設定が足りてないよ!");
   }
}

これでちゃんと戻ってくる事ができるようになりました。

<コンティニューした演出を加えよう>

さて、このままではやられたらパッと戻ってきてしまって実感があんまりわかないので、コンティニューしたという演出を加えてみましょう。

演出と言っても難しい事はせず、ただ点滅させたいと思います。

プレイヤーのスクリプトの中に↓の変数を追加します。

private bool isContinue = false; 
private float continueTime = 0.0f;
private float blinkTime = 0.0f; 
private SpriteRenderer sr = null;

Start内で↓のようにインスタンスを捕まえるのを忘れずに

sr = GetComponent<SpriteRenderer>();

そしてUpdate内に↓を追加します。

クリックすると展開します
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;
          }
      }
 }

今までFixedUpdateに処理を書いていましたが、↑は物理演算とは関係ないですし、レンダリングに関する事なのでUpdateに書きます。

フラグがたったら0.1秒ごとにSpriteRendererをオンオフして点滅する処理を書いています。

blinkTimeというのが0.1秒ごとを測るのに使用している変数で、continueTimeというのが全体の時間になります。0.1秒の点滅を1秒間続けたらフラグを下ろして点滅終わりにします。

↓コンティニューした時にフラグをオンにするようにします。

/// <summary>
/// コンティニューする
/// </summary>
public void ContinuePlayer()
{
     isDown = false;
     anim.Play("player_stand");
     isJump = false;
     isOtherJump = false;
     isRun = false;
     isContinue = true;
}

これで再生すると

このようにスタート地点に戻ってきて明滅します。

<まとめ>

今回の変更でプレイヤーのスクリプトは↓のようになりました。

 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 SpriteRenderer sr = null;
     private bool isGround = false; 
     private bool isJump = false; 
     private bool isHead = false;  
     private bool isRun = false; 
     private bool isDown = false; 
     private bool isOtherJump = false; 
     private bool isContinue = false;
     private float jumpPos = 0.0f; 
     private float otherJumpHeight = 0.0f; 
     private float dashTime = 0.0f;
     private float jumpTime = 0.0f; 
     private float beforeKey = 0.0f;
     private float continueTime = 0.0f;
     private float blinkTime = 0.0f;
     private string enemyTag = "Enemy"; 
     #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 = 0.0f;
                  continueTime = 0.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)
           {
                 //上方向キーを押しているか
           bool pushUpKey = verticalKey > 0;
           //現在の高さが飛べる高さより下か
           bool canHeight = jumpPos + jumpHeight > transform.position.y;
           //ジャンプ時間が長くなりすぎてないか
           bool canTime = jumpLimitTime > jumpTime;

           if (pushUpKey && canHeight && canTime && !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 IsContinueWaiting() 
    { 
        return IsDownAnimEnd(); 
    } 

    //ダウンアニメーションが完了しているかどうか 
    private 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; 

     // Start is called before the first frame update
     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("プレイヤーじゃない物がアタッチされているよ!"); 
             } 
         } 
         else 
         { 
             Debug.Log("設定が足りてないよ!"); 
         }
     } 

     // Update is called once per frame 
     void Update() 
     {
         if (p != null && p.IsContinueWaiting()) 
         { 
              if (continuePoint.Length > GManager.instance.continueNum) 
              { 
                  playerObj.transform.position = continuePoint[GManager.instance.continueNum].transform.position; 
                  p.ContinuePlayer(); 
              } 
              else 
              { 
                   Debug.Log("コンティニューポイントの設定が足りてないよ!"); 
              } 
         }
     }
 }

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

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

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