cocos2dx 制作一个简单的三消游戏

很多时候在学习或者尝试做游戏的时候总是会无疾而终,现在把自己的收获和做出来的示例记下来给自己一个督促吧。

平台环境: cocos2dx(3.8)+lua vs2012

关于三消游戏的认识

基础的三消游戏是在一个二维空间上,放置不同的元素,通过滑动消除相邻的几个相同元素,来获得积分或过关。当然完整的上线项目会有很多玩法,像是一次消除多个会产生一个炸弹,或者地图上有各种的障碍,甚至有的游戏像best friends是通过连接临近相同元素来进行消除。

期望的效果

这次的一个小尝试期望做出的效果只是在一个二维的空间中,随机几种不同颜色的元素进行填充,接受玩家的滑动操作,能够检测相邻三个以上的相同元素进行消除,并且判断当无法有滑动消除的情况时,会随机交换几个元素的位置重新继续。暂时不包含其他的玩法。

系统设计

需要的对象

  1. 二维界面中的元素,我命名为对象SXBlock,本身存储有所处的行和列,拥有一个sprite,并且实现了sprite需要的各个动画效果;
  2. 游戏世界,在cocos2dx中我以一个layer实现,存储了二维的数组存储所有SXBlock,同时监听玩家的输入,控制游戏状态的转换,同时游戏的大部分算法都在这里实现。

游戏中的各个状态

我把整个三消流程分为四个状态:

  1. 填充:当二维数组有空位时,其他的元素往下移,同时从上方随机出新的元素掉落进行填充。
  2. 检测是否满足消除条件并消除。
  3. 接受玩家输入,并表现输入动画:只有在此状态下玩家的操作才有效果。
  4. 检测是否无法再消除,是的话随机交换几个元素的位置。

状态之间的转换如下图示:
cocos2dx 制作一个简单的三消游戏_第1张图片

算法需求

在实现过程中,考虑到的一些核心的算法总共有三个:

1. 当前玩家滑动操作是否有效

根据玩家输入获得的两个相邻元素,判断交换位置后是否能够达到消除条件,lua代码实现如下:

function UIMainLayer:FunGetRowSameBlocks( row, col )
    local ltabBlocks = {}
    local block = self:FunGetBlock( row, col )
    if not block then return {} end

    table.insert( ltabBlocks, block )
    for i = col - 1, 1, -1 do
        if block:FunSameType( self:FunGetBlock( row, i ) ) then
            table.insert( ltabBlocks, self:FunGetBlock( row, i ) )
        else
            break
        end
    end
    for i = col + 1, NUM_COL do
        if block:FunSameType( self:FunGetBlock( row, i ) ) then
            table.insert( ltabBlocks, self:FunGetBlock( row, i ) )
        else
            break
        end
    end
    return ltabBlocks
end

function UIMainLayer:FunGetColSameBlocks( row, col )
    local ltabBlocks = {}
    local block = self:FunGetBlock( row, col )
    if not block then return {} end

    table.insert( ltabBlocks, block )
    for i = row - 1, 1, -1 do
        if block:FunSameType( self:FunGetBlock( i, col ) ) then
            table.insert( ltabBlocks, self:FunGetBlock( i, col ) )
        else
            break
        end
    end
    for i = row + 1, NUM_ROW do
        if block:FunSameType( self:FunGetBlock( i, col ) ) then
            table.insert( ltabBlocks, self:FunGetBlock( i, col ) )
        else
            break
        end
    end
    return ltabBlocks
end

-- 判断
 local ltabRowSame1 = self:FunGetRowSameBlocks( block1.mvarRow, block1.mvarCol )
 local ltabRowSame2 = self:FunGetRowSameBlocks( block2.mvarRow, block2.mvarCol )
 local ltabColSame1 = self:FunGetColSameBlocks( block1.mvarRow, block1.mvarCol )
 local ltabColSame2 = self:FunGetColSameBlocks( block2.mvarRow, block2.mvarCol )
 if #ltabRowSame1 >= 3 or #ltabRowSame2 >= 3 or #ltabColSame1 >= 3 or #ltabColSame2 >= 3 then
    -- 达到条件可以交换
 else
    -- 未达到
 end

2. 空间填满元素后是否结束,即无法再消除;

符合的条件如下所示,当如下相邻位置元素类型一样时,可以消除

cocos2dx 制作一个简单的三消游戏_第2张图片

判断逻辑如下:

function UIMainLayer:FunCheckIsFixCon3R2C( row, col )
    -- 5 6
    -- 3 4
    -- 1 2
    local ltabType = {}
    for r = row, row + 2 do
        for c = col, col + 1 do
            table.insert( ltabType, self:FunGetBlock( r, c ):FunGetType() )
        end
    end
    if not (ltabType[1] == ltabType[3] and ltabType[1] == ltabType[6]) then
        if not (ltabType[1] == ltabType[4] and ltabType[1] == ltabType[5]) then
            if not (ltabType[2] == ltabType[3] and ltabType[2] == ltabType[5]) then
                if not (ltabType[1] == ltabType[4] and ltabType[1] == ltabType[6]) then
                    if not (ltabType[2] == ltabType[3] and ltabType[2] == ltabType[6]) then
                        if not (ltabType[2] == ltabType[4] and ltabType[2] == ltabType[5]) then
                            return false
                        end
                    end
                end
            end
        end
    end
    return true
end

function UIMainLayer:FunCheckIsFixCon2R3C( row, col )
    -- 4  5  6
    -- 1  2  3
    local ltabType = {}
    for r = row, row + 1 do
        for c = col, col + 2 do
            table.insert( ltabType, self:FunGetBlock( r, c ):FunGetType() )
        end
    end
    if not (ltabType[3] == ltabType[4] and ltabType[3] == ltabType[5]) then
        if not (ltabType[2] == ltabType[4] and ltabType[2] == ltabType[6]) then
            if not (ltabType[1] == ltabType[5] and ltabType[1] == ltabType[6]) then
                if not (ltabType[2] == ltabType[3] and ltabType[2] == ltabType[4]) then
                    if not (ltabType[1] == ltabType[3] and ltabType[1] == ltabType[5]) then
                        if not (ltabType[1] == ltabType[2] and ltabType[1] == ltabType[6]) then
                            return false
                        end
                    end
                end
            end
        end
    end
    return true
end

function UIMainLayer:FunCheckIsEnd()
    for r = 1, NUM_ROW - 2 do
        for c = 1, NUM_COL - 1 do
            if self:FunCheckIsFixCon3R2C( r, c ) then
                return false
            end
        end
    end

    for r = 1, NUM_ROW - 1 do
        for c = 1, NUM_COL - 2 do
            if self:FunCheckIsFixCon2R3C( r, c ) then
                return false
            end
        end
    end
    return true
end

3. 无法消除时,随机交换几个元素位置达能够继续消除。

代码如下示:

function UIMainLayer:FunGetRandomGroups()
    local lvarNum = MAX_RANDOM_GROUP_WHEN_END * 2
    local ltabIndex = {}

    while(#ltabIndex
        local lvarIndex = math.random(1, NUM_ROW*NUM_COL)
        local lvarIsIn = false
        for k, v in pairs( ltabIndex ) do
            if v == lvarIndex then
                lvarIsIn = true
                break
            end
        end
        if not lvarIsIn then
            table.insert( ltabIndex, lvarIndex )
        end
    end

    local ltabBlocks = {}
    for k, v in pairs( ltabIndex ) do
        table.insert( ltabBlocks, self.mtabBlocks[math.floor((v-1)/NUM_COL)+1][(v-1)%NUM_COL+1])
    end
    return ltabBlocks
end

function UIMainLayer:FunRandomSwap()
    local function SwapGroup( ltabBlocks, lvarCover )
        for i = 1, MAX_RANDOM_GROUP_WHEN_END do
            local block1 = ltabBlocks[i*2 - 1]
            local block2 = ltabBlocks[i*2]
            self.mtabBlocks[block2.mvarRow][block2.mvarCol] = lvarCover and block2 or block1
            self.mtabBlocks[block1.mvarRow][block1.mvarCol] = lvarCover and block1 or block2
        end
    end

    local ltabRandomBlocks = self:FunGetRandomGroups()
    SwapGroup( ltabRandomBlocks, false )
    if not self:FunCheckIsEnd() then
        -- 交换位置
        return
    else
        -- 仍然不符合
       SwapGroup( ltabRandomBlocks, true ) 
    end
end

总结

这几天的尝试,实现了这个简单的三消demo,但是在玩法上并没有什么乐趣…

还有一个可能出现的问题如果总行,列比较少会造成如何随机都不能消除的bug,导致一直卡在第四个状态。

暂时到这里吧,有进一步完善的话后续添加。

完整的可玩demo和lua代码在此下载 。

你可能感兴趣的:(cocos2dx)