【バイオハザード風のゲームを作ってみよう。その10】

今回も下記やっていきましょう。
youtu.be

今回はNavMeshを使用してプレイヤーをゾンビが追いかけさせるようにしていきます。

NavMesh概要は下記参照。
www.midnightunity.net

・ゾンビにNavMeshAgentコンポーネントとRigidBodyコンポーネントをアタッチします。なお、前回の【その9】内でRayCastHit
使用してゾンビはプレイヤーを探知していますが、RigidBodyはひとつのオブジェクトと見なされるようで、ゾンビ自身のRigidBodyが干渉してプレイヤー探知が出来ません。そこで、ゾンビレイヤーを追加しゾンビレイヤーを無視するようにします。また、下記スクリプトを変更します。

public class IdleState : State //EP8追加 ゾンビ直下のstatesオブジェクトにアタッチ
{
    (省略)
    //This setting determins where our linecast starts on the Y Axis of the character(used for line of sight)
    [Header("Line of Sight Detection")] 
    [SerializeField] float characterEyeLevel = 1.8f; //EP10追加
    [SerializeField] LayerMask ignoreForLineOfSightDetection; //EP10追加

 (省略)
 private void FindATargetViaLineOfSight(ZombieManager zombieManager)
    {
        //We are searching ALL colliders on the layer of PLAYER within a certain radius
        //Physics.OverlapSphere戻り値: Collider[] 球体の内部や触れたすべてのコライダーの配列を取得します.
        Collider[] colliders = Physics.OverlapSphere(transform.position, detectionRadius, detectionLayer);
       // Debug.Log(colliders.Length);

        Debug.Log("we are checking for colliders");

        //For every collider that we find, that is on the same layer pf the player, we try and serch it for PlayerManager script
        for(int i = 0; i < colliders.Length; i++)
        {
            PlayerManager player = colliders[i].transform.GetComponent<PlayerManager>();

            //If rhe playerManager is detected, we then check for line of sight
            if (player != null)
            {
                Debug.Log("we have found the player collider");

                //The target mudt be infront of us
                Vector3 targetDirection = transform.position - player.transform.position;
                float viewableAngle = Vector3.Angle(targetDirection, transform.forward);
              //  Debug.Log("VIEWABLE ANGLE=" + viewableAngle);

                if(viewableAngle>minimumDetectionRadiusAngle && viewableAngle < maximumDetectionRadiusAngle) 
                {
                    Debug.Log("we have passed the field of view check");

                    RaycastHit hit;
                   
                    Vector3 playerStartPoint = new Vector3(player.transform.position.x, characterEyeLevel, player.transform.position.z);
                    Vector3 zombieStartPoint = new Vector3(transform.position.x, characterEyeLevel, transform.position.z);

                    Debug.DrawLine(playerStartPoint, zombieStartPoint,Color.yellow);

                    //CHECK ONE LAST TIME OUR
                    //Linecast:始点と終点を設定してそこに線を張り、コライダーがヒットした場合 true を返します.
                    if (Physics.Linecast(playerStartPoint,zombieStartPoint,out hit, ignoreForLineOfSightDetection)) //EP10変更
                    {
                        Debug.Log("There is something iin the way");
                        //Cannot find the target, there is an object in the way
                                            }
                    else
                    {
                        Debug.Log("We have a target, switching states");
                        zombieManager.currentTarget = player;
                    }
                }
            }
        }
    }

}

・ゾンビがプレイヤーを追いかけるようにします。そして、両者が一定距離となるとゾンビがIdle状態なるようにします。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI; //EP10追加

public class ZombieManager : MonoBehaviour //EP8追加 ゾンビオブジェクトにアタッチ
{
    //The state this character begins on
    public IdleState startingState;

    [Header("Current State")]
    //The state this character is currently on
    [SerializeField] private State currentState;

    [Header("Current Target")]
    public PlayerManager currentTarget; //EP9追加
    public float distanceFromCurrentTarget; //EP10追加

    [Header("Animator")] //EP10追加
    public Animator animator;

    [Header("Navmesh Agent")]
    public NavMeshAgent zombieNavmeshAgent; //EP10追加

    [Header("RigidBody")]
    public Rigidbody zombieRigidbody; //EP10追加

    [Header("Locomotion")]
    public float rotationSpeed = 5f; //EP10追加

    [Header("Attack")]
    public float minimumAttackDistance = 1f; //EP10追加

    private void Awake()
    {
        currentState = startingState;
        zombieNavmeshAgent = GetComponentInChildren<NavMeshAgent>(); //EP10追加
        animator = GetComponent<Animator>(); //EP10追加
        zombieRigidbody = GetComponent<Rigidbody>(); //EP10追加
    }

    private void FixedUpdate()
    {
        HandleStateMachine();
    }

    private void Update() //EP10追加
    {
        zombieNavmeshAgent.transform.localPosition = Vector3.zero;

        if (currentTarget != null) //EP10追加
        {
            //ターゲットと自身の距離を計算
            distanceFromCurrentTarget = Vector3.Distance(currentTarget.transform.position, transform.position);
        }
    }

    private void HandleStateMachine()
    {
        State nextState;

        if (currentState != null)
        {
            nextState = currentState.Tick(this);

            if (nextState != null)
            {
                currentState = nextState;
            }
        }
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PurseTargetState : State //EP8追加 ゾンビ直下のstatesオブジェクトにアタッチ
{
    AttackState attackState; //EP10追加

    private void Awake()
    {
        attackState = GetComponent<AttackState>(); //EP10追加
    }

    public override State Tick(ZombieManager zombieManager)
    {
        //logic to follow current target goes here

        Debug.Log("Running pursue target state");
        MoveTowardsCurrentTarget(zombieManager); //EP10追加
        RotateTowardsTarget(zombieManager); //EP10追加

        //ゾンビとプレイヤーの距離が設定値(zombieManager.minimumAttackDistance)よりも近い場合
        if (zombieManager.distanceFromCurrentTarget <= zombieManager.minimumAttackDistance) //EP10追加
        {
            return attackState;
        }
        else
        {
            return this;
        }
        
    }

    private void MoveTowardsCurrentTarget(ZombieManager zombieManager) //EP10追加
    {
        //walkアニメーションに切り替え
        zombieManager.animator.SetFloat("Vertical", 1f, 0.2f, Time.deltaTime);
    }

    private void RotateTowardsTarget(ZombieManager zombieManager) //EP10追加
    {
        zombieManager.zombieNavmeshAgent.enabled = true;
        //移動先に移動開始
        zombieManager.zombieNavmeshAgent.SetDestination(zombieManager.currentTarget.transform.position);
        //ターゲット方向に回転
        zombieManager.transform.rotation = Quaternion.Slerp(zombieManager.transform.rotation,
            zombieManager.zombieNavmeshAgent.transform.rotation, zombieManager.rotationSpeed / Time.deltaTime);
            
    
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AttackState : State //EP10追加 ゾンビ直下のstatesオブジェクトにアタッチ
{
    public override State Tick(ZombieManager zombieManager)
    {
        Debug.Log("ATTACK");
        //Idleアニメーションに切り替え
        zombieManager.animator.SetFloat("Vertical", 0f, 0.2f, Time.deltaTime);
        return this;
    }
}

今回の結果がこんな感じです。
youtu.be

今回は以上です。