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

閱讀時間約 13 分鐘
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))
即將進入廣告,捲動後可繼續閱讀
為什麼會看到廣告
5會員
16內容數
留言0
查看全部
發表第一個留言支持創作者!
無限升級的沙龍 的其他內容
可能是初老症發作的的原因,最近玩到這類懷舊遊戲就很容易陷入某種懷舊時光的情緒中,一不小心就會玩到忘了時間,所以就興起了何不自己動手把童年那些有印象的街機遊戲全部實作一遍的念頭呢?不囉嗦以下就是這系列的第一款。。。
Blockade(封鎖線)是一款在1976年街機上發行的對戰遊戲,其簡單易懂的玩法,在當時可讓不少人為之瘋狂,這也是我們本次教學的主角,借由重製這款經典遊戲來學習如何使用Family BASIC開發遊戲,讓經典再現。
緣起 場景回到1986年鄉下的一家遊戲專賣店,一群小朋友圍著老闆正聽他口沫橫飛的說著,我跟你們說阿這卡夾還有這個鍵盤組合叫Family BASIC(註1)是用來開發紅白機遊戲用的,是我託廠商從日本弄回來的。。。
可能是初老症發作的的原因,最近玩到這類懷舊遊戲就很容易陷入某種懷舊時光的情緒中,一不小心就會玩到忘了時間,所以就興起了何不自己動手把童年那些有印象的街機遊戲全部實作一遍的念頭呢?不囉嗦以下就是這系列的第一款。。。
Blockade(封鎖線)是一款在1976年街機上發行的對戰遊戲,其簡單易懂的玩法,在當時可讓不少人為之瘋狂,這也是我們本次教學的主角,借由重製這款經典遊戲來學習如何使用Family BASIC開發遊戲,讓經典再現。
緣起 場景回到1986年鄉下的一家遊戲專賣店,一群小朋友圍著老闆正聽他口沫橫飛的說著,我跟你們說阿這卡夾還有這個鍵盤組合叫Family BASIC(註1)是用來開發紅白機遊戲用的,是我託廠商從日本弄回來的。。。
你可能也想看
Google News 追蹤
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
11/20日NVDA即將公布最新一期的財報, 今天Sell Side的分析師, 開始調高目標價, 市場的股價也開始反應, 未來一週NVDA將重新回到美股市場的焦點, 今天我們要分析NVDA Sell Side怎麼看待這次NVDA的財報預測, 以及實際上Buy Side的倉位及操作, 從
Thumbnail
Hi 大家好,我是Ethan😊 相近大家都知道保濕是皮膚保養中最基本,也是最重要的一步。無論是在畫室裡長時間對著畫布,還是在旅途中面對各種氣候變化,保持皮膚的水分平衡對我來說至關重要。保濕化妝水不僅能迅速為皮膚補水,還能提升後續保養品的吸收效率。 曾經,我的保養程序簡單到只包括清潔和隨意上乳液
Thumbnail
在讀取檔案時,最怕路徑的問題,常常會有路徑錯誤造成的異常報錯。 為了避免諸如此類的問題發生,明白程式的當前目錄與檔案的路徑是很重要的。 可以利用os 模組是 Python 中的一個標準庫,提供了許多與操作系統的功能。 以下是一些常用的 os 模組基本操作及其範例: 1. os.getcwd
解讀JSON 字串 首先,你需要使用 Python 的 json 模組來解讀JSON 字串。 JSON的基本結構: 由花括號 {} 包圍,內部是鍵值對的集合,每個鍵值對之間用逗號分隔。 鍵是字串類型,值可以是任何JSON支持的資料類型(字串、數字、布林值、陣列、物件或 null)。 {
Thumbnail
在我們正式開始Python串接LINE Bot的教程之前,有必要先為大家準備好所需的工具。實際上,製作LINE Bot的工具有很多選擇,我將會介紹我在製作過程中所使用的具體工具有哪些,以及如何進行事前的準備工作。讓我們一起走進這個有趣的製作過程吧!
Thumbnail
每當要研究投資標的時,你是否也會覺得A網站的財報資料完整,又覺得B網站的選股條件完善,又覺得C網站的新聞比較重要,有時身邊又沒有隨身攜帶筆電怎麼好查詢呢?這時可能就在想怎麼沒有人開發一個程式能完成符合我想要的功能啊~~與其求助於人,不如自己動手做最好,有相同煩惱的人,LINE Bot超適合你!!
Thumbnail
在看官網文件時,看到一份文件:PEP 8 -- Style Guide for Python Code。這份文件是關於Python程式碼風格的指引和建議。
Thumbnail
Selenium WebDriver Page Object Model (POM) 設計模式,若是有開發過 UI 自動化測試或是寫網路爬蟲的多少都有聽過或是看過吧!  最近剛好有幸接觸到某外商的 SDET 面試考試,題目要求使用 POM 設計 UI 自動化測試,又再度幫自己複習一遍。 Why?
Thumbnail
畢竟~我是一個懶人嘛... 估狗之後,大部分的做法就是手動的去下載當前瀏覽器對應版本的 Driver,放到一個特定位置,將 Driver Binary 位置指向它。 在 Google Chrome About Page 你可以看到版本 接著去下載對應的 Chrome Driver ↓ 解決方案:
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
11/20日NVDA即將公布最新一期的財報, 今天Sell Side的分析師, 開始調高目標價, 市場的股價也開始反應, 未來一週NVDA將重新回到美股市場的焦點, 今天我們要分析NVDA Sell Side怎麼看待這次NVDA的財報預測, 以及實際上Buy Side的倉位及操作, 從
Thumbnail
Hi 大家好,我是Ethan😊 相近大家都知道保濕是皮膚保養中最基本,也是最重要的一步。無論是在畫室裡長時間對著畫布,還是在旅途中面對各種氣候變化,保持皮膚的水分平衡對我來說至關重要。保濕化妝水不僅能迅速為皮膚補水,還能提升後續保養品的吸收效率。 曾經,我的保養程序簡單到只包括清潔和隨意上乳液
Thumbnail
在讀取檔案時,最怕路徑的問題,常常會有路徑錯誤造成的異常報錯。 為了避免諸如此類的問題發生,明白程式的當前目錄與檔案的路徑是很重要的。 可以利用os 模組是 Python 中的一個標準庫,提供了許多與操作系統的功能。 以下是一些常用的 os 模組基本操作及其範例: 1. os.getcwd
解讀JSON 字串 首先,你需要使用 Python 的 json 模組來解讀JSON 字串。 JSON的基本結構: 由花括號 {} 包圍,內部是鍵值對的集合,每個鍵值對之間用逗號分隔。 鍵是字串類型,值可以是任何JSON支持的資料類型(字串、數字、布林值、陣列、物件或 null)。 {
Thumbnail
在我們正式開始Python串接LINE Bot的教程之前,有必要先為大家準備好所需的工具。實際上,製作LINE Bot的工具有很多選擇,我將會介紹我在製作過程中所使用的具體工具有哪些,以及如何進行事前的準備工作。讓我們一起走進這個有趣的製作過程吧!
Thumbnail
每當要研究投資標的時,你是否也會覺得A網站的財報資料完整,又覺得B網站的選股條件完善,又覺得C網站的新聞比較重要,有時身邊又沒有隨身攜帶筆電怎麼好查詢呢?這時可能就在想怎麼沒有人開發一個程式能完成符合我想要的功能啊~~與其求助於人,不如自己動手做最好,有相同煩惱的人,LINE Bot超適合你!!
Thumbnail
在看官網文件時,看到一份文件:PEP 8 -- Style Guide for Python Code。這份文件是關於Python程式碼風格的指引和建議。
Thumbnail
Selenium WebDriver Page Object Model (POM) 設計模式,若是有開發過 UI 自動化測試或是寫網路爬蟲的多少都有聽過或是看過吧!  最近剛好有幸接觸到某外商的 SDET 面試考試,題目要求使用 POM 設計 UI 自動化測試,又再度幫自己複習一遍。 Why?
Thumbnail
畢竟~我是一個懶人嘛... 估狗之後,大部分的做法就是手動的去下載當前瀏覽器對應版本的 Driver,放到一個特定位置,將 Driver Binary 位置指向它。 在 Google Chrome About Page 你可以看到版本 接著去下載對應的 Chrome Driver ↓ 解決方案: