Unity 2Dアクションの作り方【コンティニュー・ステージクリア・メッセージ表示】

make 2d action thumbnail

前回は動く床・落ちる床を実装しました。今回はコンティニューやステージクリア、メッセージ表示といったゲーム中のイベントを作成していこうと思います。最後までついて来て頂ければ↑のようなゲームを作る事ができます。

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

<プレイヤーが範囲内に入っているかどうかを判定する>

さて、様々なイベントを作る前に、特定のポイントにプレイヤーが来たらイベントを起こすようにしたいので、プレイヤーが範囲内にいるかどうか判定するスクリプトが必要です。

が、前回作成したプレイヤーが上に乗っているかを判定するスクリプトを流用できそうです。

クリックすると展開します
using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 public class PlayerTriggerOn : MonoBehaviour
 {
     [Header("プレイヤーの下から何%が範囲内に入ってればOKとみなすか")] public float playerOnRate;

       private string playerTag = "Player";
       private BoxCollider2D col;
       private bool isOn = false;
       private bool callFixed = false;
       private bool isEnter, isStay, isExit;

       private void Start()
       {
            col = GetComponent<BoxCollider2D>();
            if (col == null)
            {
                Debug.Log("ボックスコライダーがついていません");
            }
       }

       /// <summary> 
       /// プレイヤーが上に乗っているかどうか 
       /// </summary> 
       public bool IsPlayerOn()
       {
            if(isEnter || isStay)
            {
                isOn = true;
            }
            else if(isExit)
            {
                isOn = false;
            }
            return isOn;
       }

       private void FixedUpdate()
       {
            callFixed = true;
       }

       private void LateUpdate()
       {
            if (callFixed)
            {
                //フラグを元に戻します
                isEnter = false;
                isStay = false;
                isExit = false;
                callFixed = false;
            }
       }

       private void OnTriggerEnter2D(Collider2D collision)
       {
            if (collision.tag == playerTag)
            {
                isEnter = JudgePlayerOn(collision);
            }
       }

       private void OnTriggerStay2D(Collider2D collision)
       {
            if (collision.tag == playerTag)
            {
                isStay = JudgePlayerOn(collision);
            }
       }

       private void OnTriggerExit2D(Collider2D collision)
       {
            if (collision.tag == playerTag)
            {
                isExit = true;
            }
       }

       private bool JudgePlayerOn(Collider2D collision)
       {
            //踏みつけ判定になる高さ
            float stepOnHeight = (collision.bounds.size.y * (playerOnRate / 100f));
            //踏みつけ判定のワールド座標
            float judgePos = collision.transform.position.y - (collision.bounds.size.y / 2f) + stepOnHeight;
       
            //プレイヤーの下から指定した%分上の範囲内にいる時乗っているとみなします
            if (judgePos > transform.position.y - (col.size.y / 2))
            {
                return true;
            }
            return false;
       }
 }

カラのゲームオブジェクトに↑をくっつけ、100%を入れてあげればプレイヤーのどの部分がトリガーの中に入っていようがプレイヤーを検知する事ができます。

そして、Box Collider 2DをくっつけIs Triggerにチェックを入れて、このスクリプトをくっつけたらプレイヤーが範囲内にいるかどうかを判定できるようになります。

player trigger in

緑の枠の中にプレイヤーが入ったら判定がオンになります。

いろんなものに使えるのでプレハブにしてしまいましょう。

trigger prefab

このプレハブからPrefab Variantsで派生したり、Nested Prefabにすれば色々作れそうです。

Nested PrefabとPrefab Variantsが何かわからない人は↓の記事で解説しています。

<メッセージを表示する>

プレイヤーが特定の場所に近づいたらメッセージを表示する事で様々な事ができます。アクションゲームにストーリー性を持たせることもできますし、最初に操作方法を表示するなどチュートリアルも作成する事ができます。

単純にメッセージを表示するだけならプレイヤーが範囲内に入ったらSetActive(true)にしてあげればいいだけですが、パッと出てパッと消えるとちょっと無機質なので演出を加えます。

まずCanvasを作成しましょう。

create ugui canvas

Render ModeをWorld SpaceにしてEvent CameraにMain Cameraをアタッチします。

canvas world space

キャンバス配下にPanelとTextを作成します。

create massage plate

デザイン的なのは後に置いておいて、↓のような感じにします。

massage plate

そして、PanelにCanvas Groupというコンポーネントをつけます。

panel inspector

このコンポーネントはそのUGUIとその子オブジェクト全てのUGUIのアルファ値をコントロールする事ができます。

そして、↑で作成したプレハブを追加し、プレイヤーが範囲内に入った事がわかるようにします。

Canvas配下に起きましょう。

set trigger

次に↓のスクリプトをCanvasにくっつけます。

クリックすると展開します
 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 
 public class FadeActiveUGUI : MonoBehaviour
 {
     [Header("フェードスピード")] public float speed = 1.0f;
     [Header("上昇量")] public float moveDis = 10.0f;
     [Header("上昇時間")] public float moveTime = 1.0f;
     [Header("キャンバスグループ")] public CanvasGroup cg;
     [Header("プレイヤー判定")] public PlayerTriggerOn trigger;
 
     private Vector3 defaltPos;
     private float timer = 0.0f;
 
     private void Start()
     {
         //初期化
         if (cg == null && trigger == null)
         {
             Debug.Log("インスペクターの設定が足りません");
             Destroy(this);
         }
         else
         {
             cg.alpha = 0.0f;
             defaltPos = cg.transform.position;
             cg.transform.position = defaltPos - Vector3.up * moveDis;
         }
     }
 
     private void Update()
     {
         //プレイヤーが範囲内に入った
         if (trigger.IsPlayerOn())
         {
             //上昇しながらフェードインする
             if(cg.transform.position.y < defaltPos.y || cg.alpha < 1.0f) 
             {
                 cg.alpha = timer / moveTime;
                 cg.transform.position += Vector3.up * (moveDis/ moveTime) * speed * Time.deltaTime ;
                 timer += speed * Time.deltaTime;
             }
             //フェードイン完了
             else
             {
                 cg.alpha = 1.0f;
                 cg.transform.position = defaltPos;
             }
         }
         //プレイヤーが範囲内にいない
         else
         {
             //下降しながらフェードアウトする
             if (cg.transform.position.y > defaltPos.y - moveDis || cg.alpha > 0.0f) 
             {
                 cg.alpha = timer / moveTime;
                 cg.transform.position -= Vector3.up * (moveDis / moveTime) * speed * Time.deltaTime;
                 timer -= speed * Time.deltaTime;
             }
             //フェードアウト完了
             else
             {
                 timer = 0.0f;
                 cg.alpha = 0.0f;
                 cg.transform.position = defaltPos - Vector3.up * moveDis;
             }
         }
     }
 }

スクリプトの中身はコメントを読んでいただければわかるかなと思います。

インスペクターの値を設定します。上昇量というのはちょっと上に上がりながらフェードしてほしかったので入れています。上に上がる必要がなければ0を入れればOKです。

それと、このスクリプトを使う上で、注意が必要なところがあります。

canvas-renderer-cull-transparent-mesh

PanelとTextのCanvas RendererのCull Transparent Meshにチェックを入れましょう。

透明なオブジェクトというのは存在するだけで重くなってしまうので、このように対策する必要があります。フェードアウトしている時は消えていただきましょう。

fade ugui

はい。こんな感じでフェードしながらメッセージを表示する事ができました。

<コンティニューポイント>

続いてコンティニューポイントを作って行きます。

まぁ、メッセージの表示ができたならほぼ一緒です。

continue point

まぁ、下書きなのでデザインは適当にして、↑のような感じで作りました。

そして、以前に作成したゲームマネージャーと、ステージコントローラーを使用してスクリプトを書きます。

作っていない方は↓の記事を参考にしてみてください。

クリックすると展開します
 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 
 public class ContinuePoint : MonoBehaviour
 {
     [Header("コンティニュー番号")] public int continueNum;
     [Header("音")] public AudioClip se;
     [Header("プレイヤー判定")] public PlayerTriggerOn trigger;
     [Header("スピード")] public float speed = 3.0f;
     [Header("動く幅")] public float moveDis = 3.0f;
 
     private bool on = false;
     private float kakudo = 0.0f;
     private Vector3 defaultPos;
     void Start()
     {
         //初期化
         if (trigger == null)
         {
             Debug.Log("インスペクターの設定が足りません");
             Destroy(this);
         }
         defaultPos = transform.position;
     }
 
     // Update is called once per frame
     void Update()
     {
         //プレイヤーが範囲内に入った
         if (trigger.IsPlayerOn() && !on)
         {
             GManager.instance.continueNum = continueNum;
             GManager.instance.PlaySE(se);
             on = true;
         }
 
         if (on)
         {
             if (kakudo < 180.0f)
             {
                 //sinカーブで振動させる
                 transform.position = defaultPos + Vector3.up * moveDis * Mathf.Sin(kakudo * Mathf.Deg2Rad);
                 
                 //途中からちっちゃくなる
                 if(kakudo > 90.0f)
                 {
                     transform.localScale = Vector3.one * (1 - ((kakudo - 90.0f) / 90.0f));
                 }
                 kakudo += 180.0f * Time.deltaTime * speed;
             }
             else
             {
                 gameObject.SetActive(false);
                 on = false;
             }
         }
     }
 }

プレイヤーが範囲内に入ったら音を鳴らして、コンティニュー位置をゲームマネージャーに報告します。

         //プレイヤーが範囲内に入った
         if (trigger.IsPlayerOn() && !on)
         {
             GManager.instance.continueNum = continueNum;
             GManager.instance.PlaySE(se);
             on = true;
         }

ステージコントローラーのインスペクターに追加してあげればコンティニューできるようになっています。

stage ctrl

Element1に入っているのでインスペクターで自分のコンティニュー番号を1にしてあげればコンティニューポイントの位置からスタートします。

continue point inspector

コンティニューポイントに接触した場合の演出をちょっと凝ってみました。

             if (kakudo < 180.0f)
             {
                 //sinカーブで振動させる
                 transform.position = defaultPos + Vector3.up * moveDis * Mathf.Sin(kakudo * Mathf.Deg2Rad);
                 
                 //途中からちっちゃくなる
                 if(kakudo > 90.0f)
                 {
                     transform.localScale = Vector3.one * (1 - ((kakudo - 90.0f) / 90.0f));
                 }
                 kakudo += 180.0f * Time.deltaTime * speed;

サインカーブを用いて上下させています。変数名がkakudoなのでわかりやすいと思いますが、Mathf.Sinの中にはラジアンを入れてあげなければいけません。

Mathf.Sin(kakudo * Mathf.Deg2Rad);

↑で角度をラジアンに直しています。角度にMathf.Deg2Radというものを掛ければOKです。

まぁ、わからなくても別に困ることではないので、わからなかったらなんかこんな方法もあるんだ程度に理解していただければと思います。

↓のような感じになります。

continue point

<ステージクリアー>

次はステージクリアーを実装していきましょう。

コンティニューができれば正直新しく覚えることは特にありません。

clear stage text

UGUIのテキストで適当にクリアーを作りました。なんども言いますが、下書きなので適当でOKです。

クリックすると展開します
using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 public class ClearEffect : MonoBehaviour
 {
     [Header("拡大縮小のアニメーションカーブ")] public AnimationCurve curve;
     [Header("ステージコントローラー")] public StageCtrl ctrl;
     private bool comp = false;     
     private float timer;
     private void Start()
     {
          transform.localScale = Vector3.zero;
     }

     private void Update()
     {
          if (!comp)
          {
              if (timer < 1.0f)
              {
                  transform.localScale = Vector3.one * curve.Evaluate(timer);             
                  timer += Time.deltaTime;
              }
            else
              {
                  transform.localScale = Vector3.one;
                  ctrl.ChangeScene(GManager.instance.stageNum + 1);
                  comp = true;
              }
         }
     }
 }

今度はアニメーションカーブを使用してみたいと思います。コンティニューの演出は上昇と縮小のタイミングを同期させたかったので計算でやりましたが、動きが単一ならアニメーションカーブを使えば簡単に実装できます。

animation curve clear

インスペクターでアニメーションカーブを設定しましょう。ポイントとしては、最初0から始まって、最高点が1.5になり、最後1になっている点です。

要は、ステージクリアーが最初大きさ0で始まって1.5倍の大きさまで大きくなった後、1倍の大きさまで戻るというアニメーションになります。

こういったアニメーションは普通にAnimationでやればいいのでは?と思う方もいらっしゃるかと思いますが、わざわざAnimatorをくっつけてAnimationで制御すると後々わかりづらくなってしまうのでスクリプトでします。

Animationは階層の変更ができなくなったり、制御を追いづらくなったりするので複雑な動きはAnimationで、簡単な動きはスクリプトでと分けた方がいいかなと筆者は思います。

物を動かそうとしたのにAnimationに掴まれているせいで動かせないということはよくあります。特にチーム開発で。誰かにAnimation制御されてしまうとそれはもう触れません。

後はゲームマネージャーとステージコントローラー、プレイヤーにステージクリアーした時の処理を追加します。

using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 using UnityEngine.SceneManagement;
 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 int defaultHeartNum = 3;
     public bool isGameOver = false;
     public bool isStageClear = false;

    private AudioSource audioSource = null;

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

     private void Start()
     {
          audioSource = GetComponent<AudioSource>();
     }

     /// <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;
     }

     /// <summary>
     /// SEを鳴らす
     /// </summary>
     public void PlaySE(AudioClip clip)
     {
          if (audioSource != null)
          {
              audioSource.PlayOneShot(clip);
          }
          else
          {
              Debug.Log("オーディオソースが設定されていません");
          }
     }
 }
using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 using UnityEngine.SceneManagement;
 public class StageCtrl : MonoBehaviour
 {
       [Header("クリアした時次のシーンへ行くか")] public bool goNextScene = true;
       [Header("プレイヤーゲームオブジェクト")] public GameObject playerObj;
       [Header("コンティニュー位置")] public GameObject[] continuePoint;
       [Header("ゲームオーバー")] public GameObject gameOverObj;
       [Header("フェード")] public FadeImage fade;
       [Header("ゲームオーバー時に鳴らすSE")] public AudioClip gameOverSE;
       [Header("ステージクリアーSE")] AudioClip stageClearSE;
       [Header("ステージクリア")] public GameObject stageClearObj;
       [Header("ステージクリア判定")] public PlayerTriggerOn stageClearTrigger;

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

       void Start()
       {
            if (playerObj != null && continuePoint != null && continuePoint.Length > 0 && gameOverObj != null && stageClearObj != null)
            {
                gameOverObj.SetActive(false);
                stageClearObj.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);
                GManager.instance.PlaySE(gameOverSE);
                doGameOver = true;
            }
            //プレイヤーがダメージを受けた
            else if (p.IsDownAnimEnd() && !doGameOver)
            {
                PlayerSetContinuePoint();
            }
            //ステージを切り替える
            if (fade != null && startFade)
            {
                if (fade.compFadeOut)
                {
                    GManager.instance.stageNum = nextStageNum;
                    SceneManager.LoadScene("stage" + nextStageNum);
                }
            }
            if (stageClearTrigger != null && stageClearTrigger.IsPlayerOn() && !doClear)
            {
                StageClear();
                doClear = true;
            }
       }

       /// <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;
            }
       }

       /// <summary>
       /// ステージをクリアした
       /// </summary>
       public void StageClear()
       {
            GManager.instance.isStageClear = true;
            stageClearObj.SetActive(true);
            GManager.instance.PlaySE(stageClearSE);
            if (goNextScene)
            {
                ChangeScene(GManager.instance.stageNum + 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;
       [Header("ジャンプする時に鳴らすSE")] public AudioClip jumpSE;
       [Header("やられた鳴らすSE")] public AudioClip downSE;
       [Header("コンティニュー時に鳴らすSE")] public AudioClip continueSE;
       #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 bool isClearMotion = false;
       private float jumpPos = 0.0f;
       private float otherJumpHeight = 0.0f;
       private float dashTime, jumpTime;
       private float beforeKey;
       private string enemyTag = "Enemy";
       private string moveFloorTag = "MoveFloor";
       private bool isContinue = false;
       private float continueTime, blinkTime;
       private SpriteRenderer sr = null;
       private MoveObject moveObj;
       #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 && !GManager.instance.isStageClear)
            {
                //接地判定を得る
                isGround = ground.IsGround();
                isHead = head.IsGround(); 

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

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

                //移動速度を設定
                Vector2 addVelocity = Vector2.zero;
                if (moveObj != null)
                {
                    addVelocity = moveObj.GetVelocity();
                }
                rb.velocity = new Vector2(xSpeed, ySpeed) + addVelocity;
            }
            else
            {
                if (!isClearMotion && GManager.instance.isStageClear)
                {
                    anim.Play("player_clear");
                    isClearMotion = true;
                }
                rb.velocity = new Vector2(0, -gravity);
            }
       }

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

            //地面にいるとき
            if (isGround)
            {
                if (verticalKey > 0 && jumpTime < jumpLimitTime)
                {
                    ySpeed = jumpSpeed;
                    jumpPos = transform.position.y; //ジャンプした位置を記録する
                    isJump = true;
                    jumpTime = 0.0f;
                    GManager.instance.PlaySE(jumpSE);
                }
                else
                {
                    isJump = false;
                }
            }
            //何かを踏んだ際のジャンプ
            else if (isOtherJump)
            {
                if (jumpPos + otherJumpHeight > transform.position.y && jumpTime < jumpLimitTime && !isHead)
                {
                    ySpeed = jumpSpeed;
                    jumpTime += Time.deltaTime;
                }
                else
                {
                    isOtherJump = false;
                    jumpTime = 0.0f;
                }
            }
            //ジャンプ中
            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)
            {
                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;
            GManager.instance.PlaySE(continueSE);
       }

       #region//接触判定 
       private void OnCollisionEnter2D(Collision2D collision)
       {
            if (!GManager.instance.isStageClear && !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();
                            GManager.instance.PlaySE(downSE);
                            break;
                        }
                    }
                }
                //動く床
                else if (collision.collider.tag == moveFloorTag)
                {
                    //踏みつけ判定になる高さ
                    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)
                        {
                            moveObj = collision.gameObject.GetComponent<MoveObject>();
                        }
                    }
                }
            }
       }

       private void OnCollisionExit2D(Collision2D collision)
       {
            if (collision.collider.tag == moveFloorTag)
            {
                //動く床から離れた
                moveObj = null;
            }
       }
       #endregion
 }

配置としてはこんな感じです。Stage Clearとなっていますが、これが↑でプレハブにしたプレイヤーの侵入判定です。

stage clear object

ステージクリアーはUGUIの表示順の関係上ゲームオーバーの上に持ってくるといいと思います。

インスペクターの設定を忘れずに

clear effect inspector

できたら↓のようになります。

stage clear

ちょっとフェードアウトするのが早い気がしますが、この辺は下書きから本ちゃんにした時にちゃんとしましょう。

以上、ステージクリアーでした。

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

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

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

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



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