A*寻路算法的lua实现

一、问题概述

游戏中有敌我双方,有四十个方格,当轮到我方武将行动的时候,要先显示出我方武将可以行动的方位,这个就涉及到我方武将的行动力的大小来决定,预先做出路径的预算。这里还要考虑敌方以及地标(例如:炸弹、势头)的阻挡,以及特殊方格对武将行动力的消耗以及敌方的间隔阻挡规则。

当碰到这个问题的时候,问老大选择用什么寻路算法,他推荐的是Dijstra算法,但我看了之后感觉还不是很适合我的需求,第一:我觉得Dijstra算法是有向图的最佳路径选择,节点之间路径长度必须先知晓,但我这里四十个方格如果要两两制定感觉稍微复杂,而且随着武将的移动,地图还是时时变化的,感觉更复杂。第二:Distra算法没有阻挡考虑,当然也可以记录若干路径然后考虑阻挡选择最优路径,我感觉也稍复杂。

然而我的第一反应该需求A*算法挺接近的,然后咨询老大,老大说A*方格之间距离必须要均等不然比较复杂。然后再咨询公司其他稍有经验同事,当然各有个的说法,什么深度、广度遍历,感觉没一个确定的说法。让我选择就纠结了一天,不敢轻易选择,如果稍有考虑不慎,说不定就要做几天白用工,但最后我还是坚持自己的想法用A*来实现。

二、关于A*

A*通用的计算公式F=G+H

G:表示从起点A移动到网格上指定方格的移动耗费(我这里没考虑斜方向移动),也可理解为节点的权重

H:表示从制定方格移动到终点B的预计耗费,这里一般就是计算距离*系数

三、寻路思想

1.从起点A开始,把它添加到"开启列表"

2.寻找A点可到到的周围四个点,这里可到达是指没有阻挡并且已经不再关闭列表中的节点,并把他们添加进开启列表,并设置他们的父节点为A

3.从开启列表中删除A点并添加进关闭列表中,关闭列表就是存放的不需要检测的方格节点

4.检查它所有相邻并且合一到达的方格,如果这些方格还不再开启列表里的话就把他们加入开启列表,计算这些方格的GHF值并设置它的父节点

5.如果某个相邻方格 D 已经在 "开启列表" 里了, 检查如果用新的路径 (就是经过C 的路径) 到达它的话, G值是否会更低一些, 如果新的G值更低, 那就把它的 "父方格" 改为目前选中的方格 C, 然后重新计算它的 F 值和 G 值 (H 值不需要重新计算, 因为对于每个方块, H 值是不变的). 如果新的 G 值比较高, 就说明经过 C 再到达 D 不是一个明智的选择, 因为它需要更远的路, 这时我们什么也不做

6.当发现开启列表中有终点目标方格的时候则说明路径已经找到。

关于详细的图解可以参考其他网友的详解,我这里就不详细写了。

四、找回路径

我们找到最后一个点的时候然后层层往之前找到他的父节点迭代到最后不为空结束

五、数据结构

Point类

[plain] view plaincopyprint?

  1. module(‘Point‘, package.seeall)
  2. -- require("script/battle/BattleCommon")
  3. --计算F值
  4. function CalcF( point )
  5. point.F = point.G + point.H
  6. end
  7. function create( posId )
  8. local point = {}
  9. point.parentPoint = {}
  10. point.step = 1 --用于计算h值
  11. local x,y = BattleCommon.convertPosIdToCoord(posId)
  12. point.F = 0
  13. point.G = 0
  14. point.H = 0
  15. point.X = y            --point.X范围[1,5]
  16. point.Y = x            --point.Y范围[1,8]
  17. point.posId = posId
  18. point.CalcF = CalcF
  19. return point
  20. end
  21. return Point

地形(Maze)结构

[plain] view plaincopyprint?

  1. --根据一个table创建一个地形
  2. function create( tb )
  3. local maze = {}
  4. maze.step = 1 --格子与格子的基本距离,用于计算H值
  5. maze.mazeArray = tb
  6. maze.openList = TabledArray.create() --开启列表
  7. maze.closeList = TabledArray.create() --关闭列表
  8. maze.findPath = findPath
  9. return maze
  10. end

六、主要代码

[plain] view plaincopyprint?

  1. module(‘Maze‘, package.seeall)
  2. require("script/battle/TabledArray")
  3. require("script/battle/BattleCommon")
  4. require ("script/battle/Point")
  5. -- --获取列表中F值最小的点
  6. function getMinPoint( pointsList )
  7. local minPoint = pointsList.tbl[1]
  8. for i = 1,pointsList:getSize() do
  9. if minPoint.F > pointsList.tbl[i].F then
  10. minPoint = pointsList.tbl[i]
  11. end
  12. end
  13. return minPoint
  14. end
  15. -- --检测是否有阻挡,没有阻挡为true
  16. function checkBlock( maze,x,y,roleFlag)              --x范围[1,5]  y范围[1,8]
  17. if roleFlag == BattleCommon.BATTLE_GROUP_ALLIES then --我方阵营
  18. if maze.mazeArray[x][y][1] == 0 or maze.mazeArray[x][y][1] == 1 then
  19. return true  --没有阻挡
  20. else
  21. return false
  22. end
  23. elseif roleFlag == BattleCommon.BATTLE_GROUP_ENEMY then
  24. if maze.mazeArray[x][y][1] == 0 or maze.mazeArray[x][y][1] == 2 then
  25. return true  --没有阻挡
  26. else
  27. return false
  28. end
  29. end
  30. end
  31. -- --列表中是否包含x,y的点
  32. function existsXY( list,x,y )
  33. if list:getSize()>0 then
  34. for i,point in pairs(list.tbl) do
  35. if point.X == x and point.Y == y then
  36. return true
  37. end
  38. end
  39. end
  40. return false
  41. end
  42. --列表中是否包含某个点
  43. function existsPoint( list,point )
  44. for i, p in pairs(list.tbl) do
  45. if (p.X == point.X) and (p.Y == point.Y) then
  46. return true
  47. end
  48. end
  49. return false
  50. end
  51. -- --检测能达到的点
  52. function canReach( maze,startPoint,x,y,roleFlag)
  53. if (not checkBlock(maze,x,y,roleFlag)) or  existsXY(maze.closeList,x,y) then --关闭列表中包含这个点或者有阻挡
  54. return false
  55. else
  56. if (math.abs(x-startPoint.X)+math.abs(y-startPoint.Y) == 1 ) then
  57. return true
  58. end
  59. end
  60. end
  61. -- --获取相邻的点
  62. function getSurroundPoints( maze,point,roleFlag )
  63. local surroundPoints = TabledArray.create()
  64. for i = point.X - 1 ,point.X + 1 do
  65. for j=point.Y - 1,point.Y + 1 do
  66. if i>0 and i<6 and j > 0 and j < 9 then  --排除超过表姐
  67. if BattleCommon.distanceFromTo(point.posId,BattleCommon.convertToPositionId(j-1,i-1)) < 2 then
  68. if canReach(maze,point,i,j,roleFlag) then
  69. surroundPoints:append(maze.mazeArray[i][j][2])
  70. end
  71. end
  72. end
  73. end
  74. end
  75. return surroundPoints   --返回point点的集合
  76. end
  77. -- --计算G值
  78. function CalcG( point )
  79. local G = point.G
  80. local parentG = 0
  81. if point.parentPoint then
  82. parentG = point.parentPoint.G
  83. end
  84. return G + parentG
  85. end
  86. function foundPoint( tempStart,point )
  87. local G = CalcG(point)
  88. if G < point.G then
  89. point.parentPoint = tempStart
  90. point.G = G
  91. point:CalcF()
  92. end
  93. end
  94. function notFoundPoint( maze,tempStart,point )
  95. point.parentPoint = tempStart
  96. point.G = CalcG(point)
  97. point:CalcF()
  98. maze.openList:append(point)
  99. end
  100. function getPoint( list,data )
  101. for i,point in pairs(list.tbl) do
  102. if point.posId == data.posId then
  103. return point
  104. end
  105. end
  106. return nil
  107. end
  108. -- --寻找路径(起始路径)
  109. local function findPath( maze,startPoint,endPoint,roleFlag)
  110. maze.openList:append(startPoint)
  111. while maze.openList:getSize() ~= 0 do
  112. --找出F的最小值
  113. local tempStart = getMinPoint(maze.openList)
  114. maze.openList:removeById(1)
  115. maze.closeList:append(tempStart)
  116. --找出它相邻的点
  117. local surroundPoints = getSurroundPoints(maze,tempStart,roleFlag)
  118. for i,point in pairs(surroundPoints.tbl) do
  119. if existsPoint(maze.openList,point) then
  120. --计算G值,如果比原来大,就什么都不做,否则设置他的父节点为当前节点,并更新G和F
  121. foundPoint(tempStart,point)
  122. else
  123. --如果他们不再开始列表里面,就加入,并设置父节点,并计算GHF
  124. notFoundPoint(maze,tempStart,point)
  125. end
  126. end
  127. --如果最后一个存在则返回
  128. if getPoint(maze.openList,endPoint) then
  129. return getPoint(maze.openList,endPoint)
  130. end
  131. end
  132. return getPoint(maze.openList,endPoint)
  133. end
  134. --根据一个table创建一个地形
  135. function create( tb )
  136. local maze = {}
  137. maze.step = 1 --格子与格子的基本距离,用于计算H值
  138. maze.mazeArray = tb
  139. maze.openList = TabledArray.create() --开启列表
  140. maze.closeList = TabledArray.create() --关闭列表
  141. maze.findPath = findPath
  142. return maze
  143. end
  144. return Maze

调用方法

[plain] view plaincopyprint?

  1. function printPath( presentPoint )
  2. local pathArray = TabledArray.create()
  3. while presentPoint do
  4. pathArray:preppend(presentPoint.posId)
  5. presentPoint = presentPoint.parentPoint
  6. end
  7. local startPoint = pathArray:get(2)
  8. local endPoint = pathArray:getLast()
  9. cclog(startPoint)
  10. cclog(endPoint)
  11. cclog("从"..startPoint.."到"..endPoint.."的路径是:")
  12. for i,p in pairs(pathArray.tbl) do
  13. cclog(p)
  14. end
  15. end

[plain] view plaincopyprint?

  1. local array = battleBoard:createBoxPoints(cRole:getFlag(),40)
  2. local maze = Maze.create(array)
  3. local startPoint = Point.create(cRole:getPositionId())
  4. local endPoint = Point.create(40)
  5. local presentPoint = maze:findPath(startPoint,endPoint,cRole:getFlag())
  6. printPath(presentPoint)

七、运行效果

手机测试效果还可以,貌似在255*255规模的方格规模上采用A*还是不会有太大的效率影响。如果需要交流欢迎联系。

时间: 2024-08-05 10:54:13

A*寻路算法的lua实现的相关文章

这是一个真正靠谱的寻路算法

绝对没有其他看起来高大上 给别人讲都将不明白的理论.管你 人工智能 啥的 ,还有一百度一大篇的a*算法 ,其实大部分文章的理论都是讲不通的 或者没有讲清楚 更别说代码.做事刨根问底的牛脾气又上来了. 两周前 偶然原因接触到了寻路算法 于是百度 找到了a* .讲来将去大概意思就是持续性的找离目标近的节点 并且走过的节点不能重复走.反正百度搜a*算法 一搜一大片.在很简单的障碍的情况下是没问题 : 但是拐个弯就不行了,不是找最近的么,我让你一直找最近的. 直接迂在里面出不来了: 期间我曾经想过为什么

A*寻路算法入门(一)

大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流之用,请勿进行商业用途.同时,转载时不要移除本申明.如产生任何纠纷,均与本博客所有人.发表该翻译稿之人无任何关系.谢谢合作! 该篇博客由iOS课程团队的Johann Fradj发布,他现在是一个全职开发iOS的开发者.他是Hot Apps Factory(其是App Cooker的创造者)的共同创建

A*寻路算法的优化与改进

提要 通过对上一篇A*寻路算法的学习,我们对A*寻路应该有一定的了解了,但实际应用中,需要对算法进行一些改进和优化. Iterative Deepening Depth-first search- 迭代深化深度优先搜索 在深度优先搜索中一个比较坑爹情形就是在搜索树的一枝上没有要搜的结果,但是却非常深,甚至深不见底,这样就根本搜索不到结果.为了防止这种情况出现,就出现了Iterative Deepening的思想. 迭代深化搜索(Iterative deepening search, IDS)或者

[转] A*寻路算法C++简单实现

参考文章: http://www.policyalmanac.org/games/aStarTutorial.htm   这是英文原文<A*入门>,最经典的讲解,有demo演示 http://www.cnblogs.com/technology/archive/2011/05/26/2058842.html  这是国人翻译后整理的简版,有简单代码demo,不过有些错误,讲得很清晰,本文图片来自这篇 http://blog.csdn.net/b2b160/article/details/4057

HTML5-A*寻路算法

设置起点 设置终点 设置障碍 清除障碍 允许斜向跨越 HTML5-A*寻路算法,布布扣,bubuko.com

RCP:gef智能寻路算法(A star)

本路由继承自AbstactRouter,参数只有EditPart(编辑器内容控制器),gridLength(寻路用单元格大小),style(FLOYD,FLOYD_FLAT,FOUR_DIR). 字符集编码为GBK,本文只做简单的代码解析,源码戳我 如果源码不全,可以联系本人. 算法实现主要有三: 1.Astar单向寻路 2.地图预读 3.弗洛伊德平滑算法 Astar寻路的实现: ANode minFNode = null; while (true) { minFNode = findMinNo

js实现A*寻路算法

这两天在做百度前端技术学院的题目,其中有涉及到寻路相关的,于是就找来相关博客进行阅读. 看了Create Chen写的理解A*寻路算法具体过程之后,我理解A*算法的原理,不得不说作者写的很好,通熟易懂,图片也做的很好,可见作者在这上面是花了心思的.如果让我写,我是写不来这么好的. 唯一的不足就是,因为我学的是js,因此最后给我的源码我是用不了的......因此才有自己写一篇的打算,方面学习js人的学习.然而前面的描述我就借用他的了,因为如果然我的表达能力实在是太渣了. 简易地图 如图所示简易地图

算法:Astar寻路算法改进,双向A*寻路算法

早前写了一篇关于A*算法的文章:<算法:Astar寻路算法改进> 最近在写个js的UI框架,顺便实现了一个js版本的A*算法,与之前不同的是,该A*算法是个双向A*. 双向A*有什么好处呢? 我们知道,A*的时间复杂度是和节点数量以及起始点难度呈幂函数正相关的. 这个http://qiao.github.io/PathFinding.js/visual/该网址很好的演示了双向A*的效果,我们来看一看. 绿色表示起点,红色表示终点,灰色是墙面.稍浅的两种绿色分别代表open节点和close节点:

ActionScript 3.0 实现的A*寻路算法源代码

首先是文档类Index.as: package code{ import flash.display.Sprite; import flash.text.TextField; import flash.text.TextFormat; public class Index extends Sprite{ private var road:Road; public function Index(){ stage.align = "TL"; stage.scaleMode = "