A*寻路算法的lua实现

前言:又好久没写blog了,感觉有点“颓废”了,最近认识好多好多同龄人,也是大学刚毕业,觉得他们很优秀,认识到自己跟他们的差距,有点自愧不如。没写blog当然也有一部分原因是因为工作,本来经验就有点欠缺,还要承担起一个项目的压力,原本国庆回去就要把这个寻路的功能改进一下,结果第一次去女朋友家了就没碰电脑,回上海来的第一个夜晚满脑子全是心事,早上凌晨四点就在床上辗转睡不着了,这个月随着项目的进行感觉压力也越来越大,上班时期天天六点多就醒了睡不着,希望挺过这段适应期。关于寻路问题,在几个月之前一次面试就碰到,面试官叫我上机写出这个算法,由于之前没看过A*,只是有这个概念,所以只能凭着自己的思路来临时编写。最后憋出了一段后来主动跟面试官说名可以用A*算法来实现,最后还是我的坚持打动了面试官。今天又碰到同样的问题,也让我纠结了一两天,再次写下寻路的学习心得。

一、问题概述

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

当碰到这个问题的时候,问老大选择用什么寻路算法,他推荐的是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类

module('Point', package.seeall)
-- require("script/battle/BattleCommon")
--计算F值
function CalcF( point )
	point.F = point.G + point.H
end

function create( posId )
	local point = {}
	point.parentPoint = {}
	point.step = 1 --用于计算h值
	local x,y = BattleCommon.convertPosIdToCoord(posId)
	point.F = 0
	point.G = 0
	point.H = 0
	point.X = y            --point.X范围[1,5]
	point.Y = x            --point.Y范围[1,8]
	point.posId = posId
	point.CalcF = CalcF

	return point
end

return Point

地形(Maze)结构

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

六、主要代码

module('Maze', package.seeall)
require("script/battle/TabledArray")
require("script/battle/BattleCommon")
require ("script/battle/Point")

-- --获取列表中F值最小的点
function getMinPoint( pointsList )
	local minPoint = pointsList.tbl[1]
	for i = 1,pointsList:getSize() do
		if minPoint.F > pointsList.tbl[i].F then
			minPoint = pointsList.tbl[i]
		end
	end
	return minPoint
end

-- --检测是否有阻挡,没有阻挡为true
function checkBlock( maze,x,y,roleFlag)              --x范围[1,5]  y范围[1,8]
	if roleFlag == BattleCommon.BATTLE_GROUP_ALLIES then --我方阵营
		if maze.mazeArray[x][y][1] == 0 or maze.mazeArray[x][y][1] == 1 then
			return true  --没有阻挡
		else
			return false
		end
	elseif roleFlag == BattleCommon.BATTLE_GROUP_ENEMY then
		if maze.mazeArray[x][y][1] == 0 or maze.mazeArray[x][y][1] == 2 then
			return true  --没有阻挡
		else
			return false
		end
	end
end

-- --列表中是否包含x,y的点
function existsXY( list,x,y )
	if list:getSize()>0 then
		for i,point in pairs(list.tbl) do
			if point.X == x and point.Y == y then
				return true
			end
		end
	end
	return false
end

--列表中是否包含某个点
function existsPoint( list,point )
	for i, p in pairs(list.tbl) do
		if (p.X == point.X) and (p.Y == point.Y) then
			return true
		end
	end
	return false
end

-- --检测能达到的点
function canReach( maze,startPoint,x,y,roleFlag)
	if (not checkBlock(maze,x,y,roleFlag)) or  existsXY(maze.closeList,x,y) then --关闭列表中包含这个点或者有阻挡
		return false
	else
		if (math.abs(x-startPoint.X)+math.abs(y-startPoint.Y) == 1 ) then
			return true
		end
	end
end

-- --获取相邻的点
function getSurroundPoints( maze,point,roleFlag )
	local surroundPoints = TabledArray.create()
	for i = point.X - 1 ,point.X + 1 do
		for j=point.Y - 1,point.Y + 1 do
			if i>0 and i<6 and j > 0 and j < 9 then  --排除超过表姐
				if BattleCommon.distanceFromTo(point.posId,BattleCommon.convertToPositionId(j-1,i-1)) < 2 then
					if canReach(maze,point,i,j,roleFlag) then
						surroundPoints:append(maze.mazeArray[i][j][2])
					end
				end
			end
		end
	end
	return surroundPoints   --返回point点的集合
end

-- --计算G值
function CalcG( point )
	local G = point.G
	local parentG = 0
	if point.parentPoint then
		parentG = point.parentPoint.G
	end
	return G + parentG
end

function foundPoint( tempStart,point )
	local G = CalcG(point)
	if G < point.G then
		point.parentPoint = tempStart
		point.G = G
		point:CalcF()
	end
end

function notFoundPoint( maze,tempStart,point )
	point.parentPoint = tempStart
	point.G = CalcG(point)
	point:CalcF()
	maze.openList:append(point)
end

function getPoint( list,data )
	for i,point in pairs(list.tbl) do
		if point.posId == data.posId then
			return point
		end
	end
	return nil
end

-- --寻找路径(起始路径)
local function findPath( maze,startPoint,endPoint,roleFlag)
	maze.openList:append(startPoint)
	while maze.openList:getSize() ~= 0 do
		--找出F的最小值
		local tempStart = getMinPoint(maze.openList)
		maze.openList:removeById(1)
		maze.closeList:append(tempStart)
		--找出它相邻的点
		local surroundPoints = getSurroundPoints(maze,tempStart,roleFlag)
		for i,point in pairs(surroundPoints.tbl) do
			if existsPoint(maze.openList,point) then
				--计算G值,如果比原来大,就什么都不做,否则设置他的父节点为当前节点,并更新G和F
				foundPoint(tempStart,point)
			else
				--如果他们不再开始列表里面,就加入,并设置父节点,并计算GHF
				notFoundPoint(maze,tempStart,point)
			end
		end
		--如果最后一个存在则返回
		if getPoint(maze.openList,endPoint) then
			return getPoint(maze.openList,endPoint)
		end
	end
	return getPoint(maze.openList,endPoint)
end

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

return Maze

调用方法

function printPath( presentPoint )
	local pathArray = TabledArray.create()
	while presentPoint do
		pathArray:preppend(presentPoint.posId)
		presentPoint = presentPoint.parentPoint
	end
	local startPoint = pathArray:get(2)
	local endPoint = pathArray:getLast()

	cclog(startPoint)
	cclog(endPoint)
	cclog("从"..startPoint.."到"..endPoint.."的路径是:")
	for i,p in pairs(pathArray.tbl) do
		cclog(p)
	end
end

	local array = battleBoard:createBoxPoints(cRole:getFlag(),40)
	local maze = Maze.create(array)
	local startPoint = Point.create(cRole:getPositionId())
	local endPoint = Point.create(40)
	local presentPoint = maze:findPath(startPoint,endPoint,cRole:getFlag())
	printPath(presentPoint)

七、运行效果

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

时间: 2024-10-31 05:05:51

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

ActionScript 3.0(as3)实现的A*寻路算法源代码下载

曾经写过A*寻路算法的教程,但没有贴出任何代码,这次代码全都贴出,大家可以进一步学习我只是按照A*的基本思想,将A*寻路算法用AS3实现了一下,没有做太多的寻路优化等如果你本身已经会用A*寻路算法了,那么下面的代码也不必研究了代码中注释很多,就不贴过多的解释看代码以前,可以先看看下面这两篇文章,或者看代码的时候配合例子和教程来看A*(A星)寻路算法讲解A*寻路算法,DEMO展示在DEMO展示中,有三个版本,由于代码写了很久了,我也不记得下面贴出的代码是哪个版本的了,见谅.. 首先是文档类Inde

深入理解游戏中寻路算法

如果你玩过MMOARPG游戏,比如魔兽,你会发现人物行走会很有趣,为了模仿人物行走的真实体验,他们会选择最近路线达到目的地,期间会避开高山或者湖水,绕过箱子或者树林,直到走到你所选定的目的地. 这种看似寻常的寻路在程序实现起来就需要一定的寻路算法来解决,如何在最短时间内找到一条路径最短的路线,这是寻路算法首先要考虑的问题. 在这篇文章中我们会循序渐进来讲解寻路算法是如何演进的,你会看到一种算法从简单到高效所遇到的问题,以及精进的过程,带着问题来阅读,理解更快. 本篇主要包含以下内容: 1.图 2

A*寻路算法入门(一)

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

如何在Cocos2D游戏中实现A*寻路算法(一)

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

寻路算法:找到NPC最好的行走路径

本文将从搜索空间,可接受的启发式算法.贪婪最佳优先算法进行探讨. 搜索空间的表示 最简单的寻路算法设计就是将图作为数据结构.一个图包含了多个节点,连接任意邻近的点组成边.在内存中表示图有很多种方法,但是最简单的是邻接表.在这种表示中,每个节点包含了一系列指向任意邻近节点的指针.图中的完整节点集合可以存储在标准的数据结构容器里.下图演示了简单的图的可视化形象和数据表示. 这意味着在游戏中实现寻路的第一步是如何将游戏世界用图来表示.这里有多种方法.一种简单的方法就是将世界分区为一个个正方形的格子(或

A*寻路算法入门(二)

大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流之用,请勿进行商业用途.同时,转载时不要移除本申明.如产生任何纠纷,均与本博客所有人.发表该翻译稿之人无任何关系.谢谢合作! 一只寻路的猫咪(本猫猪也是路痴,猫猪注.) 让我们想象我们有一个游戏,游戏中一只猫咪想要找到得到骨头的路径. "为毛一只在世界上的猫咪想要一根骨头呢?!"你可能会这

如何在Cocos2D游戏中实现A*寻路算法(八)

大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流之用,请勿进行商业用途.同时,转载时不要移除本申明.如产生任何纠纷,均与本博客所有人.发表该翻译稿之人无任何关系.谢谢合作! 就拿上图中左上角的情况举个栗子. 这只猫咪想要从原点(O)到左下角的对角线方块中去.如果在左边或底下(或全部都有)有墙壁并且测试穿过对角线将会切入一个墙角(或2个).所以左下

如何在Cocos2D游戏中实现A*寻路算法(六)

大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流之用,请勿进行商业用途.同时,转载时不要移除本申明.如产生任何纠纷,均与本博客所有人.发表该翻译稿之人无任何关系.谢谢合作! 然而,就像你在玩时发现的那样,你将看到一堆的问题: 这只猫咪看起来有点僵硬 这只猫咪并没有将骨头拿走 这只猫咪可以直接穿过狗狗(即使没有任何骨头)而不被咬 如果你在前一个移动

赫赫大名的A*寻路算法(vb.net版本)_实用技巧

在网上看到一篇A*寻路算法的译文 http://data.gameres.com/message.asp?TopicID=25439 按此原理写了以下程序 另外补充:1.此算法不是最短路径算法.        2.在实际应用中肯定还需要优化,以适合具体游戏.        3.(vb.net2005测试通过) /Files/bearhunter/6f1e1005-a5a3-4fc9-9bfe-99a615e113ed.rar 本地下载