欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Unity--Stealth秘密行动开发(六):敌人AI

程序员文章站 2022-07-13 08:35:00
...

Unity–Stealth秘密行动开发(六):敌人AI

分别有移动,动画,视野,射击

AI移动

把动画加入到状态机的融合树中,选择第一个控制的值为AnglarSpeed,第二控制值Speed
选择参数通过速度和角度来生成融合的控制
Unity--Stealth秘密行动开发(六):敌人AI
通过控制一个速度的值,和一个旋转的值来控制敌人AI动画

1:设置成员变量
 	public Transform[] wayPoint; //存储AI移动的位置
    public float patrolTime = 3;//休息时间
    
	private int index = 0;//AI当前要去的一个位置索引
    private float patrolTimer = 0;//巡逻计时器
    private NavMeshAgent navAgent; //导航组件--引用命名空间using UnityEngine.AI;
    private EnemySight sight;//敌人的视野类
    private float chaseTime = 3f; //等待时间
    private float chaseTimer = 0; //等待时间计时器
    private PlayerHealth health;
2:Awake() 初始化
private void Awake()
    {
        navAgent = this.GetComponent<NavMeshAgent>(); //using UnityEngine.AI;
        navAgent.destination = wayPoint[index].position;//设置目标位置
        //navAgent.updatePosition = false; //是否允许navAgent更新位置
        //navAgent.updateRotation = false;//是否允许navAgent更新旋转
        sight = GetComponent<EnemySight>();

        //委托时间测试
        //EventCenter.AddListener<string>(EventType.Printf, Printf);

        health = GameObject.FindGameObjectWithTag(Tags.player).GetComponent<PlayerHealth>();
    }
3.每一帧控制AI动画
void Update()
    {
        //判断敌人是否在射击范围内
        if (sight.playerInSight && health.hp>0)
        {
            //shoot
            Shooting();
        }
        else if(sight.alerPosition!=Vector3.zero && health.hp > 0)
        {
            //chase 追捕
            Chasing();
        }
        else
        {
            //patrolling巡逻
            Patrolling();

        }
       
    }
4:AI巡逻
private void Patrolling()
    {
        navAgent.speed = 0.5f;//巡逻速度
        navAgent.destination = wayPoint[index].position;//更新目的地
        if (navAgent.remainingDistance<0.5f)//判断导航所剩下的距离
        {
            //navAgent.isStopped = false ;//停止导航
            navAgent.isStopped = true;

            patrolTimer += Time.deltaTime;

            if(patrolTimer>patrolTime)
            {
                index++;
                index %= 4;
                navAgent.destination = wayPoint[index].position;//设置目标位置
                //navAgent.updatePosition = false; //是否允许navAgent更新位置
                //navAgent.updateRotation = false;//是否允许navAgent更新旋转
                patrolTimer = 0;
                navAgent.isStopped = false;
                                
            }

        }
    }
5:AI追捕
 private void Chasing()
    {
        navAgent.destination = sight.alerPosition;//更新导航目标的位置
        navAgent.speed = 3;//加快速度

        //navAgent.updatePosition = false; //是否允许navAgent更新位置
        //navAgent.updateRotation = false;//是否允许navAgent更新旋转

        if ( navAgent.remainingDistance<2f)//判断距离目标位置的距离
        {
            chaseTimer += Time.deltaTime;

            if(chaseTimer>=chaseTime)//当玩家在警报位置没有发现玩家,就把警报解除
            {
                sight.alerPosition = Vector3.zero; 
                GameController._instance.lastPlayerPositon = Vector3.zero;
                GameController._instance.alermOn = false;
            }
        }

    }
6.开枪
//开枪
    private void Shooting()
    {

        navAgent.isStopped = true;//停止导航

    }

AI视野

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

//敌人的视野检测(视野,声音

public class EnemySight : MonoBehaviour
{

    public bool playerInSight = false;//玩家是否在触发器内
    public float fieldOfView = 110;//视野角度
    public Vector3 alerPosition = Vector3.zero;//发现玩家警报当前位置

    public  Action  actionHandle;

    private Animator playerAnim;
    private NavMeshAgent navAgent; //导航组件:引用命名空间AI
    private SphereCollider collider;
    private Vector3 preLastPlayerPosition = Vector3.zero;

    private void Start()
    {
        preLastPlayerPosition = GameController._instance.lastPlayerPositon;
    }

    private void Awake()
    {
        playerAnim= GameObject.FindGameObjectWithTag(Tags.player).GetComponent<Animator>();//获得英雄的animation组件
        navAgent = this.GetComponent<NavMeshAgent>();
        collider = this.GetComponent<SphereCollider>();
      
    }


    private void Update()
    {
       // EventCenter.Broadcast(EventType.Printf,"委托");


        if(preLastPlayerPosition!=GameController._instance.lastPlayerPositon)//判断是否与上一个位置不同
        {
            alerPosition = GameController._instance.lastPlayerPositon;//更新发现玩家的位置
            preLastPlayerPosition = GameController._instance.lastPlayerPositon;
        }
    }


    //触发器检测----每一帧检测
    private void OnTriggerStay(Collider other)
    {
        if(other.tag==Tags.player)
        {
            //----视野检测
            //取得两个方向:1:敌人往前看的方向 2:敌人到英雄角度的方向
            Vector3 forward = transform.forward;
            Vector3 playerDir = other.transform.position - transform.position;
            float temp=Vector3.Angle(forward, playerDir); //取得两个向量最近的夹角   

            //射线检测--敌人到玩家的一条射线,是否中间有障碍物
            RaycastHit hitInfo;
            bool res=  Physics.Raycast(transform.position + Vector3.up, other.transform.position - transform.position, out hitInfo);//参数:1:射线起点 2:射线方向   3.存储射线的检测的返回值  //Vector3.up=(0,1,0),Y轴为1


            if(temp<0.5f*fieldOfView  && (res==false || hitInfo.collider.tag==Tags.player))//判断英雄是否在敌人视野的夹角内,是否碰撞到物体或者碰撞体是英雄
            {
                playerInSight = true;
                alerPosition = other.transform.position;//更新警报位置
                GameController._instance.SeePlayer(other.transform);//开启报警
            }
            else
            {
                playerInSight = false;
            }


            //声音检测--通过导航检测距离
            if (playerAnim.GetCurrentAnimatorStateInfo(0).IsName("Locomotion")) // 判断动画是否在播放  //参数0:是动画层级,IsName:当前动画名字
            {
                NavMeshPath path = new NavMeshPath(); //路径
                if(navAgent.CalculatePath(other.transform.position, path))  //计算一个路径的位置 :参数:1:目标位置  2:存储的最短路径
                {
                    Vector3[] wayPoint = new Vector3[path.corners.Length + 2]; //路点位置
                    wayPoint[0] = transform.position; //起始位置
                    wayPoint[wayPoint.Length - 1] = other.transform.position;//结束位置

                    for(int i=0;i<path.corners.Length;i++)
                    {
                        wayPoint[i + 1] = path.corners[i];//存放路点
                    }

                    //计算路点的总长度
                    float length = 0;
                    for(int i=1; i<wayPoint.Length;i++)
                    {
                        length += (wayPoint[i] - wayPoint[i - 1]).magnitude;//取得一个向量的长度
                    }

                    if(length<collider.radius)//判断总长度是否在触发器范围内
                    {
                        alerPosition = other.transform.position;//更新位置
                    }

              

                }
            }


            
        }       
    }

    //退出触发器
    public void OnTriggerExit(Collider other)
    {
        if(other.tag==Tags.player)
        {
            playerInSight = false;
        }
    }

    
}

AI动画

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

//敌人AI动画

public class EnemyAnimation : MonoBehaviour
{
    public float speedDampTime = 0.3f;//速度渐变时间
    public float anglerSpeedDampTime = 0.3f;

    private NavMeshAgent navAgent;
    private Animator anim;
    private EnemySight sight;

    private void Awake()
    {
        navAgent = GetComponent<NavMeshAgent>();
        anim = GetComponent<Animator>();
        sight = GetComponent<EnemySight>();
    }

    void Start()
    {

    }


    void Update()
    {
        //navAgent期望达到的速度 //取到旋转夹角

        if (navAgent.desiredVelocity == Vector3.zero) //判断是否达到目标位置
        {
            anim.SetFloat("Speed", 0, speedDampTime, Time.deltaTime); //设置一个值,是慢慢去设置,通过一个时间间隔去调用,speedDampTime经过多长时间达到目标值
            anim.SetFloat("AnglarSpeed", 0, anglerSpeedDampTime, Time.deltaTime);
           
        }
        else
        {
            //取得度数
            float angle = Vector3.Angle(transform.forward, navAgent.desiredVelocity);//得到需要旋转的角度
            float angleRad = 0f;
            if (angle > 90)//得到的角度大于90度,就先进行旋转
            {
                anim.SetFloat("Speed", 0, speedDampTime, Time.deltaTime);
            }
            else//得到的角度小于90度,
            {

                Vector3 projection = Vector3.Project(navAgent.desiredVelocity, transform.forward);//得到期望速度的一个投影
               
                anim.SetFloat("Speed", projection.magnitude+1, speedDampTime, Time.deltaTime*5);//设置速度
                

            }

            angleRad = angle * Mathf.Deg2Rad;  //度数转弧度函数 Mathf.Deg2Rad

            Vector3 crossRes = Vector3.Cross(transform.forward, navAgent.desiredVelocity);//得到两个向量的朝向的方向向量
            if (crossRes.y < 0)//判断是否向左旋转,改变方向
            {
                angleRad = -angleRad;
            }

            anim.SetFloat("AnglarSpeed", angleRad, anglerSpeedDampTime, Time.deltaTime);//更新旋转的角度--通过弧度来旋转
            navAgent.nextPosition = transform.position;

        }


        anim.SetBool("PlayerInSight", sight.playerInSight);
    }
}

AI射击

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

//敌人射击

public class EnemyShooting : MonoBehaviour
{
    public float minDamage = 30;//最低伤害

    private Animator anim;
    private bool haveShoot=false;//是否已经进行过计算伤害
    private PlayerHealth health;


    private void Awake()
    {
        anim = this.GetComponent<Animator>();
        health = GameObject.FindGameObjectWithTag(Tags.player).GetComponent<PlayerHealth>();
    }

    private void Update()
    {
        if(anim.GetFloat("Shot")>0.5f)
        {
            Shooting();
        }else
        {
            haveShoot = false;
        }
    }

    //开枪动画
    private void Shooting()
    {
        if(haveShoot==false)
        {
            //计算伤害
            float damage = minDamage + 90 - 9 * (transform.position - health.transform.position).magnitude;//伤害根据英雄和敌人的距离来计算
            health.TakeDamage(damage);//吸收伤害
            haveShoot = true;

        }



    }
}