前言
It’s Time for Code!
今天研究的内容:
Path脚本
Path定义了若干transform点组成的队列,描述Tile中的路径。
Path还指定了与其连接的下一个Path,可能有多个Path于其相连,比如十字路口处车辆有三个方向可以开。就有一个路径和三个路径相连。
甚至还指定了载具的运动速度,不过我最好把这个放到每种载具中指定。
Tile脚本
明显它定义了一大堆枚举来描述一个Tile的各种性质。我不需要行人相关的类型。
Paths来自Tile游戏对象的子对象;static对象tiles存储场景中所有tile,通过每个Tile在Awake函数中把自己加到里面去。*
NeighborTiles,由GetNeighborTiles函数取得。
首先,根据Tile的verticalType,设置待会碰撞检测盒要用到的各方向偏移量和尺寸。
执行碰撞检测,根据TileShape,获取对应方向上的NeighborTile;比如直线Tile的NeighborTile就是Top和Bottom两个方向。弯道Tile就是Top和Left两个方向
获取碰撞检测得到的Tile
用于判断neighbor位置的Direction类。不仅仅是场景位置,还有NeighborTiles队列中的位置。
最后,由Tile给Paths中的每条path指定其连接的下一条path。同样忽视人行道。
连接方法是,传入path的最后一个点。以及pathType。首先我们忽视人行道path,然后查看所有Tile中的所有Path,并与距离小于标准缩放下0.5m的path起点相连就行。有时会有多条,比如十字路口的情况。头尾相连确保了算法得到的路径不会使车辆逆行。
PathFinding
Low Poly Epic City的寻路最小单元为Path,由于Path本身只是一连串点的队列,所以由Path包装上fCost和gCost,项目中叫score和currentScore,组成PathNode类作为寻路算法的Node。
和塞巴大佬一样,用到堆来优化Open中找最小fcost的速度。BinaryHeap是专门开发出来用于Open的,内部为PathNode。
两个Dictionary用于快速查找,Guid是每个Tile、Path生成时调用Unity引擎的API自动生成的随机唯一标识。
PathList为目标性质的,有始有终的路径,而wholePath用于循环路径。
startTile和endTile用于寻找起始和终点的Path。endId用于存储寻路算法找到的最终Path的Guid。
是时候祭出那张图了。
接下来看看他们是如何获取路径的。
首先,根据传入的Vector3得到起始Tile和终点Tile,再去获取起点的所有Path。
对于每一条Path,计算hCost和gCost,hCost采用CalculateHeuristic,gCost为寻路对象的位置到Path头尾两点的和。
hCost这样算。
接下来是算法最关键的一步,获取Ope中fCost最小的节点作为当前节点,查看是否为终点;如果不是,找当前节点的相邻节点,也就是Path的nextPath。由于Path一定是可走的,所以没有traversable检查。对于不在Closed以及不在Open中的相邻节点,设置lastNode为父节点,计算fCost然后加入Open,当前节点移除到Closed,继续挑Open中最合适的节点,直到找到终点。
可以看到,没有考虑某个相邻节点在Open中但是路径更短的情况,估计是不考虑也行。
当DoBfsSteps执行完毕返回true,说明到endTile的路径找到了,但是endTile中要走哪条路径由那条路径距离终点最近决定。GetPathList要做的就是一层层看lastNode,最后将PathList反转就是我们需要的路径。
检查点路径通过一个给定的checkPoint队列中前后两元素之间的寻路路径相拼接得到。其他的大差不差。
接下来的计划: