LUACN论坛

 找回密码
 加入我们

QQ登录

只需一步,快速开始

搜索
热搜: YJWOW MagicStone BoL
查看: 17749|回复: 14

[综合] 【教程】【GC八段】魔兽自动寻路之二:最短路径算法LUA版本

[复制链接]
发表于 2016-1-23 20:38:59 | 显示全部楼层 |阅读模式
本帖最后由 托托 于 2016-2-3 21:32 编辑

对上篇帖子的回顾:
上次我发了一个帖子,关于如何自动躲避障碍物的方法,传送门,龙套、醉骚也提供了不少想法。

对这些想法进行研究之后,发现智能躲避障碍物有一个问题,采用之前提到的方法,只能躲避一些简单的障碍,对于那种迷宫式的障碍,很容易就会卡住。解决的办法就是需要知道魔兽的模型形状,然后采用A*算法就可以智能获取最短路径。——难点在于我不知道怎么获取地形,加上魔兽bt的模型,所以这条路暂时打住了。


本次主题:
最近尝试用了一下hb,主要是收菜,所以我就针对收菜来说,别的场景的也是类似。

根据人物在里面的行为,大概推测出hb是在要塞中先记录一些坐标,然后hb就通过在这些坐标之间移动,达到不会卡住的目的。如果不能理解这个,可以试想一下,要塞里面有N个梅花桩,你只能在梅花桩上走路的情形,走梅花桩是人肉就能做到的,很简单,而最短路径要解决的就是如何从你目前所在的位置到达目的坐标,例如从要塞里面到矿井。



拿上面这个图来举例子,每个橙色的圈代表一个坐标,坐标之间有线的代表这两个点是可见的(魔石用msII方法可以判断),线上的数字代表距离。如果两个点之间没有线,就代表不可见,也就是你走过去的话会卡住的。

假设我要从1走到5,最短路径就是1、3、4、5,我们的算法就是要计算出这个1、3、4、5。避免你的人物在几个点之间迷茫。

废话结束上代码:
最后一个方法是算法方法,算法是dijkstra。前面是我写的移动的例子。使用的时候只需要用m2方法就可以了,提前采集好中介点,然后m2矿洞,就自己过去了,m2钓鱼点就自己过去了,是不是很屌。

我试了一下可以用,大家可以试试看,有问题一起探讨~~
  1. way_frame = way_frame or CreateFrame("frame");
  2. MAXINT = 32767;

  3. --------------------------------------------------------------
  4. --移动到目标点
  5. --tar可以是坐标、坐标table、unit
  6. --mediators是中介点,可为空。为空的话就是简单的移动,加上中介点的话,可以使用这些点自动获取可用的最短距离进行移动
  7. --例如:m2("target")
  8. --例如:m2({1,2,3})
  9. --例如:local mediators={{1,1,1},{2,2,2}}; m2("target", mediators);
  10. --------------------------------------------------------------
  11. function move2(tar, mediators)
  12.         local xt, yt, zt;--target's point
  13.         if type(tar) == "table" then
  14.                 xt, yt, zt = tar[1],tar[2],tar[3];
  15.         elseif type(tar) == "string" and UnitExists(tar) then
  16.                 xt, yt, zt = ObjectPosition(tar);
  17.         end
  18.         
  19.         if mediators and #mediators>0 then
  20.                 local x0,y0,z0 = ObjectPosition("player");
  21.                 local path = getShortestPath({x0,y0,z0}, {xt,yt,zt}, mediators);
  22.                 moveAlongPositions(path);
  23.         else
  24.                 MoveTo(xt,yt,zt);
  25.         end
  26. end
  27. m2 = move2;

  28. --------------------------------------------------------------
  29. --按照固定的点移动
  30. --例如:list={{1,1,1},{2,2,2}};moveAlongPositions(list);
  31. --------------------------------------------------------------
  32. function moveAlongPositions(posList)
  33.         local i=1;
  34.         way_frame:SetScript("OnUpdate", function()
  35.                 if not msgv("way_time") or GetTime()-msgv("way_time")>0.1 then
  36.                         mssv("way_time",GetTime());
  37.                         
  38.                         local x0,y0,z0 = msGUL("player");--player's position
  39.                         local x1,y1,z1 = posList[i][1], posList[i][2],posList[i][3];--target's position
  40.                         if abs(x1-x0)>1 or abs(y1-y0)>1 then
  41.                                 MoveTo(x1,y1,z1);
  42.                         else
  43.                                 print("到达:",x1,y1,z1);
  44.                                 if i==#posList then
  45.                                         way_frame:SetScript("OnUpdate",nil);
  46.                                 else
  47.                                         i=i+1;
  48.                                 end
  49.                         end
  50.                 end
  51.         end);
  52. end

  53. --------------------------------------------------------------
  54. --获取最短路径点
  55. --例如:start={1,1,1}; target={5,5,5}; mediators={{3,3,3},{2,2,2}}; list=getShortestPath(start, target, mediators);
  56. --------------------------------------------------------------
  57. function getShortestPath(start, target, mediators)
  58.         local points = mediators or {};
  59.         table.insert(points,1,start);
  60.         table.insert(points,target);
  61.         
  62.         for _,v in ipairs(points) do print(v);end
  63.         
  64.         local pointsDiagram = {};
  65.         local i,j;
  66.         for i=1,#points,1 do
  67.                 local pd = {};
  68.                 for j=1,#points,1 do
  69.                         local dist = MAXINT;
  70.                         if i==j then
  71.                                 dist = 0;
  72.                         else
  73.                                 if not TraceLine(points[i][1], points[i][2], points[i][3]+1, points[j][1], points[j][2], points[j][3]+1, 0x10) then
  74.                                         dist = msGD(points[i], points[j]);
  75.                                 end
  76.                         end
  77.                         table.insert(pd, dist);
  78.                 end
  79.                 table.insert(pointsDiagram, pd);
  80.         end
  81.         
  82.         local path = getShortestPathIdx(1,#points,pointsDiagram);
  83.         
  84.         local resultPoints = {};
  85.         for i,v in ipairs(path) do
  86.                 table.insert(resultPoints, points[v]);
  87.         end
  88.         return resultPoints;
  89. end

  90. --------------------------------------------------------------
  91. --根据各点距离信息获得两点的最短路径
  92. --例如:local A={{0,2,3,MAXINT,MAXINT},{2,0,MAXINT,4,MAXINT},{3,MAXINT,0,2,4},{MAXINT,4,2,0,1},{MAXINT,MAXINT,4,1,0}};Dijkstra(1,5,A);
  93. --------------------------------------------------------------
  94. function getShortestPathIdx(startIdx, endIdx, pointsDiagram)
  95.         local dist={};--记录startIdx到各个点的距离
  96.         local prev={};--记录路径
  97.         local S={};--判断是否已存入该点到S集合中
  98.         local pointCount = #pointsDiagram;
  99.         local i;
  100.         local j;
  101.         
  102.         --init
  103.         for i=1,pointCount,1 do
  104.                 dist[i] = pointsDiagram[startIdx][i];
  105.                 S[i] = false;--初始都未用过该点
  106.                 if dist[i] == MAXINT then
  107.                         prev[i] = -1;
  108.                 else
  109.                         prev[i] = startIdx;
  110.                 end
  111.         end
  112.         dist[startIdx] = 0;
  113.         S[startIdx] = true; --起始点out
  114.         
  115.         --计算
  116.         for i=2,pointCount,1 do
  117.                 local mindist = MAXINT;
  118.                 local u = startIdx;
  119.                 --找出当前未使用的点j的dist[j]最小值
  120.                 for j=1,pointCount,1 do
  121.                         if not S[j] and dist[j]<mindist then
  122.                                 --u保存当前邻接点中距离最小的点的号码
  123.                                 u, mindist = j, dist[j];
  124.                         end
  125.                 end
  126.                 S[u] = true;
  127.                 for j=1,pointCount,1 do
  128.                         if not S[j] and pointsDiagram[u][j]<MAXINT then
  129.                                 --在通过新加入的u点路径找到离startIdx点更短的路径  
  130.                                 if dist[u] + pointsDiagram[u][j] < dist[j] then
  131.                                         dist[j] = dist[u] + pointsDiagram[u][j];--更新dist
  132.                                         prev[j] = u;--记录前驱顶点
  133.                                 end
  134.                         end
  135.                 end
  136.         end
  137.         
  138.         local result={endIdx};
  139.         i=endIdx;
  140.         while i ~= startIdx do
  141.                 i = prev[i];
  142.                 table.insert(result,1,i);
  143.         end
  144.         return result;
  145. end
复制代码


附赠一个录制坐标的方法,自动记录到魔兽目录下的_ys_points.txt文件中了,文件会自动生成。——这个是@醉骚原创的,我改写了一下。
  1. function record2file(unit)
  2.         unit = unit or "player"
  3.         if not UnitExists(unit) then
  4.                 print("unit not exists: "..unit)
  5.                 return;
  6.         end
  7.         local X, Y, Z = ObjectPosition(unit);
  8.         local folder = GetWoWDirectory();
  9.         
  10.         local filePath = folder.."\\_ys_points.txt";
  11.         WriteFile (filePath,"{" .. X .. "," .. Y .. ","  .. Z .. "},\n" ,true )
  12.         print (unit,X,Y,Z)
  13. end
复制代码


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?加入我们

x

评分

参与人数 1伸手费 +1000 收起 理由
老血 + 1000 很给力!

查看全部评分

回复

使用道具 举报

发表于 2016-1-23 21:57:13 | 显示全部楼层
老胡给力   请收下我的膝盖
回复 支持 反对

使用道具 举报

发表于 2016-1-23 22:39:12 | 显示全部楼层
这个很666666老胡66666666666666
回复 支持 反对

使用道具 举报

发表于 2016-1-24 17:42:25 | 显示全部楼层
这个叼炸天啊~~~~~~~~~~~~
回复 支持 反对

使用道具 举报

发表于 2016-1-24 20:09:19 | 显示全部楼层
这个太流弊了
回复 支持 反对

使用道具 举报

发表于 2016-1-25 10:16:36 | 显示全部楼层
向科学致敬!
回复 支持 反对

使用道具 举报

发表于 2016-2-20 09:30:28 | 显示全部楼层
老胡同志一看就是程序猿
回复 支持 反对

使用道具 举报

发表于 2016-3-14 22:24:34 | 显示全部楼层
非常吊的原创!很给力
回复 支持 反对

使用道具 举报

发表于 2016-8-18 12:33:02 | 显示全部楼层
测试下回帖速度
回复 支持 反对

使用道具 举报

发表于 2016-8-18 12:56:05 | 显示全部楼层
测试回帖速度
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 加入我们

本版积分规则

小黑屋|手机版|Archiver|LUACN论坛

GMT+8, 2024-4-25 06:02 PM , Processed in 0.079267 second(s), 34 queries , Gzip On, Redis On.

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表