Unity--Stealth秘密行动开发(六):敌人AI
程序员文章站
2022-07-13 08:35:00
...
Unity–Stealth秘密行动开发(六):敌人AI
分别有移动,动画,视野,射击
AI移动
把动画加入到状态机的融合树中,选择第一个控制的值为AnglarSpeed,第二控制值Speed
选择参数通过速度和角度来生成融合的控制
通过控制一个速度的值,和一个旋转的值来控制敌人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;
}
}
}
下一篇: 图的深度优先搜索和广度优先搜索