前言
公司目前在开发一款迷宫类的游戏,我负责在迷宫中行走的部分。
这次的任务是写一个随机迷宫,之前自己想了几个方案以及公司之前的迷宫方案,发现都不是很合适,于是就上网找了找随机迷宫的生成,在csdn上翻到一篇文章,写的很不错,列举了三种常用的随机迷宫生成方案,但是人家使用python写的,对于我这种python十窍只通了一窍的人来说确实有点吃力,于是就记录下自己用lua写的代码,防止忘记。
参考文章传送门:三大迷宫生成算法
一、准备工作
这部分是迷宫节点以及地图的的lua类,比较简单,仅做参考,实际使用的迷宫类可与这些节点做下转换即可,这里的节点仅做随机迷宫逻辑开发使用
---@class BHRandomMazeNode
---@field pos Vector2
---@field index number
---@field visit boolean 是否访问过
---@field path EOrientation[] 畅通的方向
---@field eventType number
---@field eventData string
BHRandomMazeNode = class()
---@class BHRandomMazeMapData
---@field width number 地图的宽
---@field height number 地图的高
---@field map BHRandomMazeNode[]
BHRandomMazeMapData = class()
function BHRandomMazeMapData:ctor()
self.width = 0
self.height = 0
self.map = {}
end
---@private 获取空的地图数据
---@param width number
---@param height number
---@return BHRandomMazeMapData
function RandomMazeHelper:GetNilMapData(width,height)
---@type BHRandomMazeMapData
local mapData = BHRandomMazeMapData.new()
mapData.width = width
mapData.height = height
mapData.map = self:InitMap(width,height)
return mapData
end
---@private 获取空的地图
---@param width number
---@param height number
---@return BHRandomMazeNode[]
function RandomMazeHelper:InitMap(width,height)
local map = {}
for i = 0, height - 1 do
for j = 1, width do
local node = BHRandomMazeNode.new()
node.pos = Vector2.new(j,i)
node.path = {}
node.visit = false
node.index = node.pos.x + node.pos.y * width
table.insert(map,node)
end
end
return map
end
二、Recursive backtracker ( 递归回溯,也是深度优先算法)
1.思路:
开始将所有的迷宫房间设置为未访问过,然后随机或者从第一个开始访问,四方向寻找未访问过的房间,将未访问过得房间入栈,并从找到的这几个未访问的房间单独入表,随机一个选择一个房间打通此房间的墙壁,然后进入此房间,将此房间入栈,然后从此房间开始寻找下一个房间,直到所有的房间都被访问过为止。这种算法是效率最低的算法,生成50*50的迷宫格子消耗的时间高达10828ms,但是个人觉得这个生成的迷宫是最好看的迷宫
代码如下(示例):
---@public 设置当前迷宫的房间数据
---@param mapData BHRandomMazeMapData
function BackTrackerMap:SetNodeVisit(mapData)
local randomNode = RandomMazeHelper:GetRandomNode(mapData)
---@type BHRandomMazeNode
local list = {randomNode}
while #list > 0 do
---@type BHRandomMazeNode
local curNode = list[#list]
curNode.visit = true--设置为已访问过
local nodes = self:GetNodeRoundState(curNode,mapData)--周边未被访问过的房间
local nodesCount = table.count(nodes)
if nodesCount > 0 then
local randomIndex = math.random(nodesCount)
local selectNode = nodes[randomIndex]
local dir = selectNode.pos - curNode.pos
for k, v in pairs(MazeHelper.Offset) do
if dir == v then
table.insert(curNode.path,k)
table.insert(selectNode.path, MazeHelper.NotOrientation(k))
table.insert(list,selectNode)
end
end
else
table.remove(list,#list)
end
end
end
2.随机Prim
这种方式具体思路与上面的方式类似,但是不同的是这次这个随机打通的是已探明的格子,上面那个是未探明的格子,这种方式效率很高,比上面那种方式效率高很多,但是迷宫看上去会更加凌乱,但是更加自然,路径也会更多
代码如下:
---@public 设置当前迷宫的房间数据
---@param mapData BHRandomMazeMapData
function PrimMap:SetNodeVisit(mapData)
local randomNode = mapData.map[1]--RandomMazeHelper:GetRandomNode(mapData)
---@type BHRandomMazeNode[]
local list = {randomNode}
table.containsValue()
local listIsContains = function(curList,index)
local is = false
for _, v in pairs(curList) do
if v.index == index then
is = true
break
end
end
return is
end
while #list > 0 do
local randomIndex = math.random(#list)
local curNode = list[randomIndex]
curNode.visit = true
table.remove(list,randomIndex)
---@type BHRandomMazeNode[]
local nodes = {}
for _, v in pairs(MazeHelper.Offset) do
local node = RandomMazeHelper:GetNodeDataForPos(v + curNode.pos,mapData)
if node and node.visit == true then
table.insert(nodes,node)
elseif node and node.visit == false then
if not listIsContains(list,node.index) then
table.insert(list,node)
end
end
end
local nodesCount = table.count(nodes)
if nodesCount > 0 then
local index = math.random(nodesCount)
local selectNode = nodes[index]
local dir = selectNode.pos - curNode.pos
for k, v in pairs(MazeHelper.Offset) do
if dir == v then
if not table.containsValue(curNode.path,k) then
table.insert(curNode.path,k)
end
local pathType = MazeHelper.NotOrientation(k)
if not table.containsValue(selectNode.path,pathType) then
table.insert(selectNode.path, pathType)
end
break
end
end
end
end
end
总结
个人比较懒,就实现了这两种方式,两种迷宫风格很明显各不相同,因为我们的随机迷宫最大就只是10*10的,所以我就没太在乎效率问题,剩下一种方式原博主说是效率最高的方式,但是个人不太喜欢那种迷宫风格,再加上我们的迷宫比较小,那种风格也不好体现,所以就没做(其实主要是懒),以上两种方式各有优劣,建议可以先看原博主的,如果看不懂python的再来看我的也行