Unity3D敌人AI自动追击功能

我这次制作的是狼这个敌人:unity中有免费的资源,我选择的资源如下图所示,望读者下载方便动手以便更好地学习和理解:

Wolf Animated | 3D Animals | Unity Asset Store

将狼引入资源包后,首先把狼的一个实例拖入场景,

 在检测器中新增两个组件:分别为Rigidbody和Boxcollider:

注意细节!记得冻结y轴旋转。

接下来拖入animator的控制器,加入nav mesh agent:

 

 狼有很多动画之间的转换,我设置了两个Layer,分别为:BaseLayer和AttackLayer

BaseLayer中狼执行正常的移动巡逻动画:

 AttackLayer中狼执行发现攻击目标(玩家)后的追击功能;

接下来设置四个bool值,分别为:walk chase follow hit

 

接下来为动作切换时bool值得转换,鼠标单击动画之间的箭头:

 

AttakLayer中: 

 Base State到breathes之间的切换为:bool Chase  ,Chase为1切换到breathes。,Chase为0代表breathes返回Base State

以此类推:

breathes与run之间为:follow;attack2和run之间为:hit;

BaseLayer中:

breathes和walk之间的bool为:walk。

完成好animator的设置之后我们便来到最后一步:代码:

首先放上完整代码:

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


public enum EnemyStates { GUARD,PATROL,CHASE,DEAD }  //守卫、巡逻、追击、死亡状态
[RequireComponent(typeof(NavMeshAgent))]
public class EnemyController : MonoBehaviour
{

    private EnemyStates enemyStates;
    private NavMeshAgent agent;    //确保变量组件一定存在

    private Animator anim;


    [Header("Basic Setting")]

    public float sightRadius;
    public GameObject attackTarget;
    public bool isGuard;
    private float speed;    //追玩家的时候是一个速度,自己回去的时候又是一个速度
    public float lookAtTime;
    private float AttackWaitTime;
    private float remainLookAtTime;

    [Header("Patrol State")]

    public float patrolRange;

    private Vector3 waypoint;

    private Vector3 guardPos;  //获取最开始怪物的坐标



    //怪物受伤

    private float attackTimer;
    public float attackTime;
    public float HP = 15;

    //bool值配合动画
    bool isWalk;
    bool isChase;
    bool isFollow;
    bool isHit;

    private void Awake()
    {
        agent = GetComponent<NavMeshAgent>();
        anim = GetComponent<Animator>();

        speed = agent.speed;
        guardPos = transform.position;
        remainLookAtTime = lookAtTime;
     
    }

    private void Start()
    {
        attackTime = 2;
        attackTimer = attackTime;
        if (isGuard)
        {
            enemyStates = EnemyStates.GUARD;
        }
        else
        {
            enemyStates = EnemyStates.PATROL;
            GetNewWayPoint();//最开始给一个点
        }
    }
    private void Update()
    {
        Attacktimer();
        SwitchStates();
        SwitchAnimation();
    }

    void SwitchAnimation()
    {
        anim.SetBool("Walk", isWalk);
        anim.SetBool("Chase", isChase);
        anim.SetBool("Follow", isFollow);
        anim.SetBool("Hit", isHit); //bool值关联到一起

    }
    void SwitchStates()
    {
        //如果发现敌人切换为chase
        if (FoundPlayer())
        {
            enemyStates = EnemyStates.CHASE;
            Debug.Log("找到players");
        }

        switch(enemyStates)
        {
            case EnemyStates.GUARD:
                break;
            case EnemyStates.PATROL:
                isChase = false;
                agent.speed = speed * 0.5f;  //巡逻状态下移动速度更低
                if(Vector3.Distance(waypoint,transform.position)<=agent.stoppingDistance)//nav mesh agent 中有效帮助我们控制怪物应该停下的距离 ,判断是否走到该点。
                {
                    isWalk = false;
                    if(remainLookAtTime > 0)
                    {
                        remainLookAtTime -= Time.deltaTime;
                    }
                    else
                    {
                        GetNewWayPoint();
                    }
                }
                else
                {
                    isWalk = true;
                    agent.destination = waypoint;
                }



                break;
            case EnemyStates.CHASE:

                //配合动画
                isWalk = false;
                isChase = true;
                agent.speed = speed;
                if(!FoundPlayer())
                {
                    //回到上一个状态

                    isFollow = false;
                    if (remainLookAtTime > 0)
                    {
                        agent.destination = transform.position;//脱离目标后,追击动画失效然而动物仍然会以idle状态移动至脱离目标时角色位置;
                        remainLookAtTime -= Time.deltaTime;
                    }
                    else if(isGuard)
                    {
                        enemyStates = EnemyStates.GUARD;
                    }
                    else
                    {
                        enemyStates = EnemyStates.PATROL;
                    }
                }
                else
                {
                    isFollow = true;
                    agent.SetDestination(attackTarget.transform.position);
                    if(Vector3.Distance(attackTarget.transform.position, transform.position) <= 3)
                    {
                        isHit = true;
                        
                    }
                    else
                    {
                        isHit = false;
                    }
                }

                //追到玩家后攻击

                break;
            case EnemyStates.DEAD:
                break;

        }
    }


    bool FoundPlayer()
    {
        var colliders = Physics.OverlapSphere(transform.position, sightRadius);//以敌人为中心和当前半径查找周围所有的碰撞体;
        foreach (var target in colliders)
        {
            if(target.CompareTag("Player"))
            {
                attackTarget = target.gameObject;
                return true;
            }


        }
        attackTarget = null;
        return false;

    }


    void GetNewWayPoint()
    {
        remainLookAtTime = lookAtTime;  //重新还原时间
        float randomX = Random.Range(-patrolRange, patrolRange);
        float randomZ = Random.Range(-patrolRange, patrolRange);

        Vector3 randomPoint = new Vector3(guardPos.x + randomX, transform.position.y, guardPos.z + randomZ);    //在初始位置的基础上获取新的坐标  ,地形有坑坑洼洼,y不变
        //Vector3 randomPoint = new Vector3(transform.position.x + randomX, transform.position.y, transform.position.z + randomZ);   //在当前位置的基础上获取新的坐标

        // waypoint = randomPoint;  //选取到不能行走的点是会卡bug
        NavMeshHit hit;
        waypoint = NavMesh.SamplePosition(randomPoint, out hit, patrolRange, 1) ? hit.position : transform.position;  //获取一个点后先判断是否可移动,不可移动则保持当前坐标再次获取新的坐标点。
    }

    private void OnDrawGizmosSelected()
    {
        Gizmos.color = Color.blue;
        Gizmos.DrawWireSphere(transform.position, sightRadius);  //方便调节敌人追击范围。。。。
    }
    private void OnTriggerStay(Collider other)
    {
        if (other.tag == "Player" && Input.GetKey(KeyCode.Q) && attackTimer<=0)
        {
            attackTimer = attackTime;
            Debug.Log("kill");
            HP -= 5;

            if (HP == 0)
            {
                gameObject.SetActive(false);
           }

        }
    }
    void Attacktimer()
    {
        if (attackTimer > 0)
        {
            attackTimer -= Time.deltaTime * 2;
        }

    }
}

 代码中有部分备注,值得注意的一个地方是:

    private void OnDrawGizmosSelected()
    {
        Gizmos.color = Color.blue;
        Gizmos.DrawWireSphere(transform.position, sightRadius);  //方便调节敌人追击范围。。。。
    }

这个可以显示出敌人发现目标玩家的范围,方便我们调整合适的距离。寻找玩家是在一定范围内寻找other.tag == "Player"的物体,因此记得把玩家的tag更改为Player,把狼的tag更改为Enemy。

调整一下狼身上挂载的脚本相关信息:

 

最后还需要找到地形,手动烘培一下地形,让navmesh生成自动寻路的路径!!!否则会报错 

 

猜你喜欢

转载自blog.csdn.net/weixin_51083610/article/details/126285158