ひつじコレクション - 7. チェイサーとの当たり判定を実装

[ミニゲームを作ってUnityを学ぶ！[ひつじコレクション編]](https://qiita.com/YzRoid/items/8dfd6c168b916ac82a0d)
###**第7回目: チェイサーとの当たり判定を実装**

前回はプレイヤーの邪魔をする敵キャラクター（チェイサー）をゲームに登場させて3種類の行動パターンを実装しましたが、思ったより長くなってしまったためコードを読み解いてもらうような解説になってしまいました。

そちらは後程スキをみて解説を付け足すとして、今回はチェイサーに対する当たり判定を実装していきます。

#MonoBehaviourを拡張する
話が前後しますがチェイサーとの接触時の処理について、プレイヤーがチェイサーと接触した際、あるいは追従しているフォロワーがチェイサーと接触した際にはまずメソッドAを実行し、その数秒後にメソッドBを実行するといった遅延処理の仕組みが必要になります。

この遅延処理をUnityで行うにはInvoke()またはコルーチンを利用するのが一般的です。

**参考:** [スクリプトの処理の実行タイミングを操作する](https://qiita.com/toRisouP/items/e402b15b36a8f9097ee9)

今回はこちらの参考にさせていただいた記事に習って、簡単に遅延処理が実行できるようMonoBehaviourを拡張します。

+ スクリプト「CustomMonoBehaviour」を作成


```cs:CustomMonoBehaviour

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

public class CustomMonoBehaviour : MonoBehaviour {

    protected IEnumerator DelayMethod(float waitTime, Action action)
    {
        yield return new WaitForSeconds(waitTime);
        action();
    }
}

```
MonoBehaviourを継承し、遅延処理を行うためのコルーチンを定義したシンプルなクラスです。
このCustomMonoBehaviourを継承したクラスは通常のMonoBehaviourを継承することで利用できる機能に加えて、遅延処理を簡単に導入することができます。

[MonBehaviour（Unityスクリプトリファレンス）](https://docs.unity3d.com/jp/current/ScriptReference/MonoBehaviour.html)



#プレイヤー側の処理
それでは実際にCustomMonoBehaviourを継承してプレイヤーに当たり判定の実装と接触時の処理を追加していきます。

+ 新しいタグ「Chaser」を作成して3種のチェイサーそれぞれのタグに設定
+ PlayerActionにCustomMonoBehaviourを継承させる

```cs:PlayerAction.cs

    public class PlayerAction : MonoBehaviour{
        ~
        ~
        ~
        ~
    }

    ↓MonoBehaviourの部分をCustomMonoBehaviourに変更

    public class PlayerAction : CustomMonoBehaviour{
        ~
        ~
        ~
        ~        
    }


```

+ PlayerActionに当たり判定と接触時の処理を追加

```cs:PlayerAction.cs

    void Update()
    {

↓CheckInputMouse()はもう必要ないので削除する

        CheckInputMouse();

        switch (State)
        {
            case STATE.IDLE:
            case STATE.RUN:
                CheckInputMove();
                break;
        }
    }

        void FixedUpdate()
        {
            switch (State)
            {
                case STATE.IDLE:
                case STATE.RUN:
                    UpdateMove();
                    break;
追加            case STATE.ROLL:
                    UpdateRoll();
                    break;
            }
        }

追加    private readonly string TAG_CHASER = "Chaser";

        void OnTriggerEnter(Collider other)
        {
            if (State != STATE.IDLE && State != STATE.RUN) return;

            // ポップアップポイントに接触した
            if (other.tag == TAG_POPUP_POINT) TakeFollower(other.GetComponent<PopupPoint>());

            // ゴールエリアに到達した
            if (other.tag == TAG_GOAL_AREA) ReleaseFollower();

            // 敵と接触した
追加        if (other.tag == TAG_CHASER) OnRoll();
        }

        //---------------
        // 回転・ダウン //
        //---------------------------------------------------------------------------------

        /// <summary>
        /// その場で回転を開始し、一定秒後にダウン状態へ遷移する
        /// </summary>
        public void OnRoll()
        {
            if (State == STATE.ROLL || State == STATE.DOWN) return;
            State = STATE.ROLL;
            mAnim.Play(PlayerAnimation.ANIM_ID.IDLE);
            mRigid.velocity = Vector3.zero;
            mRigid.angularVelocity = Vector3.zero;
            StartCoroutine(DelayMethod(1.0f, () => OnDown()));
        }

        private void UpdateRoll()
        {
            Vector3 angle = new Vector3(0.0f, 540.0f, 0.0f) * Time.deltaTime;
            mTrans.Rotate(angle);
        }

↓OnDown()はすでに実装済

        /// <summary>
        /// ダウンアニメーションの初期化
        /// </summary>
        public void OnDown()
        {
            State = STATE.DOWN;
            mRigid.velocity = Vector3.zero;
            mRigid.angularVelocity = Vector3.zero;
            mAnim.Play(PlayerAnimation.ANIM_ID.DOWN);
        }

```
遅延処理を行っている部分はOnRoll()の最後の行です。
<pre>
StartCoroutine(DelayMethod(1.0f, () => OnDown()))
</pre>

プレイヤーはチェイサーに接触するとその場で回転を始め、その1秒後にOnDown()を実行します。また回転開始のタイミングで操作不能となり、現段階では事実上のゲームオーバーとなります。

![action_sheep_ss_7_1.jpg](https://qiita-image-store.s3.amazonaws.com/0/239877/18fb4317-b510-e342-d325-c8214dd83131.jpeg)



#フォロワー側の処理
続いて、フォロワーがチェイサーと接触した際の処理を実装していきます。

+ FollowerModelにCustomMonoBehaviourを継承させる
+ FollowerModelに当たり判定と接触時の処理を追加
+ PlayerActionに対応するメソッドを追加


```cs:FollowerModel.cs

        /// <summary>
        /// 吹き飛ばされ、一定時間後に休眠状態へ遷移する
        /// </summary>
        public void BlowOff()
        {
            State = STATE.BLOW_OFF;
            mAgent.velocity = Vector3.zero;
            mAgent.isStopped = true;
            mAgent.updatePosition = false;
            mRigid.isKinematic = false; // 物理挙動を一時的に有効にする
            mRigid.AddForce(new Vector3(200.0f, 200.0f, 0.0f));
            StartCoroutine(DelayMethod(3.0f, () => Sleep()));
        }

        //-------------
        // 当たり判定 //
        //---------------------------------------------------------------------------------

        private readonly string TAG_CHASER = "Chaser";

        void OnTriggerEnter(Collider other)
        {
            // 追従中に敵と接触した場合はプレイヤーに通知
            if (other.tag == TAG_CHASER)
            {
                if (State == STATE.FOLLOW)
                {
                    PlayerAction pa = GameController.Instance.StageManager.GetPlayer().GetComponent<PlayerAction>();
                    pa.OnContactFollower(mOrder);
                }
            }
        }

```

```cs:PlayerAction.cs

        /// <summary>
        /// フォロワーが敵と接触した際に、接触したフォロワーから呼び出される。
        /// 接触したフォロワーとそれより後ろに追従していたフォロワーは吹き飛ばされ、追従が解消される。
        /// </summary>
        /// <param name="order">接触したフォロワーの並び番号</param>
        public void OnContactFollower(int order)
        {
            int count = mFollowerList.Count;
            for (int i = count - 1; i >= order; i--)
            {
                mFollowerList[i].BlowOff();
                mFollowerList.RemoveAt(i);
            }

            // 速度を再設定
            SetSpeed();
            foreach (FollowerModel model in mFollowerList)
            {
                model.SetSpeed(mSpeed);
            }
        }

```
チェイサーと接触したフォロワーは自身の接触をPlayerActionに通知します。
PlayerActionは通知を受け取ると、接触したフォロワーとそれより後ろを追従していた全てのフォロワーに対してBlowOff()を実行することで吹き飛ばされるアクションを開始させ、BlowOff()を呼び出されたフォロワーはその3秒後にSleep()を実行することで休眠状態（次のポップアップまで待機する状態）になります。

<font color="blue">フォロワーとしてウサギさんのアセットを使用している場合は飛び跳ねるアニメーションによってチェイサーとの接触を避けているように見える場合がありますが、AnimatorのApplyRootMotionのチェックを外しているため実際の当たり判定はアニメーションの影響を受けず、常に地面近くに存在しています。</font>

![action_sheep_ss_7_2.jpg](https://qiita-image-store.s3.amazonaws.com/0/239877/a029e35d-9c27-8c30-754e-96df7956c620.jpeg)


---
[次のページに進む](https://qiita.com/YzRoid/items/4f20f17302ba0c105a54)
[イントロダクションに戻る](https://qiita.com/YzRoid/items/8dfd6c168b916ac82a0d)
<BR>
<div><img src="http://unity-chan.com/images/imageLicenseLogo.png" alt="ユニティちゃんライセンス"><p>この作品は<a href="http://unity-chan.com/contents/license_jp/" target="_blank">ユニティちゃんライセンス条項</a>の元に提供されています</p></div>
<Br><Br>