2022-05-20|閱讀時間 ‧ 約 14 分鐘

【Python】那些年我們一起玩過的遊戲(二)-俄羅斯方塊

Tetris,沒錯這次要聊的主題就是俄羅斯方塊(Tetris)也是小老弟我從小玩到大的最愛遊戲之一,每次只要學習新程式語言就會在祭出來在給他狠狠地致敬一下,俄羅斯方塊以其簡單易懂的遊戲規則,往往讓人一玩就無法自拔。。。剛說到哪裡了,什麼教程,先別吵,等我玩完這局俄羅斯方塊再說。。。
依照慣例在開始之前先來看看完成後的遊玩影片:
以下會以”A、提案企劃 B、執行企劃 C、製作日誌”的順序寫作,各部份負責說明如下:
  • A、提案企劃:主要用來紀錄點子,平常想到就可以陸續不斷的寫,等有專案要執行時就從裡面挑出一份合適的企劃來執行。
  • B、執行企劃:在確定要執行的提案企劃後,接下來會在針對需要執行的內容作更完整的細節規格與製作規畫,以方便讓製作人員了解到要如何執行此專案。
  • C、製作日誌執行專案時,每位製作人員會將製作時的心得、經驗與規劃方式紀錄下來,以方便往後維護時可以給自己或接手的人參考。

A、提案企劃書

一句話形容這個遊戲
  • 老而不死謂之經典

遊戲類型
  • 益智類

遊戲特色
  • 懷舊復古風

發想概念
  • 藉以重製俄羅斯方塊(Tetris)遊戲學習Python語言

遊戲玩法
  • 玩家控制往下掉落的方塊,使其連線後消除
  • 如果連續無法連成線消除,會在累積20層後結束遊戲
  • 玩家的目的就是追求最高連線數量

目標族群
  • 想用Python學做遊戲的人

發行平台
  • Windows
  • MAC
  • Linux

預計製作期
  • 7天

美術風格
  • 懷舊灰階風格

遊戲周邊
  • 版權歸屬複雜很難拿到授權

製作人員需求
  • 企劃 x 1
  • 美術 x 1
  • 程式x 1

收費方式
  • 談到錢就傷感情了

製作預算
  • 燒熱血當預算

參考資料
  • 有興趣了解俄羅斯方塊故事的建議可以找以下這本書看看:

B、執行企劃書

前言
本遊戲的玩法大致上與市面上可玩到的俄羅斯方塊(Tetris)玩法相同,只是在畫面與介面的配置上作了些簡化。

遊戲流程
俄羅斯方塊遊戲運作流程圖

遊戲玩法說明
遊戲畫面示意圖
以下是遊戲中會出現的七種方塊種類
方塊種類
遊戲開始後我該作什麼?
遊戲開始後,會從上方示意圖中”方塊由此掉落”處掉落”下次出現方塊”顯示的方塊(然後方框內會在亂數從七種方塊中在挑出一種方塊出現在此),這時出現在”遊戲區”的方塊會慢慢往下掉,玩家可以控制方塊左右移動、順時鐘旋轉與快速往下掉落。
那我的遊戲目標?
遊戲的目的就是盡量讓往下掉落的方塊以一個可以盡量填滿橫向連線的方式組合,在每次方塊掉落後有橫向組合連線的方塊就會消除不見,消除後的空間會由上方的方塊往下掉落補滿,然後消除的連線數會累加到”本局連線數”內。
遊戲結束條件?
如果連續20列都無法達成橫向連線,遊戲就會宣告結束,並清除畫面,然後遊戲會自動重新開始。
關於最大連線數?
遊戲結束後會將”本局連線數”內的數量與”最大連線數”內的數量作比較,然後留下最大的數字當作最高大連線數。

遊戲操作
鍵盤
左右鍵:移動方塊左右移動
上鍵:旋轉方塊
下鍵:方塊快速往下移動
D鍵:開啟與關閉除錯訊息
Esc鍵:離開遊戲

美術元件列表
方塊-1
  • N型方塊,共兩種狀態
方塊-2
  • N型方塊,共兩種狀態
方塊-3
  • L型方塊,共四種狀態
方塊-4
  • L型方塊,共四種狀態
方塊-5
  • T型方塊,共四種狀態
方塊-6
  • 口型方塊,共一種狀態
方塊-7
  • I型方塊,共二種狀態

C、開發日誌

開發工具
  • Python 3.7.0
  • PyGame
第2~3天
系統分析與設計

關於方塊的編碼方式 ?
依照執行企劃需求我們需要將方塊作編碼的動作,以下我們使用一個4x4的矩陣來對L方塊作編碼說明:
方塊編碼
上圖可看出只要在4、8、12、13處標示與其他編號有不一樣的顏色就可以畫出L方塊。
另外我們也會針對每種方塊與其狀態也作編碼作,其編碼格式如下:
”方塊代號”:(方塊格子編號,…) , ”方塊代號”:(方塊格子編號,…) ...
依照上方規則我們來將七種不同形狀的方塊與其旋轉後的狀態作編碼(請注意以下各圖形下方的編碼):
方塊-1
  • 此方塊狀態1編碼為10,狀態2編碼為11
  • 狀態編碼後緊接著的就是方塊編碼
方塊-2
  • 此方塊狀態1編碼為20,狀態2編碼為21
  • 狀態編碼後緊接著的就是方塊編碼
方塊-3
  • 此方塊狀態1編碼為30,狀態2編碼為31,狀態3編碼為32,狀態4編碼為33
  • 狀態編碼後緊接著的就是方塊編碼
方塊-4
  • 此方塊狀態1編碼為40,狀態2編碼為41,狀態3編碼為42,狀態4編碼為43
  • 狀態編碼後緊接著的就是方塊編碼
方塊-5
  • 此方塊狀態1編碼為50,狀態2編碼為51,狀態3編碼為52,狀態4編碼為53
  • 狀態編碼後緊接著的就是方塊編碼
方塊-6
  • 此方塊狀態1編碼為60
  • 狀態編碼後緊接著的就是方塊編碼
方塊-7
  • 此方塊狀態1編碼為70,狀態2編碼為71
  • 狀態編碼後緊接著的就是方塊編碼
以上就是方塊的編碼說明。
關於如何透過陣列來處理遊戲的邏輯判斷與繪圖顯示 ?
在10x20陣列內作方塊的邏輯運算是本遊戲的重點,我們試著以下圖來說明其運作原理:
首先請先看圖,左邊紅色數字區顯示的就是10x20陣列的內容,0表示沒有方塊,大於0的數字表示方塊編號,畫面中白色數字表示正在掉落的方塊,再來看中間區域,這邊就是依左邊陣列內容所作的成像。
就遊戲邏輯部分我們需要以下功能的函數:
  • 取得方塊狀態編碼函數
  • 將方塊編碼複製到4x4陣列函數
  • 將方塊4x4陣列複製到10x20陣列函數
  • 判斷是否可將方塊4x4陣列複製到10x20陣列函數
  • 判斷行是否連線並清除行然後重整10x20函數
就繪圖部分,需要一個可以繪製方塊的物件
以上就是針對俄羅斯方塊作的初步系統分析,如果看完後出現以下症狀:
沒關係,建議搭配以下程式碼一起參悟,相信很快就能理解箇中奧妙了。
第4~7天
程式碼說明

關於繪製方塊物件
在系統分析裡有說道需要一個可繪製方塊的物件,已將程式碼放置GitHub Gist上,點選以下連結查看:

【drew.py程式碼】
程式碼內已寫上詳細說明
可以看出這個物件滿簡單的只是記錄方塊的基本資料與透過pygame將方塊繪製到畫面上。

主程式
重點戲來了,所有俄羅斯方塊的運作邏輯與繪圖程式碼幾乎都放在這邊,已將程式碼放置GitHub Gist上,點選以下連結查看:

【play.py程式碼】
程式碼內已寫上詳細說明,後面再針對細節作個別解說:
  • 25 ~ 34:這邊定義了所有方塊的編碼,編碼方式以在上面說明過。
  • 36 ~ 55:定義遊戲中會用到的所有陣列宣告,其中bricks_array代表我們的主遊戲區陣列其大小為10x20。
  • 101~113:這個函數主要是取得方塊編碼,使用說明如下:
getBrickIndex( 方塊編號(1~7), 方塊狀態(0~3))
使用範例:
pBrick = getBrickIndex(6, 0)
這樣會回傳”方塊-6”的方塊編碼( 8, 9,12,13)。
  • 115~145:這個函數是在處理將方塊編碼轉換到bricks陣列內,使用說明如下:
transformToBricks( 方塊編號(1~7),  方塊狀態(0~3))
使用範例:
transformToBricks( 6, 0)
執行後會得到bricks陣列內容如下:
  • 147~170:此函數會判斷如果將bricks陣列放入bricks_array陣列內是否會有碰撞產生,碰撞產生條件為,只要同一個位置其數值不為0就表示有碰撞,函數就會回傳True反之沒碰撞就回傳False,此函數使用時機為在方塊下降或移動時,先判斷要前往的位置是否已經有方塊了。
ifCopyToBricksArray()
  • 172~187:次函數會將bricks陣列複製到bricks_array陣列內,通常會在判斷到方塊無法掉落後執行此函數將掉落中的方塊複製到bricks_array陣列內。
使用範例:
copyToBricksArray()
  • 189~213:此函數處理重新開始遊戲初始變數。
使用範例:
resetGame()
  • 215~232:此函數用來判斷要清除的方塊並在bricks_array陣列內作標記(標記為9),最後會回傳被標記為9的總數量。
  • 用範例:
lines = ifClearBrick()/ 10
這邊除10的是因為消除一列為10個方塊,所以除10才是正確的消除行數(一行10個方塊)。
  • 234~267:此函數在處理更新與顯示下個要出現的方塊,需要傳入方塊編號。
使用範例:
updateNextBricks(brick_next_id)
  • 269~313:此函數主要處理產生新方塊。
使用範例:
brickNew()
  • 315~331:此函數用來執行清除表記要清除的方塊(標記號碼為9)與作方塊下壓的動作。
使用範例:
clearBrick()
  • 353~356:建立10x20的方塊物件,以作主顯示區顯示方塊用。
  • 358~361:建立4x4的方塊物件,主要在畫面上顯示下個方塊用。
  • 380~581:遊戲主迴圈。
  • 384~479:判斷玩家鍵盤輸入,並依照輸入處理相關邏輯運算。
取得鍵盤輸入的方式為先利用以下指令取的鍵盤輸入事件:
for event in pygame.event.get():
然後再判斷事件的type是否為”按下鍵盤”事件:
if event.type == pygame.KEYDOWN:
接下來在透過事件的key來判斷玩家是按下哪個鍵盤按鈕:
if event.key == pygame.K_ESCAPE:
如上表示判斷玩家按下Esc按鈕
  • 400~435:判斷玩家按上鍵後處理讓方塊作旋轉的動作。
這去區塊內的程式碼,主要是在處裡判斷各種方塊的旋轉與碰撞邏輯判斷。
  • 436~440:判斷玩家按下鍵後處理讓方塊快速下降。
  • 441~472:判斷玩家按下左右鍵後處理讓方塊左右移動。
  • 473~479:判斷玩家放開下按鍵後讓方塊往下移動的速度恢復正常。
  • 481~580:處理畫面繪圖更新。
  • 485~498:處理遊戲中所有狀態(game_mode = 0),包括方塊往下移動時間、判斷方塊碰撞、產生新方塊等。
  • 499~506:清除方塊狀態(game_mode = 1),在這個狀態下不會處理方塊下降。
  • 513~523:依照bricks_array陣列(主遊戲區)的內容繪製方塊到畫面上。
  • 524~533:依照bricks陣列(掉落中方塊)內容繪製方塊到畫面上。
  • 534~563:按下鍵盤D後可以顯示除錯訊息,以觀察陣列運作是否正確。

執行遊戲
  • 請在命令列下輸入python play.py 以執行遊戲

GitHub下載原始碼

後記
終於還是利用下班的時間斷斷續續的把這篇生出來了,個人覺還是有許多地方沒解釋的很清楚,也還在抓文章的寫作風格,不過寫作這東西只能多多練習才能慢慢有點心得了,接下來會在多多利用時間看看能不能在多產出幾款遊戲教學來,敬請期待。

版本更新
2020/4/4
更新彩色版本(已將相關檔案更新至GitHub)
  • 以下為執行遊戲畫面
  • 執行方式如下
請在命令列下輸入python playColor.py 以執行遊戲
  • 關於方塊顏色
方塊顏色我們以下方這張圖的方塊顏色為主:
  • 程式碼修改說明
請開啟playColor.py程式碼並搜尋所有ColorVer關鍵字的地方,這些地方就是彩色版本修改的部分,主要修改方向為將所有顯示方塊的地方,依方塊編號給於顏色值。
關於全螢幕顯示
彩色版我們改以全螢幕顯示,如果要更改成視窗模式只要找到以下程式碼:
# 全螢幕模式.
canvas = pygame.display.set_mode((canvas_width, canvas_height), 
pygame.DOUBLEBUF and pygame.FULLSCREEN )
# 視窗模式.
#canvas = pygame.display.set_mode((canvas_width, canvas_height))
改成以下即可
# 全螢幕模式.
#canvas = pygame.display.set_mode((canvas_width, canvas_height), 
pygame.DOUBLEBUF and pygame.FULLSCREEN )
# 視窗模式.
canvas = pygame.display.set_mode((canvas_width, canvas_height))

分享至
成為作者繼續創作的動力吧!
© 2024 vocus All rights reserved.