实现A*寻路的三种工作方式:
1.基于单元格的导航图
基于单元格的导航图将地图划分为多个正方形单元或者六边形组成的规则网络,这种导航图易于理解和使用,结构相对简单,易于动态增加建筑物或者障碍等,适用于即时战略游戏或者塔防游戏,寻路以网格为单位,精准的寻路需要大量的节点,对内存要求比较高。另外在环境中包含的不同地形,也许需要通过额外信息进行存储,这也需要一定开销。
2.创建可视点导航图
可视点导航图,由设计人员在场景中放置一些路径点,在点之间相连接就是边,AI在路径点之间进行移动。此方法存在一些局限性,当我们设计时需要大量的手工放置。
3.创建导航网格
导航网格(Navmesh)将场景中可活动区域划分为凸多边形。导航网格表示出了可行走区域的真实几何关系,是一个非均匀网络。相比单元格导航,三角形每个相邻节点就是相邻的三角形。
4.A* Pathfinding Project插件
A* 寻路的实现具有一定难度,我们通过引入A*寻路的插件,来实现具体功能。
在场景中添加一个空物体,给这个物体添加Astar Path组件,我们先使用Grid Graph来创建一个寻路网格。
设置Width、Depth控制网格大小
为了避免浮点误差,将Center的y设置为 - 0.1
网格会进行高度测试,在网格上方RayLength的高度向下方发射Ray,以此来检测地形的高低起伏
碰撞测试系统需要检测节点的可行走性,通过添加ObstacleLayerMask,系统知道那些是障碍物,无法通过。
生成网格的实际效果:
GridGraph.PNG
网格生成后通过seeker来查找路径,将查找到的路径存储在Path类中,通过path.vectorPath[],获取到各个路径点,来实现路径移动的效果
public class AstarAI : MonoBehaviour {
//持有组件的引用
private CharacterController cha;
private Seeker seeker;
//目标的Transform
public Transform targetPosition;
public Path path;
public float speed = 100;
private float nextWaypointDistance = 3;
private int currentWaypoint = 0;
public float turnSpeed = 5;
// Use this for initialization
void Start () {
cha = GetComponent<CharacterController>();
seeker = GetComponent<Seeker>();
//seeker添加一个回调函数,在寻路完成后调用此函数
seeker.pathCallback += OnPathComplete;
seeker.StartPath(transform.position, targetPosition.position);
}
private void FixedUpdate()
{
if(path == null)
{
return;
}
//如果当前路点大于路径上点数总和,已经到达终点
if (currentWaypoint >= path.vectorPath.Count)
{
Debug.Log("arrive");
return;
}
//向着下一个路径点进行移动
Vector3 dir = (path.vectorPath[currentWaypoint] - transform.position).normalized;
dir *= speed * Time.fixedDeltaTime;
cha.SimpleMove(dir);
Quaternion targetRotation = Quaternion.LookRotation(dir);
transform.rotation = Quaternion.Lerp
(transform.rotation, targetRotation, Time.fixedDeltaTime * turnSpeed);
if ((Vector3.Distance(path.vectorPath[currentWaypoint], transform.position))
< nextWaypointDistance)
{
currentWaypoint++;
return;
}
}
public void OnPathComplete(Path p)
{
Debug.Log("Find the path" + p.error);
if(! p.error)
{
path = p;
currentWaypoint = 0;
}
}
private void OnDisable()
{
seeker.pathCallback -= OnPathComplete;
}
GridGraphComplete.PNG