前言:前面两篇博客已经实现了简单的敌人,但是呢,这样很无趣。因为敌人只会站在原地被攻击,所以本篇我们将实现敌人AI巡逻,让敌人动起来。
敌人AI巡逻
- 场景丰富一下
- 导航网格NavMesh
- 构建导航网格
- 导航网格优化
- 玩家被当作障碍物
- 不允许爬窗
- 敌人添加Nav Mesh Agent
- 巡逻功能
- 目标
- 设置巡逻点
- 代码控制敌人巡逻
- 效果
场景丰富一下
之前的场景只有一个地面,因为要让敌人巡逻,而敌人巡逻位置通常一个小范围的地方。
所以我从素材里面拖出了一个房子,打算让AI只在房子里面巡逻。
把房子的屋顶删掉了,方便观察AI移动情况。
大家可以根据自己需求去构建场景。
导航网格NavMesh
这是Unity自带的一个可以实现自动寻路的AI功能,我们将用它实现敌人AI巡逻,以及后续的追击玩家等寻路功能。
构建导航网格
创建NavMesh Surface组件,菜单-GameObject-AI-NavMesh Surface,然后直接烘焙Bake就好了。
NavMesh Surface是一个导航网格,能够让我们为敌人AI定义可以行走的表面。
导航网格优化
玩家被当作障碍物
构建网格时候,会发现,玩家也会被当成障碍物。
可以通过图层来进行烘培时忽略玩家。
点击Player的属性,找到图层Layer,添加新的Layer。
然后把新建的Layer,给Player属性中换上。
这时候再回到我们的NavMesh中,把Include Layers中的Player给勾选掉重新烘培,就不会把玩家视作为障碍物了。
不允许爬窗
会发现自动创建的导航网格,爬窗也有路径。
可以在Agent Type进行修改。
可以设置Agent代理的一些属性,会决定导航网格的构建。目前默认的是普通人类的角色设置,我们可以加号新建一个设置为HoverBot。
Radius表示能通过的最小宽度;Height表示能通过的最小高度;Step Height设置物体向上能到达的路径的高度(类似人一步能迈多高),例如上楼和上坡等带有向上走的行为;Max Slope设置场景的斜坡的最大角度;
然后因为不想让它能翻窗所以,Step Height要非常低,还有Max Slope也要低些。
修改NavMeshSurface的Agent Type为HoverBot,重新烘培。
这时候烘焙出来就不会有翻窗和把玩家当作障碍物的问题了。
敌人添加Nav Mesh Agent
导航网格构建好了,还需要让敌人挂载Nav Mesh Agent组件,才能控制敌人在NavMesh上移动。
记得设置Agent Type切换为HoverBot。
通过代码控制敌人的NavMeshAgent能够自动找到去目标地点的最短路,并移动过去。
巡逻功能
目标
希望敌人在小屋子范围里面巡逻走动。
设置巡逻点
创建WayPoints空的GameObject,房间内巡逻就是在四个点之间循环移动,所以在WayPoints下面创建四个GameObject调整Transform,这四个位置点就作为巡逻点。
代码控制敌人巡逻
主要是操作NavMeshAgent来控制,常用的几个属性有:
- destination是NavMeshAgent组件的目的地
- nextPosition是NavMeshAgent组件的当前的位置信息
- pathPending判断路径是否在计算路径
- remainingDistance和目标距离的剩余位置
思路主要是,获取前面设置的四个位置,以此按照顺序到达不同巡逻点。每次到达一个巡逻点后,就前往下一个巡逻点。
代码思路,可以看具体的代码和注释会理解的清楚。主要就是Update的时候判断是否抵达当前巡逻点,抵达了就去下一个巡逻点。
[Header("AI")]
public NavMeshAgent enemyAgent;// 路径导航代理
public GameObject wayPointParent;// 存放巡逻点
public Transform[] wayPoints;// 存放巡逻点数组
public int wayPointIndex = 1;// 当前巡逻点数组下标
void Start()
{enemyAgent = this.GetComponent<NavMeshAgent>();// 获取NavMeshAgent组件if (wayPointParent)// 获取四个巡逻点存放在数组中{wayPoints = wayPointParent.GetComponentsInChildren<Transform>();SetNextDestination();// 设置下一个目标位置}
}
void Update()
{// 没有在计算路径,而且到达目标位置很近(距离小于0.1f)if (!enemyAgent.pathPending&&enemyAgent.remainingDistance<0.1f){SetNextDestination();// 设置下一个目标位置}
}
// 设置下一个目标位置
private void SetNextDestination()
{if (wayPoints.Length <= 0) // 不存在巡逻点,就不用设置了return;enemyAgent.destination = wayPoints[wayPointIndex].position;// 设置下一个目标位置wayPointIndex++;// 下标加加if (wayPointIndex == wayPoints.Length)// 巡逻点下表循环wayPointIndex = 1;// 跳过第0个巡逻点,因为获取的第0个巡逻点是Parent,用不上
}