一.综述
赛车游戏的敌人赛车自动寻路一般有两种方式,一种是路点寻路,另外一种就是使用Unity自带组件NavMeshAgent进行寻路了,我介绍的是后者,另外后者在水平面上的汽车寻路导航还不错,但是一旦有Y方向的爬坡、下坡等,汽车就会因为角度依然保持水平显得很不自然,并且轮胎也不会旋转,我对此进行了优化
二.NavMeshAgent
这个和NavMesh是一对,用来设置寻路的游戏对象。关于属性,我不再一一介绍,我就距我的设置来说一下:
1.AgentSize
如下图中,那个圆柱形就是NavMeshAgent了,其中AgentSize调整到更好适合车辆就可以了;
2.Stopping Distance
我将Stopping Distance设置为0,是因为,我每次都会将寻路目标设置为下一个道路监测点,如果Stopping Distance设置为大于0的话,就会看到车辆会不停地减速加速减速加速,不够连贯;
3.Speed
这个我最后查出来的它的尺寸是M/S,所以我设置的它的速度是47km/h,当然我的游戏赛道比较难跑,设置这个值比较合适。
4.Auto Traverse Off Mesh Link
这个指的是是否自动通过OffMeshLink,我这里没有什么特殊需求,不需要有些OffMeshLink需要设置才能通过,自然是自动通过最好了。
5.Obstacle Avoidance Quality
然后属性Obstacle Avoidance Quality是值的躲避障碍物的质量,对于我的游戏来说,障碍物只有一种,那就是封路用的,如下图中的导流牌,这个导流牌就添加了组件Nav Mesh Obstacle,它的作用就是动态的用作障碍物,如果隐藏掉这个组件或者直接隐藏掉组件所在的对象,障碍物效果就没有了,图中的道路就又通了
三.NavMesh
NavMesh就是NavMeshAgent就来赖以前行的寻路网格,将自己需要作为寻路路径的那部分游戏对象设置为NavMesh Static,然后再Navigation中进行Bake烘焙即可。
如下图
我将所有的道路到设置为了NavMesh Static,然后烘焙后蓝色区域就覆盖了整个道路。
需要注意的是道路有可能有不平整的地方,需要自己将道路检查一遍,如果哪些地方有断裂,需要铺平道路后重新烘焙
上图中也需要设置Agent信息,作用就是用来根据代理进行烘焙,因为代理Radius Height的不同都会导致烘焙结果的不同。
比如你将Agent Radius设置得较大,路面上的蓝色区域就会越窄,因为NavMesh会考虑给路两边留够距离。
然后Max Slope的设置就得考虑车辆最高爬坡角度了。
接下来就是对断裂地带实在没办法连接的地方进行处理了
四.Off Mesh Link
这个是专门对断裂地带进行连接的,如下图
由于坡度过大,无法贯通,于是使用Off Mesh Link。
1.首先在自动汽车对象上添加组件Off Mesh Link。
2.然后分别点击左边需要连接的木板和右边需要连接的木板,点击Navigation,选择Object选项卡,会出现Generate OffMeshLinks选项打钩,重新Bake,如下图
3.分别将左右木板对象拖曳到自动汽车的OffMeshLink组件的Start和End属性上,这样在两个木板间就会出现一个弧形的桥,连接两端。这样道路就导通了,车辆会飞跃过去。
5.NavMeshAgent.SetDestination
直接设置自动寻路汽车的寻路目标是终点显然是不合适的。想要寻路汽车可以规规矩矩按照既定的路线前行,需要不断地更新目标点,到前方一个较近的位置。
我就使用的是当汽车碰到第一个道路监测点后,设置目标为下一个道路监测点,这样汽车就会不断地往前行进了。
注:我在开发时遇到一个问题,就是汽车到某个点后停止不动了。最后发现原因是,由于汽车身上的NavMeshAgent比较大,导致先接触到下一个道路监测点,这个时候,道路监测点还没有碰撞到汽车,导致没能再去设置下一个寻路目标,解决办法就是把车身上的Agent设置的较小一些,车的碰撞体靠前一些,这样每次都是先检测到碰撞,而还没有到达寻路目的地。
6.修正汽车上下坡时车辆未倾斜的问题
我发现使用NavMeshAgent给汽车做寻路导航,缺点就是汽车只会在水平面上转弯,上坡下坡的时候还是水平的,不会沿着坡道做倾斜,我想原因是因为这个Agent也可以给人用的缘故吧,因为人上下坡时身体不会沿着坡道做倾斜。
刚开始我试图直接修改汽车游戏对象的Rotation,发现一直受Agent的干扰。最后想到的解决办法就是:
由于汽车车体/轮胎其实都是汽车对象的子对象,我直接修改子对象的角度就好了。并不会受到Agent的干扰。
我的做法是每次设置自动寻路汽车的下一个寻路目标时,顺便设置汽车角度,如下:
//设置敌人自动寻路汽车的下一个寻路目标点
private void SetEnemyCarDestionation(int index)
{
//假如当前游戏模式是竞赛模式
if (ConfigurationManager.Instance.CurrentGameModel == ConfigurationManager.GameModel.RacingModel) {
//获取下一个道路监测点
Vector3 vec = GetTheEnemyPlayerNextCollider(index).transform.position;
//设置敌人的下一个寻路目标 _enemyPlayerMeshAgent[index].SetDestination(vec);
//获取敌人的车体(是敌人游戏对象的子对象)的当前Transform
Transform currentEnemyTrans = _enemyPlayers[index].transform;
//计算车体面向下一个监测点,需要的四元数
Quaternion rotation = Quaternion.LookRotation(vec - currentEnemyTrans.position);
//设置角度,使车辆面向下一个监测点
_enemyPlayersManager[index].TurnCarDirection(rotation);
}
}
7.修改自动寻路汽车,车轮不转动的问题
由于自动寻路汽车,并不是 物理意义上的汽车,WheelCollider并没有用。我看一个教程上使用的WayPoint方法,是可以实现物理意义上的寻路的。
所以我想给自动寻路汽车加上一个比较看起来真实的转动轮胎。
也就是说,随着汽车速度的加快,轮胎转动变快,速度变慢,轮胎转动变慢。
所以需要从速度转换出当前每秒转多少度:
设当前汽车速度为N m/s,
轮胎半径为R m
则汽车转速为 N / (2 * R * π)
汽车轮胎每秒转N / (2*R*π) * 360°
代码如下:
void FixedUpdate()
{
_angularSpeed = (_thisCarNavMesh.velocity.magnitude / _wheelCircle) * 360 * 0.2f;
for (int i = 0; i < Wheels.Length; i++) {
Wheels[i].Rotate(Vector3.right, _angularSpeed * Time.fixedDeltaTime);
}
}
其中参数0.2f,是我根据效果写的修正参数,不然轮胎转的角度太大,导致效果不好。
上面这个办法我目前实验的效果还不错,但是不知道其他人怎么认为,肯定还有很多别的好办法。