1. 程式人生 > >cocos2dx 製作一個簡單的三消遊戲

cocos2dx 製作一個簡單的三消遊戲

很多時候在學習或者嘗試做遊戲的時候總是會無疾而終,現在把自己的收穫和做出來的示例記下來給自己一個督促吧。

平臺環境: cocos2dx(3.8)+lua vs2012

關於三消遊戲的認識

基礎的三消遊戲是在一個二維空間上,放置不同的元素,通過滑動消除相鄰的幾個相同元素,來獲得積分或過關。當然完整的上線專案會有很多玩法,像是一次消除多個會產生一個炸彈,或者地圖上有各種的障礙,甚至有的遊戲像best friends是通過連線臨近相同元素來進行消除。

期望的效果

這次的一個小嚐試期望做出的效果只是在一個二維的空間中,隨機幾種不同顏色的元素進行填充,接受玩家的滑動操作,能夠檢測相鄰三個以上的相同元素進行消除,並且判斷當無法有滑動消除的情況時,會隨機交換幾個元素的位置重新繼續。暫時不包含其他的玩法。

系統設計

需要的物件

  1. 二維介面中的元素,我命名為物件SXBlock,本身儲存有所處的行和列,擁有一個sprite,並且實現了sprite需要的各個動畫效果;
  2. 遊戲世界,在cocos2dx中我以一個layer實現,儲存了二維的陣列儲存所有SXBlock,同時監聽玩家的輸入,控制遊戲狀態的轉換,同時遊戲的大部分演算法都在這裡實現。

遊戲中的各個狀態

我把整個三消流程分為四個狀態:

  1. 填充:當二維陣列有空位時,其他的元素往下移,同時從上方隨機出新的元素掉落進行填充。
  2. 檢測是否滿足消除條件並消除。
  3. 接受玩家輸入,並表現輸入動畫:只有在此狀態下玩家的操作才有效果。
  4. 檢測是否無法再消除,是的話隨機交換幾個元素的位置。

狀態之間的轉換如下圖示:
這裡寫圖片描述

演算法需求

在實現過程中,考慮到的一些核心的演算法總共有三個:

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. 空間填滿元素後是否結束,即無法再消除;

符合的條件如下所示,當如下相鄰位置元素型別一樣時,可以消除

這裡寫圖片描述

判斷邏輯如下:

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<lvarNum) do
        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程式碼在此下載