寻路

从 NavMesh 网格寻路回归到 Grid 网格寻路。

http://www.cnblogs.com/yaukey/p/rts_unit_traverse_size_based_path_finding.html

上一个项目的寻路方案是客户端和服务器都采用了 NavMesh 作为解决方案,当时的那几篇文章()是很多网友留言和后台发消息询问最多的,看来这个方案有着广泛的需求。但因为是商业项目,我无法贴出代码,只能说明下我的大致思路,况且也有些悬而未决的不完美的地方,比如客户端和服务器数据准确度和精度的问题,但是考虑到项目类型和性价比,我们忽略了这个点。

从今年5月份开始为期一个月,我的主要工作是为新项目寻找一个新的寻路方案。新项目是一个 RTS 实时竞技游戏,寻路要求是:每个寻路单位之间的碰撞精确,不能出现不正确的拥挤和穿插,并且单位大小和所在的任何位置,都会影响到其他活动单位的通路性选择,看起来就是一个典型的 RTS 游戏寻路,和红警,星际等这些游戏非常像。

项目最初的阶段为了先快速迭代功能,使用了 Unity 内建的 NavMesh 系统,当单位停下使用 NavMeshObstacle 在地上挖个坑来影响通路性,但是经常会出现两个建筑型的单位中间会有个小缝,虽然缝很细,但是也是可以走的,而很可能一个半径巨大的单位直接就试图传过去,结果是被卡在这里。如果给单位的半径很小就可以穿过去,但是感觉很怪,而且单位之间的穿插也会很明显,十分影响游戏的观感,更重要的是会影响游戏性。终于有一天,策划的同学们再也不能忍了,必须改掉!

在我看来我首先要解决的是能够将现有的或者寻找到一个能支持单位半径大小的寻路方案。但是这从一开始就排除掉了 Unity 的 NavMesh 的寻路方案,因为没有任何 api 提供给用户可供做类似的修改,通过修改 RecastNavigation 项目然后编译 Native 插件的方式我也否掉了,不划算,其实本质上是因为 NavMesh 系统无法提供我们需要的高精度要求,所以我把精力集中到寻找传统的 Grid 网格寻路上了。

过程中找到了一篇专门讲解不同单位通路性的文章:《Clearance-based Pathfinding and Hierarchical Annotated A* Search》,讲解深入详细,并且还配有源码。

这看起来似乎正好是满足我要求的东西,但是它有个致命的缺点:所有一切都是预先烘焙和计算的,如果有任何物体或者情况影响了原有的通路性,那么整个烘焙过程必须重新进行,按照作者的算法和流程,对于我们这种时刻都在改变整个地图通路性的情况来讲,完全不可能进行不断地实时计算,所以该方案最终还是放弃了。

后来朋友介绍了个很有意思的项目:Stratagus-GitHub - Wargus/stratagus: The Stratagus strategy game engine。这个项目很有意思,安装到手机后,直接把星际争霸1的资源导入,然后就可以在手机上玩星际争霸了,狂拽酷炫吊炸天。不过我安装并且导入星际资源到安卓手机后,一进入战斗场景就崩溃,试了很多次都不行,其实就是想看看它寻路的表现。好了不浪费时间,直接下载代码开始阅读,各种查找翻阅,最后看到了它是如何处理单位的大小和通路性的关键判断:位于 src/pathfinder/astar.cpp:CostMoveToCallBack_Default @line:506

1/*build-in costmoveto code*/2staticintCostMoveToCallBack_Default(unsignedintindex,constCUnit &unit)3{4#ifdef DEBUG5{6Vec2i pos;7pos.y = index /Map.Info.MapWidth;8pos.x = index - pos.y *Map.Info.MapWidth;9Assert(Map.Info.IsPointOnMap(pos));10}11#endif12intcost =0;13constintmask = unit.Type->MovementMask;14constCUnitTypeFinder unit_finder((UnitTypeType)unit.Type->UnitType);1516//verify each tile of the unit.17inth = unit.Type->TileHeight;18constintw = unit.Type->TileWidth;19do{20constCMapField *mf =Map.Field(index);21inti =w;22do{23constintflag = mf->Flags &mask;24if(flag && (AStarKnowUnseenTerrain || mf->playerInfo.IsExplored(*unit.Player))) {25if(flag & ~(MapFieldLandUnit | MapFieldAirUnit |MapFieldSeaUnit)) {26//we can't cross fixed units and other unpassable things27return-1;28}29CUnit *goal = mf->UnitCache.find(unit_finder);30if(!goal) {31//Shouldn't happen, mask says there is something on this tile32Assert(0);33return-1;34}35if(goal->Moving)  {36//moving unit are crossable37cost +=AStarMovingUnitCrossingCost;38}else{39//for non moving unit Always Fail unless goal is unit, or unit can attack the target40if(&unit !=goal) {41if(goal->Player->IsEnemy(unit) && unit.IsAgressive() && CanTarget(*unit.Type, *goal->Type)42&& goal->Variable[UNHOLYARMOR_INDEX].Value ==0&& goal->IsVisibleAsGoal(*unit.Player)) {43cost +=2*AStarMovingUnitCrossingCost;44}else{45//FIXME: Need support for moving a fixed unit to add cost46return-1;47}48//cost += AStarFixedUnitCrossingCost;49}50}51}52//Add cost of crossing unknown tiles if required53if(!AStarKnowUnseenTerrain && !mf->playerInfo.IsExplored(*unit.Player)) {54//Tend against unknown tiles.55cost +=AStarUnknownTerrainCost;56}57//Add tile movement cost58cost += mf->getCost();59++mf;60}while(--i);61index +=AStarMapWidth;62}while(--h);63returncost;64}

每次进行 AStar 寻路,计算寻路 Cost 的时候,都会走到这个回调函数,请注意以上代码中每个 Unit(建筑和可活动单位)都有一个TileWidth,这个TileWidth就是寻路单位在地图上所占的一个正方形的宽度(如果使用长方形会极大的增加计算复杂度,因为要考虑旋转后占格的问题。)一次遍历这个正方形,看看每一个格子是否被任何单位设置了使用 Mask,如果没有就说明可通过,最终如果一个正方形内所有格子都没有被设置任何 Mask 说明该区域可走,对于移动中的物体,认为它所占的区域属于可走区域,但是会给予比普通可走区域更高的 Cost。所以看来原理很简单,就是在寻找 AStar 节点的时候要遍历该节点所占的每个格子看是否可以通过,条件变得更加严格。

这个方式非常适合我的需求,我决定采取这个方式来进行后一步工作。考虑到时间紧迫且对稳定性要求高,我不打算自己重新编写整个寻路算法和框架了,寻找一个成熟的合适的插件来进行后一步工作,经过试验考察,我选择了使用 Unity 上一个非常强大和成熟的插件A* Pathfinding Project,来进行扩展和升级以便达到我的要求。

我们在Unity Asset Store中购买了此插件(很贵:100刀),然后我针对 GridGraph 类型进行了深度的修改,增加单位的 TraverseSize 作为寻路的参数传入,以便影响寻路结果,这样不同单位的大小,在穿过缝隙时,就可能会有不同的路径,如下图,每一个寻路的 Node 设置为了0.5,大的单位半径0.5,小的单位半径0.25,(寻路计算最终是直径),前往同一个地点,有如下结果:

小的单位可以通过宽度为0.5的缝隙而大的不可以,只能通过最小为1.0大小的缝隙,于是我的最基本的需求得到满足。

接下来,我需要处理碰撞的问题,精细碰撞需要单位无论大小,速度,位置,都要准确的处理,他们只能在边缘接触,不能过于穿插。期初我试用了这个插件自带的 RVO 系统,但是精度真的不够,后来我决定采用一个比较诡异但十分凑效的方案:使用 Unity 的 NavMesh 系统的 NavMeshAgent 的 Detour 系统来实现碰撞,当初据 RecastNavigation 的作者说,Unity 深度改写了该系统的 Detour 系统,可能这就是直接的体现吧。也就是说在地图上生成一个没有任何阻挡的完整的 NavMesh 可走区域,但是不用来走寻路目的,只是为了使用NavMeshAgent的碰撞计算而已,真实的寻路结果是A* Pathfinding Project提供的。

以上所有需要的基础系统都已经完成,但这只是另一个开始,和项目的结合和调试过程也是一个不断地改进的过程,需要和策划的同学们不断地沟通和交流进行调校,经过一段时间的磨合,终于开始稳定的按照预期目标进行工作了。结项!

【Written by yaukey. 若无特殊说明本博客文章均为原创,转载请注明作者yaukey和本博客地址(http://www.cnblogs.com/yaukey/)。】

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,393评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,790评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,391评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,703评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,613评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,003评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,507评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,158评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,300评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,256评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,274评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,984评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,569评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,662评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,899评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,268评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,840评论 2 339

推荐阅读更多精彩内容