zoukankan      html  css  js  c++  java
  • 客户端地图内寻路总结与优化

    首先关于客户端的坐标体系:

    菱形框是客户端使用的单位方格,也就是游戏里雷达显示的坐标。客户端中采用的等距视角,使用菱形方格能与平面的场景地图模拟出3D效果。红色矩形框则是客户端和服务端公用的坐标格。

    寻路方法入口: bool StartFindPath(CPos start, CPos end, vector<Cvector2f>& path, int IgnoreSteps, int nRatio, bool bAnyDir, int nMaxStep)

    (下面讲解具体的寻路实现时涉及到A*寻路和Dijkstra最短路算法的知识点,这里就不对这两者做详细介绍了。)

    start和end寻路的起点和终点,均为像素点。path为引用参数,用于存储寻路结果。IgnoreSteps为忽略距离终点的格数,即离终点还有几格时即可停下。nMaxSteps是最大寻路步数,用于限制使用A*算法寻路时的超时时间。

    寻路成功返回true,将线路的“拐点集”(即相邻两点间是可以直线连通没有障碍的,且任意非相邻的两点是不能直线连通的)存储在path中。否则返回false。

    StartFindPath的寻路过程:

      1.先用LinePath()检验起点与终点是否可以直线连通:

        (1)LinePath需保证起点在场景内;终点参数在场景外则直接返回起点坐标。

        (2)然后由起点和重点坐标得出直线方向,以最小单位(即上面说的菱形格)遍历从起点至终点的所有格判断是否有高障碍(高障碍一般为地形,人物角色和怪物一般为低障碍),成功返回true;否则返回遇到的第一个障碍格。

      2.若直线寻路没有成功,接下来检查输入参数的起点和终点是否均不超过场景。

      3.进入A*寻路算法FindPath():

        (1)检查如果起点和终点相同则直接返回-1。

        (2)将起点添加进open list中。

        (3)循环执行下面操作直至open list为空找到终点节点 超出最大寻路步数

          I. 在open list中找到并移除总花费值最小的节点(总花费=G+H;G为从起点到该节点的花费;H为从该点到终点的估计花费。H这里采用的是“曼哈顿距离”来估计从该节点到终点的花费,公式为横纵坐标差值的和:H=abs(start.x-end.x)+abs(start.y-end.y) )。

          II. 在该处理节点的八个相邻方向上遍历,对于到达的新节点若没存入过open list则直接加入;否则检查由该处理节点到新节点的G是否更小,如果更小就更新其花费和前置节点;并重新调整open list的顺序。

    实现该A*算法的过程中三个重要的数据结构:

        (1)     XPS_Node:存储位置节点的结构,包含了节点位置、G花费、H花费和到达该节点的前置节点等内容。

        (2)     XPS_Node** m_pOpenList:用数组实现的、以总花费做权值比较的最小堆。

        (3)     XPS_Node* m_aBacket:用于记录加入过open list的XPS_Node哈希表;用x、y坐标值计算哈希值;解决哈希冲突的方式是将哈希值相同的XPS_Node以链表结构存储在同一数组下标上。

      4.如果A*寻路没有成功,则进入下面的Dijkstra最短路算法:

        (1)     加载对应地图数据目录下的roadPoint.csv和pathlink.csv(这两个文件是在地图上预设好的各坐标顶点和相邻顶点间的距离),构建出Dijkstra算法需要的图。

        (2)     找到距离start和end最近的且能够与之直线连通的顶点。将该两个顶点作为Dijkstra图中的起点和终点。

        (3)     循环N次执行以下操作:

          I. 找出到达花费最小的节点node,并作标记 使下次查找时忽略该点。

          II. 对于所有与node节点相邻的顶点,检查由node到其相邻节点的到达花费是否更小( cost[node] + link[node][i] < cost[i] ),如果是则更新其到达花费,并将其前置节点置为node。

      5.若Dijkstra寻路未成功(没有csv文件或找不到与起/终直连的顶点等),则进行大范围A*寻路(这一步往往耗时就很严重了玩家会有明显的寻路延迟)。

      (如下示,虚线为起始点寻找能够直线连通的顶点;蓝色实线为Dijkstra得出的路径)

    对寻路代码做的优化:

     

      1.对Dijkstra算法使用堆优化:

    原先实现中,每次寻找最小花费的节点步骤中,是通过遍历一次所有节点得出的。可以用个最小堆来存储节点的到达花费,那么每次寻找最小花费节点的复杂度由O(N)降为O(logN)。 (我这里实现最小堆是直接用的std的优先队列priority_queue)。

      2.在使用Dijkstra算法寻路时,第一步是遍历图中的所有顶点中,找到能够与起点、终点位置直线连通的顶点。因为需要直线连通这一条件,因此在起/终点周围存在较多障碍格的时候很容易失效,而进入下面非常耗时的大范围A*寻路。因此将这一步改为:当直线连通失效时,则寻找与起/终点直线距离最近的若干顶点使用A*算法(超时步数限制在较小范围),来确定Dijkstra的图中起点和终点。

    下面给出巨**谷地图(还是把游戏里具体的地图名遮上吧 :D)上的一些测试结果:

    起点

    终点

    原始耗时

    优化后

    (79,42)

    (570,1097)

    151ms

    26ms

    (17,1104)

    (538,13)

    114ms

    32ms

    (246,47)

    (625,688)

    97ms

    23ms

    (577,808)

    (295,584)

    31ms

    29ms

    (36,105)

    (419,910)

    39ms

    39ms

    (625,373)

    (6,203)

    28ms

    27ms

    上述优化有些限制,就是需要寻路地图要要有相应的csv地图,并且有数量可观的结点,否则起不到较好的优化效果。而且第一步的Dijkstra算法的堆优化的实际作用并不大,毕竟数据量有限。

  • 相关阅读:
    DL/T 467-2019 电站磨煤机及制粉系统性能试验
    fidlder-05(拦截并修改数据)
    fiddler-04(怎么对APP抓包)
    Redis5设计与源码分析读后感(二)简单动态字符串SDS
    Jedis连接搭建在阿里云服务器上的Redis,基于Linux(CentOS7)
    centos7下安装redis6.0版本+3种启动方式
    Linux下端口被占用的解决方法
    Linux卸载Nginx
    linux中普通用户修改密码出现(passwd:Authentication token manipulation error)
    linux重置密码提示与用户名相似该怎么解决?
  • 原文地址:https://www.cnblogs.com/geek1116/p/12122074.html
Copyright © 2011-2022 走看看