1. 程式人生 > >一個專業的程式原該養成的好習慣(分析能力)《2048》遊戲分析

一個專業的程式原該養成的好習慣(分析能力)《2048》遊戲分析

一、遊戲分析
《2048》是一款比較流行的數字遊戲,其作者Gabriele Cirulli (加布裡埃爾斯路理)目前居住在義大利。他在2014年3月最先將 2048 的開源版本放到 Github 上,由此引發了風靡全球的狂潮,而其當時年僅20歲。
這款遊戲的玩法很簡單,每次可以選擇上下左右移動,每移動一次,所有的數字方塊都會往移動的方向靠攏,系統也會在空白的地方亂數出現一個數字方塊,相同數字的方塊在靠攏、相撞時會相加。在所有方格未被佔滿且無法移動之前合成出2048,則遊戲勝利,否則失敗

遊戲初始介面:

遊戲開始後,可以按鍵盤的上下左右鍵進行移動,數字方塊會向移動方向靠攏,並生成新方塊

數字方塊移動期間碰撞會將相同方塊合併成新方塊,並加分

數字方塊移動期間碰撞會將相同方塊合併成新方塊,並加分

直到生成2048或無法移動、碰撞為止,遊戲結束,可以重新開始遊戲或退出遊戲,重新開始則更新最高分

遊戲過程中可以按F1重新開始遊戲

遊戲過程中可以按ESC退出遊戲

二、開發過程
軟體的開發過程可以分為:
1.確定需求(軟體功能的文字描述)
2048的功能如下:

1).開始遊戲會在4*4的方格中,隨機產生兩個數字方塊2或4
2).每次可以用鍵盤上下左右移動,每移動一次,所有的數字方塊都會往移動的方向靠攏,系統也會在空白的地方隨機出現一個數字方塊2或4。如果無任何方塊移動,則不產生數字方塊。
3).相同數字的方塊在靠攏、相撞時會相加。
4).不斷的疊加最終拼湊出2048這個數字就算成功。如果在湊出2048之前,方塊佔滿所有方格,不能再移動或相加,則遊戲結束
5).遊戲有計分功能,每次發生方塊相撞相加,就會加分,分值為當前方塊數的數值,顯示在介面上,動態居中顯示
6).遊戲勝利或結束後更新最高分,可以退出遊戲或重新開始
7).遊戲過程中可以按F1重新開始遊戲,可以按esc退出遊戲

2.業務需求分析(找出物件,與物件之間的關係。本遊戲中物件關係如下)
|–StartGame(遊戲主視窗)
|–Game_2048(2048遊戲面板類)
|–Background 背景類
|–GameSerivce 遊戲功能類
|–Data 分數類
|–Resource 資源載入類

3.類的設計
要建立物件,就先要定義出類。找出物件中的資料,根據物件定義類。

a.遊戲啟動類StartGame
用於設定遊戲主介面,載入遊戲顯示內容Game_2048。

b.遊戲資源類Resources
Resources類用於提供遊戲需要使用到所有檔案的引用,包括方塊2~方塊2048、背景和次背景、得分和最高分計分板、分數數字的png圖片檔案
|–Resources
|–img_num 分數數字
|–img_bg 背景
|–img_fg 次背景方格
|–img_score 得分
|–img_highScore 最高得分
|–img_2 方塊2
|–img_4 方塊4
|–img_8 方塊8
|–img_16 方塊16
|–img_32 方塊32
|–img_64 方塊64
|–img_128 方塊128
|–img_256 方塊256
|–img_512 方塊512
|–img_1024 方塊1024
|–img_2048 方塊2048
|–getImg(String) 載入圖片

c.遊戲分數類Data
Data類用於儲存遊戲中的分數資訊,並定義用於返回和修改其中私有資料成員的公共介面方法,同時定義了繪製得分、最高分計分板、分數數字的顯示分數的功能
|–Data
|–SCORE_X 得分計分板圖片的原點x座標
|–SCORE_Y 得分計分板圖片的原點y座標
|–HIGHTSCORE_X 最高分數計分板的原點x座標
|–HIGHTSCORE_Y 最高分數計分板的原點y座標
|–SIZE_NUM 數字圖片中一個數字佔的畫素大小是21(寬高都是)
|–SIZE_SCORE 分數與最高分圖片寬度 都是140畫素
|–score 當前得分
|–hightScore 最高分
|–drawData(Graphics) 實現計分板功能,包括繪製計分板背景圖和得分、最高分資料顯示功能
|–drawScore(Graphics) 顯示得分資料
|–drawHightScore(Graphics)

d.遊戲背景類Background
在指定的座標繪製遊戲的背景和次背景圖。
|–Background
|–FG_X 次背景方格原點x值
|–FG_Y 次背景方格原點y值
|–SIZE 一個次背景方格大小
|–drawBackground(Graphics) 畫背景和次方格背景
|–drawBlock(Graphics,int,int,int) 根據圖片索引畫數字圖片
|–drawNumPic(Graphics,Image,int,int)畫出圖片數字

e.GameService類用於繪製主要功能的方法,以及實現遊戲主要功能需要呼叫的一些介面方法:
資料成員
用於對應4*4方格的儲存數字圖片的索引編號的二維陣列 int[][] gameMap;
儲存需要顯示的分數資料 Data data;
開始遊戲start();
生成一個新方塊newBlock();
繪製遊戲主功能GamePaint()
根據引數畫數字方格圖片deawNumPic
向左移動moveLeft();
向右移動moveRight();
向上移動moveUp();
向下移動moveDown();
向上消除removeUp();
向下消除removeDown();
向左消除removeLeft();
向右消除removeRight()。
遊戲結束或勝利的處理isGameOver();
是否滿屏isFull();
2048是否達成 is2048();
是否還可以移動或消除canRemove();
重新開始restart();
計分功能bonus()
重新整理最高分功能refreshHighscore();

f.遊戲顯示功能類Game_2048
遊戲顯示面板,用於呼叫背景類Background的drawBackground和遊戲顯示服務類GameService的gamePaint顯示功能來顯示遊戲畫面內容
接收從鍵盤輸入按鍵鍵值,並根據不同的鍵值呼叫不同的方法;
按←鍵,鍵值為37,呼叫GameService類中的moveleft();和removeLeft();方法實現向左移動和向左消除;根據返回值isMove和isRemove判斷移動是否有效,決定是否生成新的方塊;
按↑(鍵值為38)、→(鍵值為39)、↓(鍵值為40)鍵同理;
按F1,鍵值為112,可以在遊戲中途重新開始;
按Esc,鍵值為27,可在遊戲中途退出。

4.開發過程
步驟一:新建工程和包
新建名為JAVA_2048的工程,然後在工程下的src目錄下新建包
步驟二:新建StartGame類
該類的作用是用於載入遊戲並啟動
步驟三:新建Game_2048類
此類是遊戲顯示面板,用於顯示遊戲畫面內容,需要繼承(JPanel),也是一個監聽鍵盤操作的監聽器,
需要實現KeyListener介面
步驟四:拷貝Background類、Resource類、Data
Background類是背景類
Resource類是資源類,作用是載入資源
Data類,該類是用於封裝計分板資料與計分板計分功能
將res資原始檔夾放到Java_2048工程的根目錄下
步驟五:重構StartGame類和Game_2048類,載入背景圖片
在Game_2048類中新增成員Background,並定義無參構造方法,初始化成員Background
在StartGame類中新增載入遊戲面版的程式碼
步驟六:
測試背景圖是否繪製成功
步驟七:新建GameService類,用於實現遊戲主要顯示功能和業務處理功能
新建GameService類,並定義成員Data data和int[][] gameMap.
其中data是計分功能類的物件,gameMap是用於儲存遊戲中對應的4*4
方格的數字圖片的索引編號的。遊戲中數字圖片2~2048共有11種,對應
儲存到gamMap中的值為1~11,也就是數字圖片2對應儲存為數值1,以
此類推。如果某位置沒有圖片,則存數值0。如下圖中的數字圖片,
對應gameMap中的值為:
{{2,0,0,0},
{0,0,0,1},
{0,0,0,0,},
{0,0,0,0}}

初始化成員資訊
畫出面板上的內容:分數和方格中的數字圖片

步驟八:重構Game_2048,實現畫計分板功能
在Game_2048類中新增GameService成員,並呼叫GameService的gamePaint方法
執行StartGame的main方法測試

步驟九:實現遊戲啟動功能,隨機生成2個數字圖片
在遊戲主功能類GameService中定義start和newBlock方法
start方法用於啟動遊戲後的操作,將gameMap初始化和將data中的score和hightScore清零,並且隨機位置生成兩個數字圖片。
完成GameService的start方法,並在該類的構造方法中呼叫,隨該類建立而啟動遊戲
newBlock方法用於在4*4的方格內隨機生成方塊2或4,如果位置上存在方塊則再生成,直到不重複,並需要將生成的方塊所代表的數字圖片索引編號存入gameMap中。為了降低遊戲難度,生成方塊4的概率約1/8

步驟十:重構GameService的gamePaint功能,實現畫數字方塊的功能
數字方塊圖片與GameSerivce的gameMap是對應關係,gameMap中
儲存了遊戲主介面上的所有數字方塊圖案索引編號,如果某個位置沒有
數字方塊圖案,則儲存0,否則儲存1~11。
畫數字方塊的功能,首先需要遍歷二維陣列gameMap的內容,並將值
一一取出來判斷,根據值畫對應的圖片。
其次,畫圖片需要定點陣圖片原點的x和y值位置。數字圖片都是100100
畫素大小,次背景方格是400
400畫素,剛好放入一個背景方格佔滿。
圖片原點x值為:FG_X+i100(FG_X為背景方格原點x值,i為該行第幾個格子,格子從0開始)
圖片原點y值為:FG_Y+j
100(FG_Y為背景方格原點x值,j為該列第幾個格子,格子從0開始)
執行StartGame的main方法,測試遊戲第九步和第十步實現的功能

步驟十一:新增遊戲移動功能
在GameService類中新增moveLeft、moveRight、moveDown、moveUp功能:

步驟十二:處理鍵盤按下事件
重構StartGame,新增監聽功能

步驟十三:測試鍵盤響應功能
運行遊戲,分別測試上下左右小鍵盤、esc和F1按鍵

步驟十四:實現左移操作
鍵盤按下的處理流程(以按了小鍵盤左箭頭為例):按了鍵盤左鍵,則需要呼叫GameService中的
moveLeft和removeLeft,同時根據返回值來判斷是否有移動或消除,如果有則生成新的方塊。移動後重畫遊戲介面
運行遊戲,測試左移操作

步驟十五:實現GameService左移方法moveLeft
左移操作流程:
1).遍歷每一行,邊界條件為0~3
2).遍歷行當中的每一個格子,邊界條件為1~3(為0的格子不需要移動)
3).每一個格子需要檢查是否有數字方格圖片並且左邊的格子是否有
數字方格圖片,如果都滿足,則可以進行左移操作。左移操作實際
就是操作gameMap中的資料,將當前格子位置對應的圖片索引的
值賦值給左邊的格子位置,並將當前格子索引值賦值為0。
4).當前格子變為左邊的格子,更改移動標記
5).再迴圈3、4的過程,直到3的判斷條件不滿足,則表示該格子不
能左移動了

步驟十六:實現右移功能
重構GameService的keyPressed方法和GameService的moveRight方法,實現右移,
右移的流程:
1.遍歷每一行,邊界條件為0~3(i)
2.遍歷行當中的每一個格子,邊界條件為2~0(j,值為3的格子不需
要移動,且從值為2的格子開始右移動)
3.每一個格子需要檢查是否有數字方格圖片並且右邊的格子是否沒有
數字方格圖片,如果都滿足,則可以進行右移操作。右移操作實際
就是操作gameMap中的資料,將當前格子位置對應的圖片索引的
值賦值給右邊的格子位置,並將當前格子索引值賦值為0。
4.當前格子變為右邊的格子,更改移動標記(下一個標記要小於2)
5.再迴圈3、4的過程,直到3的判斷條件不滿足,則表示該格子不能
右移動了

步驟十七:實現下移和上移操作
下移與上移操作,流程與之前的左右移動操作類似,只是在下移和上移時是修改gameMap[i][j]中的i值這點不一樣。
下移流程:
1.遍歷每一行,邊界條件為2~0(i值,為3的格子不需
要移動,且從值為2的格子開始下移動)
2.遍歷行當中的每一個格子,邊界條件為0~3(j值)
3.每一個格子需要檢查是否有數字方格圖片並且下邊的格子是否沒有
數字方格圖片,如果都滿足,則可以進行下移操作。下移操作實際
就是操作gameMap中的資料,將當前格子位置對應的圖片索引的
值賦值給下邊的格子位置,並將當前格子索引值賦值為0。
4.當前格子變為下邊的格子,更改移動標記
5.再迴圈3、4的過程,直到3的判斷條件不滿足,則表示該格子不能
下移動了

上移流程:
1.遍歷每一行,邊界條件為1~3(i值,為0的格子不需要移動)
2.遍歷行當中的每一個格子,邊界條件為0~3(j值)
3.每一個格子需要檢查是否有數字方格圖片並且上邊的格子是否沒有
數字方格圖片,如果都滿足,則可以進行上移操作。上移操作實際
就是操作gameMap中的資料,將當前格子位置對應的圖片索引的
值賦值給上邊的格子位置,並將當前格子索引值賦值為0。
4.當前格子變為上邊的格子,更改移動標記
5.再迴圈3、4的過程,直到3的判斷條件不滿足,則表示該格子不能
上移動了

步驟十八:實現消除功能的左消除
左消除,當前的格子都會將右邊的格子左移並嘗試合併,並且最後圖片數字會移動到最左邊,所以需要從最左邊的圖片數字開始消除,流程如下(左消除之前已經呼叫
左移動,格子內的圖片數字會先移動到左邊):
1.遍歷每一行,邊界條件為0~3(i值)
2.遍歷行當中的每一個格子,邊界條件為0~2(j值,為3的格子不需
要嘗試合併,右邊沒有格子了,且從值為0的格子開始左消)
3.每一個格子需要檢查是否有數字方格圖片並且右邊的格子圖片索
引是否有與當前數字方格圖片索引值相同,如果都滿足,則可以進
行左消操作。左消操作實際就是操作gameMap中的資料,將當前
格子位置對應的圖片索引的值增加1,並將當右邊格子索引值賦值
為0。
4.當前格子變為右邊的格子,更改消除標記

運行遊戲,測試左消功能:
按小鍵盤左鍵,觸發的操作有1.左移操作 2.左消除操作

步驟十九:實現右消除、上消除、下消除功能
右消除,鍵盤右方向鍵觸發,左邊的圖片嘗試往右邊合併 因為在右消除之前已經呼叫了右移動,所以,格子都已經右移到右邊。流程:
1.遍歷每一行,邊界條件為0~3(i值)
2.遍歷行當中的每一個格子,邊界條件為3~1(j值,為0的格子不需
要嘗試合併,左邊沒有格子了,且從值為3的格子開始右消)
3.每一個格子需要檢查是否有數字方格圖片並且左邊的格子圖片索
引是否有與當前數字方格圖片索引值相同,如果都滿足,則可以進
行右消操作。右消操作實際就是操作gameMap中的資料,將當前
格子位置對應的圖片索引的值增加1,並將當左邊格子索引值賦值
為0。
4.當前格子變為左邊的格子,更改消除標記

上消除,鍵盤上方向鍵觸發,下邊的圖片嘗試往上邊合併 因為在上消除之前已經呼叫了上移動,所以,格子都已經上移到上邊。流程:
1.遍歷每一行,邊界條件為0~2(i值,為3的格子不需
要嘗試合併,下邊沒有圖片了,且從值為0的格子開始上消)
2.遍歷行當中的每一個格子,邊界條件為0~3(j值)
3.每一個格子需要檢查是否有數字方格圖片並且下邊的格子圖片索
引是否有與當前數字方格圖片索引值相同,如果都滿足,則可以進
行上消操作。上消操作實際就是操作gameMap中的資料,將當前
格子位置對應的圖片索引的值增加1,並將當下邊格子索引值賦值
為0。
4.當前格子變為下邊的格子,更改消除標記

下消除,鍵盤下方向鍵觸發,上邊的圖片嘗試往下邊合併 因為在下消除之前已經呼叫了下移動,所以,格子都已經下移到下邊。流程:
1.遍歷每一行,邊界條件為3~1(i值,為0的格子不需
要嘗試合併,上邊沒有圖片了,且從值為3的格子開始下消)
2.遍歷行當中的每一個格子,邊界條件為0~3(j值)
3.每一個格子需要檢查是否有數字方格圖片並且上邊的格子圖片索
引是否有與當前數字方格圖片索引值相同,如果都滿足,則可以進
行下消操作。下消操作實際就是操作gameMap中的資料,將當前
格子位置對應的圖片索引的值增加1,並將當上邊格子索引值賦值
為0。
4.當前格子變為上邊的格子,更改消除標記

測試右消除功能

測試上消除功能:

測試下消除

步驟二十:實現計分功能
計分功能在遊戲開始執行後,發生消除則計分,分數增量為消除後產生的數字圖片的面值,計算出分數後,
需要畫在分數面板上,並且實現動態居中的效果。
1).分析分值:4是2的2次方、8是2的3次方、16是2的4次方,
而在gameMap中正好儲存的數字圖片對應的索引編號,圖片4是
數值2,圖片8是數值3。通過Math.pow(2,num)公式則可以計算出
消除後產生的分值(num為圖片索引值)。
2).將該值累加進Data的score成員中
3).在GameService類中新增計分方法bonus,實現計分功能,
4).測試計分功能

步驟二十一:實現更新最高分功能
遊戲勝利或結束後,如果得分比最高分高,則更新最高分。
重構GameService類,新增refreshHighScore方法

步驟二十二:實現遊戲結束功能
遊戲結束包括兩種情況:合成出2048遊戲勝利、佔滿了所有格子且不能移動或合併遊戲失敗
流程:
1).判斷是否遊戲失敗(所有格子都佔滿了,且不能移動或合併)
a.如果是遊戲失敗,則彈出視窗,顯示遊戲結束,並顯示得分
b.重新整理最高分
c.視窗選項可以選擇重新開始遊戲或結束遊戲。重新開始遊戲則將當前得分清零,初始化gameMap陣列,並生成兩個新的方塊圖片。結束遊戲則退出並關閉視窗。
2).判斷是否遊戲勝利
a.如果是,則彈出視窗,提示達成2048
b.重新整理最高分。
c.視窗選項可以選擇重新開始還是結束遊戲。重新開始遊戲則將當前得分清零,初始化gameMap陣列,並生成兩個新的方塊圖片。結束遊戲則退出並關閉視窗。

A.重構GameService類,新增isFull方法和canMove方法,用於判斷是否佔滿所有方格,並且不能移動
B.新增refreshHighScore方法
C.新增restart方法和isGameOver方法
D.新增is2048方法,用來判斷是否達成2048
E.給isGameOver方法新增判斷遊戲勝利功能
F.keyPressed方法,增加判斷遊戲結束功能
G.測試更新最高分和遊戲結束功能

步驟二十三:實現Esc退出遊戲和F1重新開始遊戲功能

2048擴充套件功能:
實現最高分的檔案存取:

步驟二十四:在結束遊戲或退出遊戲的時候將最高分儲存或更新到當前專案的src的score.txt中
1.新建score.txt檔案(存放hightScore資料)
2.新建一個操作類的ScoreUtil.java,新增儲存、更新、查詢方法
3.在結束遊戲或退出遊戲的時候,先呼叫查詢方法檢視檔案中是否有資料,如果不存在就呼叫儲存
如果存在則將資料取出判斷是否需要更新最高分,在需要的情況下,呼叫
更新方法將資料更新

步驟二十五:在啟動遊戲的時候讀取最高分資料在視窗上顯示
在啟動遊戲的時候讀取檔案,將最高分數讀取出來,放入Data類的hightScore
成員中,並且畫出最高分