Lua生成随机迷宫的两种方案


前言

公司目前在开发一款迷宫类的游戏,我负责在迷宫中行走的部分。
这次的任务是写一个随机迷宫,之前自己想了几个方案以及公司之前的迷宫方案,发现都不是很合适,于是就上网找了找随机迷宫的生成,在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的再来看我的也行

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值