A-Star Pathfinding插件学习记录(二)
继A* Pathfinding插件学习记录(一) 文章,本文对笔者在项目中重点使用到的做个详细的分享,也欢迎大家交流指正。(别只做个伸手党,我写博客一是为了做个记录,其二还是希望可以得到反馈,相辅相成,共同成长。)
一. AstarPath和GraphNode
描述:AstarPath是A*寻路系统的核心组件。计算所有路径并存储信息。
重要接口介绍:
AstarPath.active获取实例
AstarPath.active.isScanning:是否有任意一个图形正在扫描
AstarPath.active.data.AddGraph//创建新的Graph
AstarPath.active.AddWorkItem:添加要在路径查找暂停时处理的工作项。这里安全的更新图。
AstarPath.active.GetNearest(transform.position)//获取图中临近的GraphNode节点.
AstarPath.active.data.pointGraph.AddNode((Int3)StartTransform.position)//在graph中添加一个节点
node.Walkable = false;//将节点设置不可走的。比如设置jump跳上平台的几率就可以扩展GraphModify中随机End的Walkable的概率。
GraphNode startNode=…; startNode.RemoveConnection(endNode);//移除startNode到EndNode的连接。
startNode.AddConnection(endNode, cost);//设置Start到End的连接
//node.ClearConnections(true);清理所有link链接。true表示双向都清理
startNode.SetPosition//设置节点坐标
二. MovementScript
描述:AIPath和AILerp可以用于任何Graph,RichAI只适用于以NavMesh为基础类型的 Graph。
AIPath 和 RichAI 脚本松散地遵循路径, AILerp 脚本使用插值非常精确地沿路径移动。
1.1 AIPath
- 平滑地遵循路径并响应物理。
- 支持ROV
- 支持3d和2d
- 使用所有graph
1.2 RichAI
- RichAI只适用于以NavMesh为基础类型的 Graph。其他都不支持,包括PointGraph。
- RichAi比AIPath在Navmesh类型的Graph上更好的追随路径。通常更加平稳的遵循路径以及物理。即使没有碰撞体,它也能很好的约束在网格内不发生穿墙,太棒了
- 而且RichAi比AIPath更好的支持NodeLink也称OffsetMeshLink。RichAi在遵循路径时可以检测到链接,知道啥时候进入了Link。处理在Link过程的逻辑。因为它提供了onTraverseOffMeshLink事件,它当Agent开始遍历某个Link时调用,如果onTraverseOffMeshLink==null,那么默认agent将使用线性插值的方式移动到Link上的点。可以参考AnimationLinkTraverser脚本。
void OnEnable () { ai = GetComponent<RichAI>(); if (ai != null) ai.onTraverseOffMeshLink += TraverseOffMeshLink; } void OnDisable () { if (ai != null) ai.onTraverseOffMeshLink -= TraverseOffMeshLink; } IEnumerator TraverseOffMeshLink (RichSpecial link) { // Traverse the link over 1 second float startTime = Time.time; while (Time.time < startTime + 1) { transform.position = Vector3.Lerp(link.first.position, link.second.position, Time.time - startTime); yield return null; } transform.position = link.second.position; }
Link:
link默认有三种类型,分别是NodeLink、NodeLink2、NodeLink3.
NodeLink:
NodeLink在遵循路径时,agent不可能检测到该链接,所以它并不支持于RichAI。如果需要检测链接,推荐使用NodeLink2
NodeLink2:
这种链接类型可以在跟踪时检测到,因为它在中间有这些特殊的点节点。这两个点是pointGraph的pointNode。虽然RichAI不支持PointGraph导航,不过它内部应该是检测nodelink的PointNode来检测是否进入连接。
NodeLink3:
连接两个TriangleMeshNodes,使共享一条边。通常不会使用
Seeker
Seeker.StartPath很傻蛋。有时候不会计算off-MeshLink。同样的逻辑 将startPath替换为destination寻路完美解决。真搞不懂seeker的设计是何用。后处理+seeker更是与AStarPath严重割裂。
路径终点如果是在Link的start上,reachedEndOfPath不会为true。主要是approachingPathEndpoint这个属性不会更新为true。很离谱!
想要停止Agent怎么办?
1. 使用seek.isStoped。不过在下次使用要恢复seek.isStoped=false
显然这种做法很笨很傻。
2. 那使用seek.StartPath(aipath.position) 将寻路目标设置当前路径。
好像没啥问题,不过发现还是会出问题,有时候不会停止,而是缓慢向前挪动。
3. 最好的解决方案就是将MovementScript 禁用再立即开启。其中让其重置了一下?
Path
FakePath:
根据描述 这是一个假路径,就是说它不是有真实地图上扫描出来的点组成的。是一个我们人为设定的点,类似与PointGraph。不同的是不能使用Seeker它生成的路径,一般用作路径计算,而不是驱动Agent移动。如果实在要驱动移动,也只能使用AIPath这类移动脚本,通过以下代码驱动,RichAI不支持直接使用 FakePath。
var path = ABPath.FakePath(new List<Vector3> { new Vector3(1, 2, 3), new Vector3(4, 5, 6) });
ai.SetPath(path);
FakePath也可和真实路径点进行合并,得到一个新的路径,示例:
var a = Vector3.zero;
var b = new Vector3(1, 2, 3);
var c = new Vector3(2, 3, 4);
var path1 = ABPath.Construct(a, b);
var path2 = ABPath.Construct(b, c);
AstarPath.StartPath(path1);
AstarPath.StartPath(path2);
path1.BlockUntilCalculated();
path2.BlockUntilCalculated();
// Combine the paths
// Note: Skip the first element in the second path as that will likely be the last element in the first path
var newVectorPath = path1.vectorPath.Concat(path2.vectorPath.Skip(1)).ToList();
var newNodePath = path1.path.Concat(path2.path.Skip(1)).ToList();
var combinedPath = ABPath.FakePath(newVectorPath, newNodePath);
如何获取路径的长度:
在NavMeshAgent中可以直接像系统申请计算CalcuatePath,得到Path信息。
AStarPathFinder使用Construct同样也可以,但是要保证能够符合agent移动轨迹需要向路径Apply modified修改器。(不过即使这样,貌似没法输入agent的radius、height等信息去计算路径。而且在应用时,在某些时候会报错,暂不清楚什么原因)。
要完美做到计算的路径是当前agent的路径轨迹,只能通过seeker来计算。不过seeker的StartPath我们知道会在计算后的下一帧执行移动,这个很操蛋。而且我们还得考虑在计算路径的时候,之前是否在执行计算路径或在移动,确保我们在主线程计算完路径长度后要能够恢复原先状态。
示例代码:
Path pathA = seeker.GetCurrentPath();
Path pathB = seeker.StartPath(seeker.transform.position,target);
pathB.BlockUntilCalculated();
dis += pathB.GetTotalLength();
if (pathA!=null&&pathA.vectorPath!=null&&pathA.vectorPath.Count>0){
seeker.StartPath(seeker.transform.position,pathA.vectorPath.Last<Vector3>()); }
来源:麦瑞克博客
链接:https://www.playcreator.cn/archives/unity/unity_technologyshare/3714/
本博客所有文章除特别声明外,均采用CC BY-NC-SA 4.0许可协议,转载请注明!