製做2048遊戲

v2-d7f852b432d4cf508c4cfc264db850c5_1200x500

下載 :: Game 2048 with Animation

今天的任務是給2048加上動畫。別看用js的transition就搞定的事情,用GDI來實現是有難度的。git

2048的遊戲實現機制

雖然代碼抄自gabrielecirulli/2048: A small clone of 1024 (https://play.google.com/store/apps/details?id=com.veewo.a1024) ,可是將js代碼用lua去實現仍是多費了點工夫。固然二者都是動態語言,坑比較少。github

如今來說下2048是怎樣實現的。算法

首先,咱們看界面,它就是個4x4的方塊,顯而易見,用4x4數組或1x16數組就能搞定。那麼數組裏面存什麼呢?咱們分析2048中的數值:空方格、2的冪次數值方塊,就兩種。解決方案是隻要存int——空方格對應0;2的冪就對應它的冪。因此:空=0,2=1,以此類推,2048=11。所以數據結構很是簡單,隨便用僞代碼表示:[2048-Table] = array<int>(4x4)。數組

知道了數據結構,那麼接下來就是算法,這是難的部分。數據結構

算法須要解決一些問題:app

  • 處理每次的上下左右動做。這個是核心功能,有幾個方面。1、判斷是否要合併;2、不合並的話就將方塊向前推到邊上,直到推不動爲止;3、隨機位置增長新方塊
  • 肯定當前是否爲死局。這個簡單,只要遍歷4x4數組,看是否存在相鄰的連續數值的方塊

下面解決主要問題。框架

1、判斷是否要合併模塊化

假如當前按下Left鍵,方塊向左移動,假如某行是「2-2-4-4」,那麼結果是「4-8-0-0」,由於只須要合併相鄰的方塊;如果「2-2-2-2」,結果是「4-4-0-0」,不會是「8-0-0-0」,這是由算法決定的。函數

那麼問題變簡單了,正如memcpy所作的,若是memcpy(src,dst),其中src和dst有交界部分。若src在dst前面,那麼應該從後向前複製,反之是從前向後。佈局

同理,方向爲Left時,對於「2-2-4-4」,是從左向右遍歷,先肯定「2-2」,將其換爲4,變成「4-0-4-4」,而後處理右邊兩個4。此時,左起第一個4其實已經合併過了,將其排除,因此當前只要處理「0-4-4」,那麼再將當中的4移至最左,成爲「4-0-4」,再處理第二個4,因爲第一個4還沒有合併過,所以兩個4再進行合併,成爲8。最後結果「4-8-0-0」。

整理一個過程:Left,2-2-4-4,加[]表示已合併過,無需再次合併。2-2-4-4 => [4]-0-4-4 => [4]-4-0-4 => [4]-[8]-0-0。

2、對不能合併的方塊進行移動

如「2-4-6-0」,方向Right,從右向左遍歷。過程爲:2-4-6-0 => 2-4-0-6 => 2-0-4-6 => 0-2-4-6。解釋略。

3、隨機增長新方塊

增長新方塊,添加2和4的機率比爲9比1,用隨機數實現。

而後須要尋找空位添加,這簡單,遍歷4x4數組,找到數值爲0的將其位置記錄,接着隨機抽位子。

---------------------------------------

2048的動畫實現機制

花了一天實現了方塊的移動,也有難度。

項目相關:佈局只支持新增GUI對象,不支持刪除,所以須要一開始就建立好。

思路:原4x4中每一個方塊對應一個GUI對象(記做origin),另外再新建4x4的GUI對象(記做anime),用於實現動畫。

例:當前2-2-4-4,方向Right,結果爲0-0-4-8。設計動畫,假設[n]表明從左起第n個位置。

方塊移動:[1,2,3,4] => [3,3,4,4]。那麼當第一個2(位置爲[1])進行移動時,這時應該將origin方塊隱藏,將替身anime方塊代替origin位置並顯現,隨後播放動畫,將anime的位置從[1]逐漸移動到[3],移動完畢後,主角origin上場,替身anime下場。

總結一下:當origin須要移動時,召喚替身anime到指定位置,替身移動,最後替身消失,origin在替身消失的地方出現。替身移動其實就是插值思想

另外,爲防止動畫沒有播放完程序仍接受遊戲指令致使邏輯亂套,所以在動畫播放期間用戶輸入無效,否則動畫就會出問題。

======我是分割線======

02/23更新

寫在前面

通過堅持不懈的努力,第一個遊戲已經制做完成。

遊戲邏輯所有用Lua實現,發揮Lua的特長。

前期的辛苦,到第一個遊戲完成時,已經煙消雲散了,想必製做遊戲的人們都是這個心情吧。

遊戲簡介

2048這個遊戲很經典,當前它的遊戲算法我是抄的:),由於沒足夠時間去思考,可是它的算法難度不算過高,我以爲沒有Popstar implementation(MFC)難寫(固然Popstar我也寫過纔敢這麼說)。

不過,因爲遊戲框架功能有限,2048中的動態滑動效果還沒有實現,下降了美觀度。

那麼搭建這個遊戲的框架所具有的最小功能有哪些呢?

  1. 交互層(Win32):攔截Win32消息,處理好程序與系統的交互。
  2. 邏輯層(Lua):根據交互層傳來的消息,實現界面或遊戲邏輯,保證程序正常運行。
  3. 渲染層(Direct2D):遵從邏輯層命令,進行窗口的渲染。

從上面能夠看出:Lua將交互層與渲染層相解耦,是十分重要的膠水語言。

這樣作的好處有哪些?

  1. 腳本語言的優點,如lambda函數、動態類型、yield等
  2. 只需修改腳本,無需再次編譯程序
  3. 有出錯提示,便於debug
  4. 垃圾回收
  5. 模塊化

初次使用Lua就體會到它強大之處,在乎料之中。

實現思路

總結一下編寫簡單的遊戲框架並用其實現2048整個過程當中的問題。

  • Lua的坑。因爲對它不熟悉,走了些彎路。Lua中統一用double保存整型與浮點,因此輸出整數就要進行轉換。再者就是類的問題,目前的問題是切換場景後,文本框的內容沒有重置。
  • D2D的坑。D2D的文檔比較少,學着費勁。要注意RenderTarget無效問題,無效後必須立刻從新建立,連帶以前的畫刷、圖片、文字等對象要所有重建。
  • Win32的坑。這已經見怪不怪了,一個字,略。

那麼總體的思路是:

  1. 交互層,將Win32消息進行封裝,對每一個消息調用Lua進行處理。這裏比較重要的是窗口改變大小問題,將大小改變時,UI也要跟着改變、跟着放縮,不過UI的問題我所有用Lua腳本去作,省去很多麻煩。
  2. 邏輯層,主要是Lua腳本的模塊化構建。個人思路:根爲場景(Scene),場景下有GDI對象(GdiObject),每一個GDI對象能夠包含其餘GDI對象,組成樹結構。場景切換時,GDI樹銷燬並建立新的。還有一個就是佈局(Layout),這裏的思路比較經典,掌握基本的遞歸方法就能夠寫成。佈局裏有絕對佈局、線性佈局、表格佈局,都比較簡單,這些佈局會自動調整成員的大小。
  3. 渲染層,很少說,能用現成的就用。像畫純色矩形算簡單,涉及富文本就複雜了,所以我暫時不考慮富文本狀況,畫畫基本的幾何圖形就足夠。這方面不算重要的內容。

http://zhuanlan.zhihu.com/p/25401920備份。

相關文章
相關標籤/搜索