秘密行动AI部分:
机器人功能:视野范围搜索玩家功能、模拟听觉范围搜索玩家功能、广播方式搜索玩家功能。
机器人动作模式:巡逻模式、追捕模式、射击模式。
使用到的知识点:Animator、触发器、BlendTree动作系统、动作遮罩、动作分层、Material、3D数学。
其中3D数学作用在AI部分最广泛,使用到:
Vector.Project(投射向量,被投射向量)获取投射后的向量。这个画图解释吧
Vector3.Angle两个向量的小夹角
角度变弧度的计算公式:angle*(2π/360)=angle*Mathf.Deg2Rad=角度*每度的弧度长度=弧度。
左手法则:Vector3.Cross(大拇指朝向的向量,食指朝向的向量)得到中指向量,即两个向量所构成面的垂直向量(法线)
根据垂直向量的朝向(垂直向量Y轴数值是正数还是负数),判断拇指向量和食指向量之间的位置关系,如果Y值是负数,那么食指指向的向量在大拇指朝向的向量的左边,否则在右边。为什么要说这个的原因是因为我们使用Vector3.Angle得到的夹角是不确定方向的,假设大拇指向量是玩家forward前方向量,食指向量是玩家到目标点向量,通过Vector3.Angle得出夹角后,你还需要知道这个夹角的方向,
Mathf.Lerp差值运算Vector3,Quaternion.Lerp差值运算角度(这个运算会最优化计算出哪个方向的角度,即往小角度方向转动)
带方向的夹角=Quaternion.LookRotation(目标向量,轴心Vector3.up、Vector3.right、Vector3.forward)。(带方向的夹角=物体朝前向量和目标朝向向量),在3D场景上使用方法一般如下:
void Update(){
//speed代表转速
float speed = 1.0f;
Quaternion targetRotate = Quaternion.LookRotation(目标向量,Vector3.up);
transform.rotate = Quaternion.Lerp(transform.rotate, targetRotate, speed * Time.deltaTime);
}
Animator中有GetCurrentAnimatorStateInfo(0).IsName("Locomotion")表示动作管理器的第0层的当前动作播放状态,判断是不是Locomotion这个动作在播放。
AI脚本最神奇的一个是:
//判断玩家是否发出脚步声
if(playerAnim.GetCurrentAnimatorStateInfo(0).IsName("Locomotion"))
{
//利用Nav导航组件获取最短路径path
NavMeshPath path = new NavMeshPath();
//当前位置与目标位置的最短路径计算,赋值给path,如果没有这个路径则返回false,有则返回true
if(navAgent.CalculatePath(other.transform.position,path))
{
//path.corners是一个数组,记录着最短路径的转折结点位置(不包括起始点和终点)
Vector3[] wayPoints = new Vector3[path.corners.Length + 2];
//起始点和终点放入路径数组
wayPoints[0] = transform.position;
wayPoints[wayPoints.Length - 1] = other.transform.position;
//把转折点也放入数组,注意按照顺序位置放
for(int i=0;i<path.corners.Length;i++)
{
wayPoints[i+1] = path.corners[i];
}
//最短路径长度计算
float length = 0;
for(int i=1;i<wayPoints.Length;i++)
{
//当前结点减去前一个结点得出向量,然后取长度,累加到length
length += (wayPoints[i] - wayPoints[i - 1]).magnitude;
}
//判断最短路径长度是否小于检测的半径,若小于代表听到了声音
if(length<sphere.radius)
{
//改变警报位置(玩家可能在的位置)
alermPos = other.transform.position;
}
}
}
第二个是视野之内发现玩家方法:使用了Vector3.Angle和射线检测来确定
Vector3 forward = transform.forward;
Vector3 playerDir = other.transform.position - transform.position;
//取得敌人正前方和玩家所在方向之间的小夹角
float temp = Vector3.Angle(forward, playerDir);
RaycastHit hit;
bool res = Physics.Raycast(transform.position + Vector3.up, other.transform.position - transform.position, out hit);
//在视野之内,判断:因为这个temp角度,是有左角度和右角度的,比如玩家在敌人左前方,那么角度是小于这个视野/2的角度的话 也算是进入到了视野范围呢
//因为视野范围的真正意思是一个半圆,中间线把视野分成2边,左边和右边视野都是territoryAngle/2
//当这个角度小于territoryAngle/2的话,就等于你进入了视野范围内了。
if (temp<territoryAngle*0.5f&&(res==false||hit.collider.tag=="Player")&& health.hp>0)
{
playerInSight = true;
alermPos = other.transform.position;
GameController._instance.SeePlayer(other.transform);
}
else
{
playerInSight = false;
}
广播方式搜索玩家功能:这个功能其实就是弄一个脚本设定一个Vector3字段保存最后一次发现玩家的位置,把该脚本弄成单例,其他机器人脚本里面就能直接根据这个Vector3字段来知道玩家的位置,如果不为Vector3.zero那就是发现了玩家,发现玩家是由某个机器人发现的,机器人发现后就要把这个玩家位置更新到单例脚本上,那么其他机器人也能知道了玩家位置,只要把这个玩家位置设定为Vector3.zero或其它什么的就相当于取消了这个机器人追捕玩家的过程。这里说的单例脚本认真看脚本的,应该能看见上面有个叫GameController._instance.SeePlayer(other.transform);的代码,这个就是把玩家位置传递到那个单例脚本了。