九宫格拼图

文章详细介绍了如何使用Unity结合Lua编程实现一个3x3拼图游戏。首先通过随机算法生成并验证可还原的数字序列,然后初始化界面和数据,接着定义判断移动逻辑以及执行移动操作,最后验证游戏是否成功。整个过程涉及随机数生成、数组操作和逻辑判断等技术。
摘要由CSDN通过智能技术生成

这边是用unity和lua做的,但是数据处理方式都差不多

思路:先随机,再初始化界面和数据,判断能否移动,最后验证成功

1.先随机一串数字,比如3*3,就随机1-8,9固定空位,还得验证一下随机的数组是否能拼好(逆序数是基数就拼不好,偶数就可以,第一次写的时候还不知道...)

什么是逆序数:“1,4,3,2”的逆序数是3,这边我从后往前看,2的前面比它大的有2个,3的前面比他大的有1个, 然后4和1前面没有比他大的,所以2+1+0+0就是3,这个时候任意调换相邻的两个位置,基数就变偶数了

self.rowNum = 3    --总行数
self.colNum = 3    --总列数
self.pCount = self.rowNum * self.colNum
self.noneId = self.pCount    --用来保存空的这个位置
self.randomArr = {}    --用来保存打乱的数组

--- 打乱逻辑
function RandomPuzzleIndex()
    self.randomArr = {}
    for i = 1, self.pCount do    --3*3,生成9个数据的数组
        table.insert(self.randomArr, i)
    end
    for i=1, #self.randomArr do   --乱随机
        local index = math.random(i, #self.randomArr)
        if i ~= index and index ~= self.pCount then --最后一张拼图空着不纳入随机
            local temp = self.randomArr[index]
            self.randomArr[index] = self.randomArr[i]
            self.randomArr[i] = temp
        end
    end
    if not self:IsReducible(self.randomArr) then
        --这边可以调换相邻下标的任意两个,因为空块固定,所以不要调换空块
        local temp1 = self.randomArr[self.pCount-1]
        self.randomArr[self.pCount-1] = self.randomArr[self.pCount-2]
        self.randomArr[self.pCount-2] = temp1
    end
end

--- 判断随机出来的数组是否可还原(逆序数为偶数可还原)
function IsReducible(arr)
    local count = 0
    for i = #arr, 1, -1 do
        if i ~= 1 then
            for j = i-1, 1, -1 do
                if arr[j] > arr[i] then
                    count = count + 1
                end
            end
        end
    end
    local num1,num2 = math.modf(count/2)
    if num2 == 0 then
        return true
    else
        return false
    end
end

2.然后是加载和赋值(初始化拼图和数据)

加载的方式就不贴代码了,每个项目加载的方式都不一样,我用的是Unity,初始化的时候加了个布局组件GridLayoutGroup,等初始化结束之后,就把这个组件关掉了,因为我用的是交换坐标的方式去做移动,这种方式后期加动画会很简单

代码段,初始化每个格子的数据,接口传了3个值,obj用来记录和交换位置用,index就是传过来的下标0-9顺序(用来计算行列),id就是上面随机出来的值(不会变的唯一Id),数据里面的row和col就是记录每个格子当前的行和列,index最后用作判断是否成功的

--- 初始化数据
function InitPuzzleData(obj, index, id)
    table.insert(self.curPuzzleData, {
        id = id,
        obj = obj,
        row = math.ceil(index/self.colNum),
        col = index % self.colNum == 0 and self.colNum or index % self.colNum,
        index = index,
    })
end

3.判断能否移动和移动(交换两个块的位置和数据)

--- 判断能否移动
function CanMove(id)
    local clickP = self:GetChuckDataById(id)
    local noneP = self:GetChuckDataById(self.noneId)
    if (math.abs(clickP.row - noneP.row) == 1 and clickP.col == noneP.col)
     or (math.abs(clickP.col - noneP.col) == 1 and clickP.row == noneP.row) then
        return true
    end
    return false
end

--- 这边是移动逻辑,改变两个块的位置,交换他们的行列和下标(下标是判断成功时候用)
function View:StartMove(id)
    local clickP = self:GetChuckDataById(id)
    --获取空块的数据,self.noneId初始化的时候就记录了
    local noneP = self:GetChuckDataById(self.noneId)
    local tempP = Clone(clickP)--这个是我们内部函数,赋值后改变clickP的值不会影响tempP
    --TODO:这边后面也可以用动画
    local x,y,z = clickP.obj.position.x, clickP.obj.position.y, clickP.obj.position.z

    clickP.obj.position = noneP.obj.position
    clickP.row = noneP.row
    clickP.col = noneP.col
    clickP.index = noneP.index

    noneP.obj.position = Vector3(x,y,z)
    noneP.row = tempP.row
    noneP.col = tempP.col
    noneP.index = tempP.index
end
--- 通过Id获取格子的数据
function GetChuckDataById(id)
    for i = 1, #self.curPuzzleData, 1 do
        if self.curPuzzleData[i].id == id then
            return self.curPuzzleData[i]
        end
    end
end

4.最后验证结果

这块最后就很简单了,移动完之后判断是否成功,只需要判断每个块的index是否等于id就可以了

function JudgeIsSuccess()
    for i = 1, #self.curPuzzleData, 1 do
        if self.curPuzzleData[i].index ~= self.curPuzzleData[i].id then
            return false
        end
    end
    return true
end

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值