案頭山寨版2048—遊戲邏輯篇之移動方塊的架構

來源:互聯網
上載者:User

標籤:style   blog   class   code   java   ext   

         昨天看到部落格(www.richinmemory.com)的流量統計,居然還有一位朋友評論了,感動的滿眼都是淚啊!謝謝支援啊!為了使互動的朋友更方便的互動,今天我加了個能用微博等帳號登入評論的外掛程式。需要源碼的朋友可以直接發信到我的郵箱。猛戳之後(www.richinmemory.com)若覺得還過得去,可以嘗試收藏啊,親。有朝一日有幸流量穩定了,我就開始放棄這邊更新了,不過這個肯定還要很久很久才能達到。

二、案頭山寨版2048—遊戲邏輯篇之移動方塊

         這個小遊戲的基本邏輯就是:合并同類項。玩家通過上下左右鍵操縱遊戲方塊,然後操縱方向上所有相鄰相同的數位方塊會被合并,合并之後方塊上的數字會被更新並且改變顏色。

         對於邏輯推理這件事,我一直深深相信的就是“從1到2再到無窮”的方式,這還是我高中老師教會我的方法,對於任何一個有規則但是複雜的事務,先從簡單的開始總是不會錯的。於是,首先考慮只有兩個方塊在一個方向進行操作(我選擇的是“下”這個方向)的時候。根據在介面篇中的原則,所有的遊戲方塊都是被儲存在一個ItemBox的數組裡並且在程式的一開始就進行了初始化,所以,現在要處理的邏輯就是如何控制每個方格裡方塊的顯示與否。

         如果只有兩個遊戲方塊時,使用者按下了“下”方向鍵,要考慮的情況有兩種:一是兩個遊戲方塊不在一列,二是兩個遊戲方塊位於一列。首先思考一下情況一的行為模式應該是:兩個遊戲方塊都應該向下移動直到它們碰到了遊戲空間的邊框。這個邏輯並不難實現,最簡單的辦法,採用一個迴圈,遍曆所有的方格,如果當前方格的bshow屬性是false,那麼不進行操作,否則,擷取當前方格的位置資訊(座標),文本,顏色。將當前列的最後一行的方格賦予相同的文本和顏色,同時將當前遊戲方塊的資訊清空(方塊顏色設定為背景色,文本清空),重新整理介面,這樣就可以造成當前遊戲方格“移動”到最後一行的假象。當一個方向做成功之後,另外三個方向只要以此類推就可以了。雖然說就這個邏輯和最終的邏輯還相差十萬八千裡,甚至對於整個遊戲邏輯來說甚至是不正確的。但是如果沒有學會加法,那麼你怎麼也不會學會三重積分吧。

         忘記上面一段扯的廢話,現在來考慮情況二,如果兩個方塊位於一列,就要考慮到合并的情況(先假設兩個方塊的文本是一樣的)。這時按下方向鍵,正確的期望結果是兩個方塊合并並且位於當前列的最後一行,這裡的做法思路有太多了。我最最最開始的第一腦想法就是從(0,0)位置的方塊開始遍曆,如果bshow是false,就不用做任何操作,如果不是,首先檢查當前列最後一行的方塊bshow的屬性,如果是false,那麼和上面情況一樣,設定當前列的最後一行的方塊和當前方塊資訊一致並清空當前方塊資訊。否則,則說明需要合并,這時只要將當前列最後一行方塊升一級(比如”2->4”)並清空當前方塊資訊就可以了。如此,兩個方塊的移動和合并已經做完了。可以按照這個思路先先出個代碼試試。

         現在已經考慮完“1”的情況了,下面要開始進一步,考慮“2”的情況了。當兩個初始方塊合并完畢以後,開始思考又多產生了一個新的方塊該怎麼辦。這裡也有好幾種情況需要被考慮。

         第一種,第一次出現的兩個方塊已經合并,新的方塊出現並且和被合并的方塊不在一列上(還是先只考慮一個方向上的情況)。這種最簡單,也就是兩個不在一列的方塊而已,上面已經說過了。

         第二種,同樣是最初出現的兩個方塊已經合并,新的方塊與舊的方塊在同一列中。這時新出現的方塊和已經合并的方塊文字不一樣,不可能發生合并(暫時先從最簡單的情況開始)。所以這時的思路是上面的一個擴充,也是從左上方的位置進行遍曆,依次判斷每個方塊的bshow是否是true,如果是,那麼這個移動方式就有點不同了,由於目前已知最後一行是已經合并的方塊,所以你知道當前這個方塊應該在倒數第二行上出現。但是如果我們不知道當前列最後一個bshow為false的行是多少該怎麼辦?怎麼使用程式來找到呢?由於我們知道當前位置的縱座標橫座標,所以從最後一行開始,依次向上遍曆,如果遇到bshow為false就立馬退出迴圈並且記錄下當前的行座標。這樣就能完成上面所說的任務,有了這個座標就可以知道當前的遊戲塊應該放在哪個位置,其餘的操作就和前面所說的一致就可以了。

         第三種,最初的兩個方塊沒有合并,那麼這種情況肯定和前面只有兩個方塊之中的某一種是一樣的。

         現在我們已經處理了”2”的情況,那麼這種”2”的情況能不能推及到無窮呢?如果這時又出現了一個新的方塊,那麼情況能不能直接套用上面的思路呢?稍微想一想,總感覺可能可以又可能不可以,因為“無窮”的情況實在是太多了。我總結了下,用一個圖表示,雖然在實際情況中不可能同時出現這四列,但是四列單獨出來,都是可能出現的:

        

         第一列和第四列的情況最簡單,直接移動合并就可以,具體步驟前面已經描述過了。

         第二列,需要判斷出同一列的下一行的文字和當前的文字不相同,只能移動不能發生合并。

         第三列,有兩個能合并的地方,列1和列2的“2”可以合并,列3和列4的“4”可以合并,而且合并後,兩個“2”合并出的“4”要顯示在第三行,“4”合并出來的“8”要出現在第四行。

         人腦思維分析完了,現在就要轉換成為電腦的思維考慮如何用程式實現。按照前面的思維模式下來,從左上方第一個遊戲方塊開始進行遍曆,如果遇到當前行的bshow是true,取得當前遊戲方塊的列標,從當前列最後一行開始,依次判斷當前的bshow是否是false,如果是,記錄下當前的行序號並且返回。獲得這個返回的行序號之後,判斷這個行序號的下一行(當然是同一列)和當前行的遊戲方塊文字是否一致(有點繞,意思你懂的就行),如果一致,那麼要考慮合并,並且重新設定資訊。

         考慮到這裡,貌似覺得邏輯都屢順了,我當時反正就是這麼想的。於是第一次我寫的代碼是這樣的,GetCoordsFromIndex(i,it)是我寫的一個函數,目的是根據當前遊戲方塊的序號得到遊戲方塊的縱座標和橫座標,這個使用除法和取餘操作很容易做到:

    if(nChar == VK_DOWN)    {        for(int i=0;i<nTotalGrids-1;i++)        {            if(m_itemBoxArray[i].bShow)            {                GetCoordsFromIndex(i,it);                 for(int j=nTotalGrids - (m_nRowAndCol-it.nColIndex);j>i;j-=m_nRowAndCol)                {                    // 找到當前列bshow為false的最大行序號                    if(!m_itemBoxArray[j].bShow)                    {                        m_itemBoxArray[i].bShow = false;                        m_itemBoxArray[j].bShow = true;                            m_itemBoxArray[j].strItemText = m_itemBoxArray[i].strItemText;                        m_itemBoxArray[i].strItemText = _T("");                        GetCoordsFromIndex(j,it);                        break;                    }                }                //合并                if ( it.nRowIndex < m_nRowAndCol -1 )                {                    ItemBox itCurrent = m_itemBoxArray[GetIndexFromCoords(it)];                     ++it.nRowIndex;                    ItemBox itNextRow = m_itemBoxArray[GetIndexFromCoords(it)];                                        if ( itNextRow.bShow &&                         itNextRow.strItemText == itCurrent.strItemText                         )                    {                        m_itemBoxArray[GetIndexFromCoords(it)].strItemText = m_arrStrItemTexts[GetIndex(itNextRow.strItemText)+1];                        --it.nRowIndex;                        m_itemBoxArray[GetIndexFromCoords(it)].bShow = false;                        m_itemBoxArray[GetIndexFromCoords(it)].strItemText = _T("");                    }                }            }        }    }

         但是一測試,發現貌似情況沒有向我想像的方向走啊。我觀察到了一個現象,那就是按照這個思路,列3的行為完全不正確。然後我又回到了這個代碼,畢竟走到這一步,從代碼的角度出發更容易找出問題所在,這也不違背數學歸納法的原則。如果按照這個代碼,列3這種情況就會出現這樣一種情況,由於我們是從左上方開始遍曆的,那麼第一行的2和第二行的2合并之後成為第二行的4,遍曆繼續,當遍曆到第三行的4時,決定與第四行的4進行合并,這樣就形成了第四行的8,但是此時,第二行的4應該移動到第三行。然而,按照我們的代碼,我們已經遍曆過了第二行,不可能再回去了,所以就造成了在錯誤,就會造成合并的4和合并的8分別在第二行和第四行,第三行空出來了,這明顯是不正確的。如何解決這個問題呢?通過分析可以發現,這個問題就是因為我們的遍曆順序有問題,那麼就簡單了,在向下這方向上,只要從最後一個遊戲方格向第一個方格進行遍曆這個問題就迎刃而解,具體邏輯同志們可以再思考思考。

        這樣之後,帶著洋洋得意的心情再次編譯,嘗試邏輯是否正確,結果發現還是太naive,問題又出現了,如果同一列出現了4個”2“,這時按一下”下“方向鍵,這4個”2“會直接合并成一個”8“,根據原遊戲的規則,這也是不對的,畢竟我們是山寨,山寨就得有點誠意,不能亂竄改原先的遊戲規則。這個問題又如何解決呢?首先得找出這個問題出現的原因,仔細思考下不難發現,原來遊戲的規則是一次操作中,合并過的遊戲方塊不可以再次合并,那麼,這個問題很容易解決,使用在封裝中的另一個成員變數bJoin,標識已經合并的方塊。所以最終”下“這個方向的代碼如下所示:

    if(nChar == VK_DOWN)    {        for(int i=nTotalGrids-1;i>=0;i--) //從後向前遍曆        {            if(m_itemBoxArray[i].bShow)            {                GetCoordsFromIndex(i,it);                 // 查詢同一列的bshow為false的最大行序號                for(int j=nTotalGrids - (m_nRowAndCol-it.nColIndex);j>i;j-=m_nRowAndCol)                {                    if(!m_itemBoxArray[j].bShow)                    {                        m_itemBoxArray[i].bShow = false;                        m_itemBoxArray[j].bShow = true;                            m_itemBoxArray[j].strItemText = m_itemBoxArray[i].strItemText;                        m_itemBoxArray[i].strItemText = _T("");                        GetCoordsFromIndex(j,it);                        break;                    }                }                // 合并                if ( it.nRowIndex < m_nRowAndCol -1 )                {                    ItemBox itCurrent = m_itemBoxArray[GetIndexFromCoords(it)];                     ++it.nRowIndex;                    ItemBox itNextRow = m_itemBoxArray[GetIndexFromCoords(it)];                                        if ( itNextRow.bShow &&                         itNextRow.strItemText == itCurrent.strItemText &&                         !itNextRow.bJoin // 判斷當前塊有無被合并過                        )                    {                        m_itemBoxArray[GetIndexFromCoords(it)].strItemText = m_arrStrItemTexts[GetIndex(itNextRow.strItemText)+1];                        m_nScore += 2*pow(2.0, GetIndex(itNextRow.strItemText)+1 );                        m_itemBoxArray[GetIndexFromCoords(it)].bJoin = true; // 設定當前塊已經被合并                        --it.nRowIndex;                        m_itemBoxArray[GetIndexFromCoords(it)].bShow = false;                        m_itemBoxArray[GetIndexFromCoords(it)].strItemText = _T("");                    }                }            }        }    }

          其餘三個方向的代碼依”下“方向類推,但要注意遍曆順序,具體代碼可進入部落格,那裡有我的郵箱,若需要原始碼,可以留言或者發信給我。到這裡,對於一個完整的遊戲,邏輯只能說完成了一個架構,還不能成為一個合格的山寨品,所以請看下一篇”邏輯面之緩緩出現的細節像風葉“。

 

        

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.