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

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

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

AI移动

把动画加入到状态机的融合树中,选择第一个控制的值为AnglarSpeed,第二控制值Speed
选择参数通过速度和角度来生成融合的控制
Unity--Stealth秘密行动开发(六):敌人AI_第1张图片
通过控制一个速度的值,和一个旋转的值来控制敌人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(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;

        }



    }
}

你可能感兴趣的:(Unity,游戏,游戏开发)