2048遊戲最近很火,想看下源碼,卻不會JavaScript。網上搜了搜安卓版的源碼,嘗試下來學習。
uberspot
在https://github.com/uberspot/2048-android 上面發現了一個安卓版的2048代碼,於是下載準備閱讀。卻發現源檔案中只有一個Java類,MainActivity.java。開啟大致看了一下:
(savedInstanceState != "file:///android_asset/2048/index.html"
原來是用一個webview對原本的JavaScript進行了封裝,使用安卓內部webkit瀏覽器進行了載入。相當於用手機瀏覽器玩網頁版的遊戲,只能再搜尋了。
極客學院
另外找到一個極客學院版本的源碼,在網站上面還有視頻教程。本文分析的主要內容就是極客學院版本的原始碼了,作者是ime。
源碼連結:https://github.com/plter/Android2048GameLesson
分析的目標為code\ide\ADT\Game2048Publish目錄中的源碼版本
1 介面
介面比較簡單了,開啟activity_main.xml看看,幾個TextView,一個按鈕,還有三個自訂的控制項GameView,AnimLayer,Card。遊戲的如下:
setNum(.num = (num<=0""+"" 00x00000000); 20xffeee4da 40xffede0c8 80xfff2b179 160xfff595630xff3c3a32
num<=0表明是空白方格。當前位置上如果沒有card,則使用num<=0的card進行替代。card 0沒有label,同時底色為透明。除了card 0之外,card 2之後的卡片都有對應的顏色和數字。
1.2 AnimLayer
類AnimLayer繼承了FramLayout,用於動畫展示。在極客學院安卓2048最主要由兩個動畫:卡片移動和卡片出現。
a) 對於卡片出現動畫:
ScaleAnimation sa = ScaleAnimation(0.1f, 1, 0.1f, 1, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f100
b) 對於卡片移動動畫:
使用ArrayList<Card> cards用於管理臨時卡片的建立和回收(避免每次建立臨時卡片時建立新的對象)
建立一個臨時卡片,從卡片from移動到卡片to,當完成動畫之後將臨時卡片設為不可見,並使用cards回收該卡片。
建立卡片:
Card getCard( (cards.size()>0= cards.remove(0=
建立卡片時,如果cards不為空白,則從cards隊首取出一張臨時卡片。(這裡認為使用LinkedList<Card>更加適合臨時卡片管理隊列)
回收卡片:
回收卡片將當前卡片設為不可見,並加入到cards中。
卡片移動:
createMoveAnim( Card from, Card to, fromX, toX, fromY, Card c = LayoutParams lp = = fromX*= fromY* (to.getNum()<=0 TranslateAnimation ta = TranslateAnimation(0, Config.CARD_WIDTH*(toX-fromX), 0, Config.CARD_WIDTH*(toY-25
1.3 GameView
GameView繼承了GridLayout,包含了介面和遊戲邏輯兩個部分。這裡介紹介面。
介面中比較重要的內容就是手勢識別,用於操控格子的移動:
0xffbbada0 MotionEvent.ACTION_DOWN: startX === event.getX()-= event.getY()- (Math.abs(offsetX)> (offsetX<-5 (offsetX>5 (offsetY<-5 (offsetY>5 ;
使用了View.OnTouchListener來偵聽觸摸事件:計算按下和抬起來時offsetX和offsetY,預測手勢的移動。
2 遊戲邏輯
上一節介紹了基本的介面展現,本節介紹商務邏輯,即遊戲實現原理。
2.1 遊戲初始化
調用函數initGameView()完成遊戲初始化:
setBackgroundColor(0xffbbada0
設定控制項的方格數量,隨後設定了控制項北京,最後註冊了剛才分析過的觸摸事件監聽器。此時遊戲已經準備好了,正式開始。
2.2 開始遊戲
函數startGame();正式開始遊戲,首先向方格內隨機寫入兩個方塊:
= ( y = 0; y < Config.LINES; y++ ( x = 0; x < Config.LINES; x++0
這個函數addRandomNum()向遊戲面板內隨機加入兩個方塊,開始遊戲:
( y = 0; y < Config.LINES; y++ ( x = 0; x < Config.LINES; x++ (cardsMap[x][y].getNum()<=0 (emptyPoints.size()>0 Point p = emptyPoints.remove(()(Math.random()* num = Math.random()>0.1?2:4
函數addRandomNum()向面板中空的格子中隨機產生一個卡片。首先搜集面板中所有空的位置,搜集到一個List中,最後產生隨機數,隨機產生一個數字,並完成組建動畫。
2.3 移動
2048遊戲通過遊戲中所有的方格朝某個方向移動,合并相同數位方塊。有四個函數負責移動,分別是上下左右,這裡只分析一個方向。
merge = ; ( y = 0; y < Config.LINES; y++) { ( x = 0; x < Config.LINES; x++ ( x1 = x+1; x1 < Config.LINES; x1++ (cardsMap[x1][y].getNum()>0) { (cardsMap[x][y].getNum()<=0) {0--; merge = *20=
左移,針對面板中所有列,將每行的方塊向左移動。在兩種情況發生卡片合并:
1 當前位置為空白卡片,右側為非空卡片,合并後當前位置卡片Num為右側卡片,右側卡片清零。
2 當前位置為非空卡片,右側卡片數值和它相等,合并後當前位置卡片數量翻倍,右側卡片清零。
從遊戲角度來講:1 對應卡片單純的移動,2 對應兩張相同卡片的合并。因此,只要發生卡片實質上的移動,就應該隨機再生產一個卡片,調用addRandomNum()。
2.4 遊戲結束的判斷
每次發生卡片移動,都要檢查遊戲還能否繼續,是否已經結束。函數checkComplete()完成遊戲失敗(感覺叫做checkFailure()更好)的檢查:
complete = ( y = 0; y < Config.LINES; y++ ( x = 0; x < Config.LINES; x++ (cardsMap[x][y].getNum()==0|| (x>0&&cardsMap[x][y].equals(cardsMap[x-1][y]))|| (x<Config.LINES-1&&cardsMap[x][y].equals(cardsMap[x+1][y]))| (y>0&&cardsMap[x][y].equals(cardsMap[x][y-1]))|| (y<Config.LINES-1&&cardsMap[x][y].equals(cardsMap[x][y+1]))) {= AlertDialog.Builder(getContext()).setTitle("你好").setMessage("遊戲結束").setPositiveButton("重新開始", onClick(DialogInterface dialog,
遊戲可以繼續的兩個條件:有空的格子,或者還有能夠合并的卡片。