基于python的俄羅斯方塊小游戲
目錄
基于python的俄羅斯方塊小游戲
1.概述
1.1 摘要
1.2 開發背景
1.3 開發環境
1.4 實現功能
2.代碼描述
2.1 模塊導入
2.2 初始化變量
2.3 播放音樂
2.4 創建方塊類
2.5 繪制游戲地圖
2.6 游戲初始化
2.7 繪制有邊框矩形
2.8 繪制我的文字
2.9 游戲主體
2.10 主程序運行
3.運行效果
4.注意事項
5.附錄源碼
1.概述
1.1 摘要
本文介紹了一個基于Python語言開發的簡單俄羅斯方塊小游戲。該游戲使用了pygame庫來實現圖形界面和聲音效果,并通過面向對象的設計方法來組織代碼。游戲的主要功能包括方塊的隨機生成、移動、旋轉、消除行以及得分計算等。游戲界面簡潔直觀,音效增強了游戲體驗。此外,游戲還提供了音樂開關和游戲暫停的功能,以增加游戲的可玩性和用戶友好性。
1.2 開發背景
俄羅斯方塊是一款經典的益智游戲,自上世紀80年代推出以來一直備受歡迎。隨著計算機技術的不斷發展,越來越多的開發者嘗試將這款游戲移植到各種平臺上。Python作為一種易學易用且功能強大的編程語言,非常適合用于開發小游戲。同時,pygame
庫提供了豐富的圖形、聲音和事件處理功能,使得開發過程更加便捷。
1.3 開發環境
Windows 11系統
pycharm 2021
python 3.9
1.4 實現功能
1.方塊生成與顯示:游戲會根據預設的方塊類型隨機生成一個新的方塊,并在游戲界面的頂部顯示。
2.方塊移動與旋轉:玩家可以通過鍵盤控制方塊的左右移動和順時針/逆時針旋轉。
3.消除行與得分:當玩家將一行方塊填滿時,該行會消除并給玩家加分。消除的行數越多,得分越高。
4.游戲結束條件:當方塊堆積到游戲界面的頂部時,游戲結束。
5.音效與音樂:游戲中有方塊下落、消除行等音效,同時提供了開啟/關閉背景音樂的選項。
6.游戲暫停與恢復:玩家可以隨時暫停游戲并在之后恢復,方便玩家在需要時暫時離開。
7.等級與速度:隨著游戲的進行,方塊的生成速度會逐漸加快,增加游戲難度。
2.代碼描述
2.1 模塊導入
import pygame
from pygame.locals import *
from sys import exit
import random
分析:
1.pygame
: 一個用于制作游戲的Python庫。
2.pygame.locals
: 從pygame
庫中導入的常量集合。
3.sys
: Python標準庫中的模塊,用于與Python解釋器交互。
4.random
: 用于生成隨機數。
2.2 初始化變量
# 使用pygame.init()初始化pygame庫
pygame.init()# 常量
MAX_I = 34
MAX_J = 15
# 定義地圖的最大行數和列數
SIZE = 15
# 方塊的大小COLORS = [(0, 0, 0), (255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0), (255, 255, 255)]
# 一個顏色列表,用于表示不同的方塊
gameMap = [[0 for j in range(MAX_J + 3)] for i in range(MAX_I + 3)]
# 一個二維列表,代表游戲地圖。初始化為全0,表示沒有方塊。
tetrisHeight = 0
# 記錄當前塔的高度,即已放下的方塊行數
tetrises = [[[1, 0, 0, 0],[1, 0, 0, 0],[1, 0, 0, 0],[1, 0, 0, 0]],[[0, 0, 0, 0],[0, 1, 1, 0],[0, 1, 1, 0],[0, 0, 0, 0]],[[1, 0, 0, 0],[1, 1, 0, 0],[0, 1, 0, 0],[0, 0, 0, 0]],[[0, 1, 0, 0],[1, 1, 0, 0],[1, 0, 0, 0],[0, 0, 0, 0]],[[0, 1, 0, 0],[1, 1, 1, 0],[0, 0, 0, 0],[0, 0, 0, 0]],[[1, 0, 0, 0],[1, 1, 1, 0],[0, 0, 0, 0],[0, 0, 0, 0]],[[0, 0, 1, 0],[1, 1, 1, 0],[0, 0, 0, 0],[0, 0, 0, 0]]
]
# 一個列表,包含7種不同的基本方塊形狀。每個形狀都是一個二維列表,表示方塊的各個部分。
# 分數
score = 0
# 等級
level = 1
# 音樂開關
isMusic = True
# 游戲暫停
isPause = False
分析:
此代碼段只是游戲的初始化部分,沒有包含游戲的主循環、方塊的移動、旋轉、消除行以及得分等邏輯。但從這段代碼中,我們可以看出這是一個俄羅斯方塊游戲的基礎框架,接下來的代碼會包含游戲的主體邏輯和界面渲染等部分。
2.3 播放音樂
def playMyMusic(src):if isMusic:sound = pygame.mixer.Sound(src)sound.play()
分析:
這個函數接收一個參數src
,這個參數應該是音樂文件的路徑。如果isMusic
變量為True
(意味著音樂功能是開啟的),那么函數會加載音樂文件并播放它。這里使用了pygame.mixer.Sound
來加載聲音文件,并調用play
方法來播放聲音。
2.4 創建方塊類
2.4.1 定義私有屬性
class Tetris:__i = 0__j = 0__color = 0__nextColor = 0__nextType = 0
分析:
1.__i
: 方塊的行位置。
2.__j
: 方塊的列位置。
3.__color
: 方塊的顏色。
4.__nextColor
: 下一個方塊的顏色。
5.__nextType
: 下一個方塊的類型。
2.4.2 構造函數__init__
這個構造函數在創建Tetris
類的實例時被調用。它初始化下一個方塊的顏色和類型,并調用createTetris
方法來創建當前的方塊。
def __init__(self):self.__nextColor = random.randint(0, 3) + 1 # 一共四種顏色self.__nextType = random.randint(0, 6) # 一共七種類型self.createTetris()
2.4.3 createTetris
方法
def createTetris(self):# 根據類型調整一下快出現的位置if self.__nextType == 0:self.__i = 1self.__j = 7else:self.__i = 2self.__j = 6self.__color = self.__nextColor# 根據方塊模板,放置整個到地圖for i in range(4):for j in range(4):if tetrises[self.__nextType][i][j] == 1:if gameMap[self.__i + i][self.__j + j] == 0:gameMap[self.__i + i][self.__j + j] = self.__colorelse:print('游戲失敗!')exit()return -1# 產生下一種類型和顏色self.__nextColor = random.randint(0, 3) + 1 # 一共四種顏色self.__nextType = random.randint(0, 6) # 一共七種類型playMyMusic('music/get.wav')
分析:
該段代碼的主要功能是創建一個新的俄羅斯方塊(Tetris)并將其放置在游戲地圖上。以下是代碼的主要功能概述:
確定方塊的初始位置:根據方塊的類型(self.__nextType),代碼確定了方塊在游戲地圖上的初始位置(self.__i 和 self.__j)。不同的方塊類型可能需要不同的起始位置。
**設置方塊的顏色:**將方塊的顏色設置為下一個可用的顏色(self.__nextColor)。顏色是通過一個隨機數生成的,確保每種顏色都有可能被選中。
**放置方塊到游戲地圖:**通過兩個嵌套的循環,代碼遍歷方塊的每個格子。如果方塊模板中的某個格子值為1(表示該格子被填充),則檢查游戲地圖中對應的位置是否為空(值為0)。如果為空,則在該位置放置方塊(即將該位置的值設置為方塊的顏色)。如果對應位置已經被其他方塊占據,則輸出“游戲失敗!”并退出游戲。
**生成下一種方塊類型和顏色:**在成功放置當前方塊后,代碼生成下一種方塊的類型和顏色。這是通過隨機數生成器實現的,確保每次生成的方塊類型和顏色都是隨機的。
**播放音效:**放置方塊后,播放一個音效文件(‘music/get.wav’),為玩家提供反饋。
總的來說,這段代碼是俄羅斯方塊游戲中生成和放置新方塊的關鍵部分,它負責處理方塊的初始位置、顏色、放置邏輯以及生成下一種方塊。
def moveDown(self):global gameMap# 判斷是否可以下移for j in range(4):for i in range(3, -1, -1):if gameMap[self.__i + i][self.__j + j] == self.__color:# 判斷是否到底if self.__i + i + 1 > MAX_I:return 1# 判斷前面是否有東西if gameMap[self.__i + i + 1][self.__j + j] != 0:return 1break# 下移for j in range(4):for i in range(3, -1, -1):if gameMap[self.__i + i][self.__j + j] == self.__color:gameMap[self.__i + i][self.__j + j], gameMap[self.__i + i + 1][self.__j + j] = \gameMap[self.__i + i + 1][self.__j + j], gameMap[self.__i + i][self.__j + j]self.__i += 1
分析:
該段代碼的主要功能是處理俄羅斯方塊游戲中方塊的下移操作。以下是代碼的主要功能概述:
**判斷是否可以下移:**通過兩個嵌套的循環遍歷方塊的每個格子,檢查當前方塊是否可以下移。首先,它檢查方塊是否已經到達地圖的底部(即是否超過了最大行數MAX_I),如果是,則不能下移,函數返回1。其次,它檢查方塊的下方是否有其他方塊占據,如果有,則不能下移,函數同樣返回1。
**執行下移操作:**如果上述檢查都通過,說明方塊可以下移,代碼將執行下移操作。通過另一個兩個嵌套的循環遍歷方塊的每個格子,找到當前方塊的每個格子,并將其與其下方的對應格子交換位置。這實際上是將整個方塊向下移動了一行。
**更新方塊位置:**在成功執行下移操作后,代碼更新方塊的__i屬性值,將其增加1,以反映方塊在游戲地圖中的新位置。
這個函數的目的是處理方塊的下移邏輯,包括檢查是否可以下移、執行下移操作以及更新方塊的位置。它是俄羅斯方塊游戲中控制方塊移動的重要部分。
def stopTetris(self):global tetrisHeightflag = Truefor i in range(4):for j in range(4):if gameMap[self.__i + i][self.__j + j] == self.__color:gameMap[self.__i + i][self.__j + j] += 10# 找到第一個顏色方塊if flag:tetrisHeight = MAX_I - self.__i if tetrisHeight < MAX_I - self.__i else tetrisHeightflag = Falseself.deleteRow()
分析:
該段代碼的主要功能是停止當前正在下落的俄羅斯方塊,并對其進行處理。以下是代碼的主要功能概述:
標記方塊停止狀態:首先,代碼通過遍歷當前方塊的每個格子,將其顏色值增加10,以標記這些格子屬于已經停止下落的方塊。這樣做通常是為了在游戲地圖上區分正在下落和已經停止的方塊。
確定方塊高度:在標記方塊的過程中,代碼還查找并記錄了當前方塊的最高位置(即方塊中最頂部的行索引)。這是通過比較當前方塊最高行與全局變量tetrisHeight
的值來完成的,確保tetrisHeight
始終存儲當前游戲地圖中最高的方塊高度。
刪除完整行:在標記完方塊并確定了其高度后,代碼調用deleteRow
函數來檢查并刪除游戲地圖中的完整行。這是俄羅斯方塊游戲中的一個重要機制,當玩家堆疊的方塊形成完整的一行時,該行會被消除,并為玩家帶來分數和可能的等級提升。
總的來說,這段代碼負責在方塊停止下落時對其進行處理,包括標記方塊狀態、確定方塊高度以及觸發行消除機制。這是俄羅斯方塊游戲中管理方塊狀態和控制游戲邏輯的關鍵部分。
def moveLeft(self):# 判斷是否能夠左移for i in range(4):for j in range(4):if gameMap[self.__i + i][self.__j + j] == self.__color:if self.__j + j - 1 < 0:return 1if gameMap[self.__i + i][self.__j + j - 1] != 0:return 1break# 左移for i in range(4):for j in range(4):if gameMap[self.__i + i][self.__j + j] == self.__color:gameMap[self.__i + i][self.__j + j], gameMap[self.__i + i][self.__j + j - 1] = \gameMap[self.__i + i][self.__j + j - 1], gameMap[self.__i + i][self.__j + j]self.__j -= 1
分析:
該段代碼的主要功能是處理俄羅斯方塊游戲中方塊的左移操作。以下是代碼的主要功能概述:
判斷是否可以左移:代碼首先通過兩個嵌套的循環遍歷方塊的每個格子,檢查當前方塊是否可以向左移動。它首先檢查方塊的左側是否有足夠的空間(即是否超出了地圖的左邊界),如果是,則不能左移,函數返回1。其次,它檢查方塊的左側是否有其他方塊占據,如果有,則不能左移,函數同樣返回1。
執行左移操作:如果上述檢查都通過,說明方塊可以左移,代碼將執行左移操作。通過另一個兩個嵌套的循環遍歷方塊的每個格子,找到當前方塊的每個格子,并將其與其左側的對應格子交換位置。這實際上是將整個方塊向左移動了一列。
更新方塊位置:在成功執行左移操作后,代碼更新方塊的__j
屬性值,將其減少1,以反映方塊在游戲地圖中的新位置。
這個函數的目的是處理方塊的左移邏輯,包括檢查是否可以左移、執行左移操作以及更新方塊的位置。它是俄羅斯方塊游戲中控制方塊移動的重要部分。
def moveRight(self):# 判斷是否能右移for i in range(4):for j in range(3, -1, -1):if gameMap[self.__i + i][self.__j + j] == self.__color:if self.__j + j + 1 >= MAX_J:return 1if gameMap[self.__i + i][self.__j + j + 1] != 0:return 1break# 右移for i in range(4):for j in range(3, -1, -1):if gameMap[self.__i + i][self.__j + j] == self.__color:gameMap[self.__i + i][self.__j + j], gameMap[self.__i + i][self.__j + j + 1] = \gameMap[self.__i + i][self.__j + j + 1], gameMap[self.__i + i][self.__j + j]self.__j += 1
分析:
該段代碼的主要功能是處理俄羅斯方塊游戲中方塊的右移操作。以下是代碼的主要功能概述:
判斷是否可以右移:代碼首先通過兩個嵌套的循環遍歷方塊的每個格子,檢查當前方塊是否可以向右移動。它首先檢查方塊的右側是否有足夠的空間(即是否超出了地圖的右邊界),如果是,則不能右移,函數返回1。其次,它檢查方塊的右側是否有其他方塊占據,如果有,則不能右移,函數同樣返回1。
執行右移操作:如果上述檢查都通過,說明方塊可以右移,代碼將執行右移操作。通過另一個兩個嵌套的循環遍歷方塊的每個格子,找到當前方塊的每個格子,并將其與其右側的對應格子交換位置。這實際上是將整個方塊向右移動了一列。
更新方塊位置:在成功執行右移操作后,代碼更新方塊的__j
屬性值,將其增加1,以反映方塊在游戲地圖中的新位置。
這個函數的目的是處理方塊的右移邏輯,包括檢查是否可以右移、執行右移操作以及更新方塊的位置。它是俄羅斯方塊游戲中控制方塊移動的重要部分。與左移操作類似,但方向相反。
def change(self):tMap = [[0 for j in range(4)] for i in range(4)]# 將所有方塊順時針旋轉90度賦值到 tMap 中i = 0k = 3while i < 4:for j in range(4):if MAX_I > self.__i + j >= 0 and MAX_J > self.__j + k >= 0 and gameMap[self.__i + j][self.__j + k] == 0 or \gameMap[self.__i + j][self.__j + k] == self.__color:tMap[j][k] = gameMap[self.__i + i][self.__j + j]else:returni += 1k -= 1# 賦值for i in range(4):for j in range(4):gameMap[self.__i + i][self.__j + j] = tMap[i][j]playMyMusic('music/change.wav')
分析:
該段代碼的主要功能是改變(旋轉)當前方塊的狀態,具體來說是將方塊順時針旋轉90度。以下是代碼的主要功能概述:
創建臨時地圖:首先,代碼創建了一個名為tMap
的4x4二維列表,用于存儲旋轉后的方塊狀態。這個列表初始時所有元素都為0。
順時針旋轉方塊:接下來,代碼通過兩個嵌套的循環來實現方塊的順時針旋轉。外部循環控制旋轉的行,內部循環控制旋轉的列。在循環中,代碼檢查原始方塊(在gameMap
中)的每個格子,如果該格子為空(值為0)或者與當前方塊的顏色相同,則將其值復制到tMap
中相應的位置。如果遇到任何障礙物(即非空且顏色不同的格子),旋轉操作將立即停止,并返回(不執行任何旋轉)。
更新游戲地圖:如果旋轉操作成功完成(即沒有遇到障礙物),代碼將tMap
中的新狀態復制回gameMap
,從而更新游戲地圖中方塊的位置和狀態。
播放音效:最后,代碼調用playMyMusic
函數播放一個名為’change.wav’的音效文件,為玩家提供旋轉操作的反饋。
總的來說,這個函數的作用是改變當前方塊的方向,將其順時針旋轉90度,并更新游戲地圖以反映這一變化。如果旋轉過程中遇到障礙物,則不會進行任何更改。
def deleteRow(self):# 找到有方塊的最后一行lastRow = 0t = Falsefor i in range(3, -1, -1):for j in range(4):if gameMap[self.__i + i][self.__j + j] == self.__color + 10:lastRow = self.__i + it = Truebreakif t:breakfor i in range(lastRow, MAX_I - tetrisHeight - 1, -1):for j in range(MAX_J):if gameMap[i][j] == 0:breakelse:global scorescore += 10playMyMusic('music/delete.wav')# 刪除行gameMap.pop(i)gameMap.insert(0, [0 for j in range(MAX_J + 3)])# 增加等級global levellevel += score // 1000# 再次調用刪除行函數操作刪行self.deleteRow()
分析:
該段代碼的主要功能是刪除俄羅斯方塊游戲中由玩家堆疊形成的完整行,并處理相應的得分和等級提升。以下是代碼的主要功能概述:
找到有方塊的最后一行:代碼首先通過兩個嵌套的循環遍歷當前方塊及其下方的行,查找最下方有方塊的行(即最頂層的完整行)。它檢查每個格子的值是否等于當前方塊的顏色值加10(這是標記已停止方塊的方式)。一旦找到這樣的格子,就記錄下其所在的行號,并跳出循環。
**刪除完整行:**接下來,代碼從最后一行開始向上遍歷游戲地圖。對于每一行,它檢查該行是否所有格子都非空(即該行是一個完整行)。如果找到一個完整行,它將執行以下操作:
- 增加玩家得分(通常增加10分)。
- 播放一個音效文件(‘delete.wav’),為玩家提供反饋。
- 從游戲地圖中移除該完整行。這是通過將該行從列表中移除(gameMap.pop(i))并在列表的開頭插入一個新行(所有格子都為空)來實現的。
- 更新玩家的等級。這是通過將玩家的得分除以1000并向上取整來實現的,每1000分提升一個等級。
遞歸調用:在刪除一行后,代碼會再次調用自身(self.deleteRow()
),以檢查是否由于刪除行而形成了新的完整行。這是必要的,因為在某些情況下,刪除一行可能會導致上方的多行也成為完整行。遞歸調用將繼續進行,直到沒有新的完整行可以刪除為止。
這個函數的目的是處理玩家堆疊形成的完整行,給予玩家相應的得分,并更新游戲狀態,包括游戲地圖、玩家得分和等級。它是俄羅斯方塊游戲中的重要機制之一,用于管理游戲進程和玩家表現。
def nextTetris(self):return self.__nextType, self.__nextColor# 全局變量
screen = '' # 屏幕
gameTetris = Tetris()
分析:
該方法用于返回下一個方塊的類型和顏色。它返回兩個私有成員變量:self.__nextType
和 self.__nextColor
。這兩個變量通常在類的其他部分被設置,表示下一個將要出現的方塊的類型(形狀)和顏色。
此外,代碼還定義了一個全局變量 screen
,它被初始化為空字符串。這個變量可能用于表示游戲的屏幕或顯示界面,但在這段代碼中并沒有進一步的使用或說明。
另一個全局變量 gameTetris
是 Tetris
類的一個實例。Tetris
類可能包含了俄羅斯方塊游戲的所有邏輯和狀態信息,但由于代碼中并沒有給出 Tetris
類的具體實現,所以無法確定其具體的功能和結構。通常,這樣的類會包含游戲的狀態(如當前方塊、下一個方塊、游戲地圖、得分等),以及控制游戲進程的方法(如開始新游戲、移動方塊、旋轉方塊、刪除行等)。
2.5 繪制游戲地圖
# 繪制游戲地圖
def drawMap():# 畫上邊框for i in range(MAX_I - 4):# 畫右邊myRect(screen, COLORS[2], [0, i * SIZE, SIZE, SIZE])# 畫左邊myRect(screen, COLORS[2], [(MAX_J + 1) * SIZE, i * SIZE, SIZE, SIZE])# 畫下面for i in range(MAX_J + 2):myRect(screen, COLORS[2], [i * SIZE, (MAX_I - 4) * SIZE, SIZE, SIZE])# 給地圖涂色for i in range(4, MAX_I):for j in range(MAX_J):t = gameMap[i][j] - 10 if gameMap[i][j] > 10 else gameMap[i][j]myRect(screen, COLORS[t], [(j + 1) * SIZE, (i - 4) * SIZE, SIZE, SIZE])# 文字內容,下一個方塊drawMyText(screen, "下一塊:", 305, 18, 15, (255, 255, 255))# 繪制下一塊方塊startX = 270startY = 30nextType, nextColor = gameTetris.nextTetris()# 顯示下一個方塊的背景pygame.draw.rect(screen, COLORS[5], [startX, startY, SIZE * 4, SIZE * 4], 1)mySize = SIZE * 0.8 # 縮小下一個方塊大小# 根據形狀,修改方塊的位置(居中)if nextType == 0:startX += (SIZE * 4 - mySize) / 2startY += (SIZE * 4 - mySize * 4) / 2elif nextType == 2 or nextType == 3:startX += (SIZE * 4 - mySize * 2) / 2startY += (SIZE * 4 - mySize * 3) / 2elif nextType == 4 or nextType == 5 or nextType == 6:startX += (SIZE * 4 - mySize * 3) / 2startY += (SIZE * 4 - mySize * 2) / 2elif nextType == 1:startX += (SIZE * 4 - mySize * 4) / 2startY += (SIZE * 4 - mySize * 4) / 2# 繪制下一個方塊for i in range(4):for j in range(4):# 繪制有圖形地方if tetrises[nextType][i][j] == 1:# 根據不同的myRect(screen, COLORS[nextColor], [startX + j * mySize, startY + i * mySize, mySize, mySize])color = (255, 0, 0)# 繪制分數scoreText = "分數:{}".format(score)drawMyText(screen, scoreText, 300, 120, 15, color)# 等級drawMyText(screen, "等級:{}".format(level), 300, 140, 15, color)color = (255, 0, 255)# 繪制音樂textMusic = "音樂:"if isMusic:textMusic += "開"else:textMusic += '關'drawMyText(screen, textMusic, 300, 220, 15, color)# 繪制游戲暫停textPause = "暫停:"if isPause:textPause += "開"else:textPause += '關'drawMyText(screen, textPause, 300, 240, 15, color)# 繪制鍵值color = (130, 205, 255)drawMyText(screen, "↑ 旋轉90度", 300, 300, 15, color)drawMyText(screen, "← 向左移動", 300, 320, 15, color)drawMyText(screen, "→ 向右移動", 300, 340, 15, color)drawMyText(screen, "↓ 快速向下", 300, 360, 15, color)# 繪制背景startY = 410color = (255, 255, 0)flower = "★"drawMyText(screen, flower, 300, startY + 0, 15, color)flower = "★★"drawMyText(screen, flower, 300, startY + 20, 15, color)flower = "★★★"drawMyText(screen, flower, 300, startY + 40, 15, color)
分析:
該段代碼的主要功能是繪制俄羅斯方塊游戲的游戲地圖和界面上的其他元素。具體來說,它執行以下操作:
繪制游戲地圖邊框:首先,代碼通過循環繪制了游戲地圖的上、下、左、右四個邊框。這些邊框使用了一個特定的顏色(COLORS[2]
),并且根據游戲地圖的尺寸(MAX_I
和 MAX_J
)來確定位置和大小。
填充游戲地圖:接下來,代碼遍歷游戲地圖的每個格子,并根據格子的值(gameMap[i][j]
)來填充相應的顏色。如果格子的值大于10,則減去10來獲取正確的顏色索引,因為游戲可能使用額外的值來表示特殊狀態(如方塊停止下落)。
繪制下一個方塊:代碼從gameTetris
實例中獲取下一個方塊的類型(nextType
)和顏色(nextColor
)。然后,它繪制一個表示下一個方塊的背景框,并根據下一個方塊的形狀調整其內部小方塊的位置。不同的方塊類型有不同的形狀,因此代碼通過條件語句來確定如何繪制每個小方塊。
繪制文本信息:代碼還繪制了界面上的文本信息,包括“下一塊:”來提示玩家下一個方塊即將出現,以及玩家的“分數”和“等級”。這些信息使用特定的顏色和字體大小繪制在屏幕上。
繪制音樂圖標:最后,代碼繪制了一個表示音樂的圖標(盡管代碼中并沒有實際繪制圖標的操作,只是預留了位置)。
2.6 游戲初始化
# 游戲初始化
def initGame():# 將地圖倒數對大行全部賦值為-1for j in range(MAX_J):gameMap[MAX_I][j] = -1# 將地圖倒數最大列全部賦值為-1for i in range(MAX_I):gameMap[i][MAX_J] = -1# 分數初始化score = 0# 加載背景音樂文件pygame.mixer.music.load('music/bgm.mp3')if isMusic:# 播放背景音樂,第一個參數為播放的次數(-1表示無限循環),第二個參數是設置播放的起點(單位為秒)pygame.mixer.music.play(-1, 0.0)
分析:
該段代碼的主要功能是初始化俄羅斯方塊游戲。具體來說,它執行了以下操作:
設置游戲地圖的邊緣:代碼通過兩個循環將游戲地圖的邊緣格子(即最底部和最右側的格子)設置為-1。這通常表示這些格子不屬于游戲區域,或者不可用于放置方塊。
初始化分數:將玩家的分數(score
)設置為0,表示游戲開始時玩家沒有獲得任何分數。
加載并播放背景音樂:代碼使用pygame
模塊的mixer
組件加載一個名為’bgm.mp3’的背景音樂文件。如果isMusic
變量為True
(表示允許播放音樂),則音樂將開始播放,并且設置為無限循環(-1表示循環播放),從音樂的開頭(0.0秒處)開始播放。
這段代碼是俄羅斯方塊游戲初始化過程的一部分,確保游戲在開始時有正確的設置和配置,包括游戲地圖的邊緣定義、玩家分數的初始值,以及背景音樂的加載和播放。這些初始化步驟是游戲能夠正常運行所必需的。
2.7 繪制有邊框矩形
def myRect(screen, colorRect, pos, lineColor=COLORS[0], bold=1):pygame.draw.rect(screen, colorRect, pos)pygame.draw.rect(screen, lineColor, pos, bold)
分析:
該函數 myRect
的主要功能是在游戲屏幕(screen
)上繪制一個矩形。它接受以下參數:
screen
:這是一個pygame的Surface對象,通常代表游戲的主屏幕或窗口。colorRect
:這是矩形的填充顏色,通常是一個RGB元組,如(255, 255, 255)
表示白色。pos
:這是一個包含矩形位置和大小的元組,格式為(x, y, width, height)
。lineColor
:這是一個可選參數,用于指定矩形邊框的顏色。如果沒有提供,則默認為COLORS[0]
。bold
:這是一個可選參數,用于指定矩形邊框的寬度。如果沒有提供,則默認為1。
函數內部調用了兩次 pygame.draw.rect
方法。第一次調用繪制了填充了顏色的矩形,第二次調用繪制了矩形的邊框。通過這種方式,myRect
函數能夠在一個步驟中同時繪制矩形的填充和邊框。
需要注意的是,pygame.draw.rect
方法的第四個參數通常用于指定線條的粗細(邊框寬度)。在這個函數中,當 bold
參數被傳遞時,它會被用作 pygame.draw.rect
方法的第四個參數。如果 bold
參數未被傳遞或設置為1,則邊框寬度將默認為1。
2.8 繪制我的文字
# 繪制我的文字
def drawMyText(screen, text, x, y, bold, fColor, bColor=None):# 通過字體文件獲得字體對象fontObj = pygame.font.Font('font/SIMYOU.TTF', bold)# 配置要顯示的文字textSurfaceObj = fontObj.render(text, False, fColor, bColor)# 獲得要顯示的對象的recttextRectObj = textSurfaceObj.get_rect()# 設置顯示對象的坐標textRectObj.center = (x, y)# 繪制字體screen.blit(textSurfaceObj, textRectObj)
分析:
獲取字體對象:使用pygame.font.Font
函數和一個指定的字體文件(在這里是’font/SIMYOU.TTF’)以及字體大小(由bold
參數指定)來創建一個字體對象。
渲染文本:使用字體對象的render
方法來創建一個文本Surface對象。這個方法需要文本字符串、一個布爾值(在這里是False
,表示文本不是抗鋸齒的)、前景色和可選的背景色。
獲取文本矩形:通過調用文本Surface對象的get_rect
方法來獲取一個矩形對象,這個矩形代表了文本在屏幕上占據的空間。
設置文本坐標:將文本矩形的中心坐標設置為函數參數中指定的x
和y
坐標。
繪制文本:最后,使用screen.blit
方法將文本Surface對象繪制到屏幕上的指定位置。
2.9 游戲主體
def my_game():# 得到屏幕global screen, isMusic, isPausescreen = pygame.display.set_mode((350, 465))# 設置標題pygame.display.set_caption('俄羅斯方塊')# 初始化游戲initGame()# 用于控制下落速度count = 0while True:if count == 3 + level * 2:if not isPause: # 只有暫停是 False 才能下移if gameTetris.moveDown() == 1:gameTetris.stopTetris()gameTetris.createTetris()count = 0# 事件判斷for event in pygame.event.get():if event.type == pygame.QUIT:# 停止播放背景音樂pygame.mixer.music.stop()pygame.quit()exit()elif event.type == pygame.MOUSEBUTTONDOWN:# 控制音樂開關if 270 < event.pos[0] < 330 and 210 < event.pos[1] < 225:# 處理音樂開關if isMusic:# 停止播放背景音樂pygame.mixer.music.stop()else:# 播放背景音樂,第一個參數為播放的次數(-1表示無限循環),第二個參數是設置播放的起點(單位為秒)pygame.mixer.music.play(-1, 0.0)isMusic = not isMusic# 處理游戲暫停開關if 270 < event.pos[0] < 327 and 233 < event.pos[1] < 248:isPause = not isPauseif isPause:pygame.display.set_caption('俄羅斯方塊:已暫停')else:pygame.display.set_caption('俄羅斯方塊')elif event.type == pygame.KEYDOWN:if event.key == K_UP:gameTetris.change()keyPressList = pygame.key.get_pressed()if keyPressList[K_DOWN]:gameTetris.moveDown()elif keyPressList[K_LEFT]:gameTetris.moveLeft()elif keyPressList[K_RIGHT]:gameTetris.moveRight()screen.fill((0, 0, 0))drawMap()pygame.display.update()count += 1pygame.time.Clock().tick(10)
分析:
該函數 my_game
是俄羅斯方塊游戲的主函數,它負責初始化游戲、處理用戶輸入和事件、更新游戲狀態以及渲染游戲畫面。以下是該函數的主要功能:
初始化游戲:首先,它設置了一個全局的屏幕對象 screen
和一些全局變量(如 isMusic
和 isPause
)。然后,它調用 initGame
函數來初始化游戲,包括設置游戲地圖的邊緣、初始化分數以及加載并可能播放背景音樂。
控制下落速度:通過一個計數器 count
來控制方塊的下落速度。當計數器達到一定的值時(由當前游戲級別 level
決定),如果游戲沒有暫停(isPause
為 False
),則嘗試將當前方塊下移一格。如果下移成功,則停止當前方塊的下落并創建一個新的方塊。
**事件處理:**函數使用一個循環來處理pygame事件。這些事件可能包括用戶點擊關閉按鈕(退出游戲)、點擊鼠標(控制音樂的播放和暫停以及游戲的暫停和繼續)、以及按下鍵盤鍵(旋轉方塊)。
- 當用戶點擊關閉按鈕時,停止播放背景音樂并退出游戲。
- 當用戶在特定的屏幕區域內點擊鼠標時,切換音樂的播放狀態或游戲的暫停狀態。
- 當用戶按下鍵盤上的上箭頭鍵時,旋轉當前方塊。
鍵盤輸入處理:函數還檢查是否有鍵盤鍵被按下,并據此移動方塊。如果下箭頭鍵被持續按下,方塊會持續下移;如果左或右箭頭鍵被按下,方塊會向左或向右移動。
渲染游戲畫面:最后,函數使用黑色填充屏幕,并調用 drawMap
函數來繪制游戲地圖和方塊。這一步通常在每次循環的末尾進行,以確保游戲畫面能夠實時更新。
2.10 主程序運行
if __name__ == '__main__':my_game()
詳細分析一下:
在Python中,if __name__ == '__main__':
是一個常見的代碼塊,用于判斷當前腳本是否作為主程序運行。如果當前腳本是作為主程序直接運行的,而不是被其他腳本導入作為一個模塊,那么 __name__
這個內置變量就會被設置為 '__main__'
。
在你提供的代碼片段中,if __name__ == '__main__':
這一行之后緊跟著調用了 my_game()
函數。這意味著,只有當這個腳本被直接運行時,my_game()
函數才會被執行。如果這個腳本被其他腳本導入作為一個模塊,那么 my_game()
函數將不會自動執行。
這樣做的好處是,你可以在一個文件中既定義函數和類(用于在其他腳本中導入和使用),又包含一些只在當前腳本直接運行時才需要執行的代碼(比如啟動游戲的主函數)。
總結來說,if __name__ == '__main__':
這一行用于區分直接運行和模塊導入兩種情況,并據此決定是否執行某些代碼。在你提供的代碼中,它確保了 my_game()
函數只有在該腳本被直接運行時才會被執行。
3.運行效果
4.注意事項
1.所有的代碼放到同一個py文件中
2.在數據可視化過程中,難免會需要導入不同的庫,這里建議使用WIN+R打開命令提示符,并使用國內鏡像安裝庫(下載快),比如安裝wordcloud庫是,使用下列代碼:
pip install wordcloud -i https://pypi.tuna.tsinghua.edu.cn/simple/
安裝庫的時候,建議更新一下你的pip庫,命令:
python -m pip install --upgrade pip
游戲窗口的創建和初始化:你需要使用Pygame庫創建一個游戲窗口,并設置其寬度、高度、標題等屬性。你還需要初始化一些Pygame模塊,如pygame.init()
來初始化所有導入的Pygame模塊。
游戲循環:游戲的核心是一個循環,通常稱為游戲主循環或事件循環。在這個循環中,你需要處理用戶輸入(如鍵盤按鍵、鼠標點擊等)、更新游戲狀態(如方塊的位置、得分等)和繪制游戲界面。
方塊的繪制和移動:你需要定義方塊的形狀和顏色,并在游戲窗口中繪制它們。你還需要實現方塊的移動功能,包括旋轉、左右移動和下落。你可能需要使用鍵盤事件來捕捉用戶的輸入,并根據輸入更新方塊的位置。
消除行和得分:當方塊下落并填滿一行時,你需要實現消除行的功能,并相應地更新得分。這可能需要你檢查每一行是否已經填滿,如果是,則消除該行并增加得分。
游戲結束條件:你需要設置游戲結束的條件,如當方塊堆積到游戲窗口的頂部時,游戲結束。在游戲結束時,你可以顯示一條消息或彈出一個對話框來通知玩家。
5.附錄源碼
import pygame
from pygame.locals import *
from sys import exit
import random# 使用pygame.init()初始化pygame庫
pygame.init()# 常量
MAX_I = 34
MAX_J = 15
# 定義地圖的最大行數和列數
SIZE = 15
# 方塊的大小COLORS = [(0, 0, 0), (255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0), (255, 255, 255)]
# 一個顏色列表,用于表示不同的方塊
gameMap = [[0 for j in range(MAX_J + 3)] for i in range(MAX_I + 3)]
# 一個二維列表,代表游戲地圖。初始化為全0,表示沒有方塊。
tetrisHeight = 0
# 記錄當前塔的高度,即已放下的方塊行數tetrises = [[[1, 0, 0, 0],[1, 0, 0, 0],[1, 0, 0, 0],[1, 0, 0, 0]],[[0, 0, 0, 0],[0, 1, 1, 0],[0, 1, 1, 0],[0, 0, 0, 0]],[[1, 0, 0, 0],[1, 1, 0, 0],[0, 1, 0, 0],[0, 0, 0, 0]],[[0, 1, 0, 0],[1, 1, 0, 0],[1, 0, 0, 0],[0, 0, 0, 0]],[[0, 1, 0, 0],[1, 1, 1, 0],[0, 0, 0, 0],[0, 0, 0, 0]],[[1, 0, 0, 0],[1, 1, 1, 0],[0, 0, 0, 0],[0, 0, 0, 0]],[[0, 0, 1, 0],[1, 1, 1, 0],[0, 0, 0, 0],[0, 0, 0, 0]]
]
# 一個列表,包含7種不同的基本方塊形狀。每個形狀都是一個二維列表,表示方塊的各個部分。
# 分數
score = 0
# 等級
level = 1
# 音樂開關
isMusic = True
# 游戲暫停
isPause = False# 播放音樂
def playMyMusic(src):if isMusic:sound = pygame.mixer.Sound(src)sound.play()# 方塊類
class Tetris:__i = 0__j = 0__color = 0__nextColor = 0__nextType = 0def __init__(self):self.__nextColor = random.randint(0, 3) + 1 # 一共四種顏色self.__nextType = random.randint(0, 6) # 一共七種類型self.createTetris()def createTetris(self):# 根據類型調整一下快出現的位置if self.__nextType == 0:self.__i = 1self.__j = 7else:self.__i = 2self.__j = 6self.__color = self.__nextColor# 根據方塊模板,放置整個到地圖for i in range(4):for j in range(4):if tetrises[self.__nextType][i][j] == 1:if gameMap[self.__i + i][self.__j + j] == 0:gameMap[self.__i + i][self.__j + j] = self.__colorelse:print('游戲失敗!')exit()return -1# 產生下一種類型和顏色self.__nextColor = random.randint(0, 3) + 1 # 一共四種顏色self.__nextType = random.randint(0, 6) # 一共七種類型playMyMusic('music/get.wav')def moveDown(self):global gameMap# 判斷是否可以下移for j in range(4):for i in range(3, -1, -1):if gameMap[self.__i + i][self.__j + j] == self.__color:# 判斷是否到底if self.__i + i + 1 > MAX_I:return 1# 判斷前面是否有東西if gameMap[self.__i + i + 1][self.__j + j] != 0:return 1break# 下移for j in range(4):for i in range(3, -1, -1):if gameMap[self.__i + i][self.__j + j] == self.__color:gameMap[self.__i + i][self.__j + j], gameMap[self.__i + i + 1][self.__j + j] = \gameMap[self.__i + i + 1][self.__j + j], gameMap[self.__i + i][self.__j + j]self.__i += 1def stopTetris(self):global tetrisHeightflag = Truefor i in range(4):for j in range(4):if gameMap[self.__i + i][self.__j + j] == self.__color:gameMap[self.__i + i][self.__j + j] += 10# 找到第一個顏色方塊if flag:tetrisHeight = MAX_I - self.__i if tetrisHeight < MAX_I - self.__i else tetrisHeightflag = Falseself.deleteRow()def moveLeft(self):# 判斷是否能夠左移for i in range(4):for j in range(4):if gameMap[self.__i + i][self.__j + j] == self.__color:if self.__j + j - 1 < 0:return 1if gameMap[self.__i + i][self.__j + j - 1] != 0:return 1break# 左移for i in range(4):for j in range(4):if gameMap[self.__i + i][self.__j + j] == self.__color:gameMap[self.__i + i][self.__j + j], gameMap[self.__i + i][self.__j + j - 1] = \gameMap[self.__i + i][self.__j + j - 1], gameMap[self.__i + i][self.__j + j]self.__j -= 1def moveRight(self):# 判斷是否能右移for i in range(4):for j in range(3, -1, -1):if gameMap[self.__i + i][self.__j + j] == self.__color:if self.__j + j + 1 >= MAX_J:return 1if gameMap[self.__i + i][self.__j + j + 1] != 0:return 1break# 右移for i in range(4):for j in range(3, -1, -1):if gameMap[self.__i + i][self.__j + j] == self.__color:gameMap[self.__i + i][self.__j + j], gameMap[self.__i + i][self.__j + j + 1] = \gameMap[self.__i + i][self.__j + j + 1], gameMap[self.__i + i][self.__j + j]self.__j += 1def change(self):tMap = [[0 for j in range(4)] for i in range(4)]# 將所有方塊順時針旋轉90度賦值到 tMap 中i = 0k = 3while i < 4:for j in range(4):if MAX_I > self.__i + j >= 0 and MAX_J > self.__j + k >= 0 and gameMap[self.__i + j][self.__j + k] == 0 or \gameMap[self.__i + j][self.__j + k] == self.__color:tMap[j][k] = gameMap[self.__i + i][self.__j + j]else:returni += 1k -= 1# 賦值for i in range(4):for j in range(4):gameMap[self.__i + i][self.__j + j] = tMap[i][j]playMyMusic('music/change.wav')def deleteRow(self):# 找到有方塊的最后一行lastRow = 0t = Falsefor i in range(3, -1, -1):for j in range(4):if gameMap[self.__i + i][self.__j + j] == self.__color + 10:lastRow = self.__i + it = Truebreakif t:breakfor i in range(lastRow, MAX_I - tetrisHeight - 1, -1):for j in range(MAX_J):if gameMap[i][j] == 0:breakelse:global scorescore += 10playMyMusic('music/delete.wav')# 刪除行gameMap.pop(i)gameMap.insert(0, [0 for j in range(MAX_J + 3)])# 增加等級global levellevel += score // 1000# 再次調用刪除行函數操作刪行self.deleteRow()def nextTetris(self):return self.__nextType, self.__nextColor# 全局變量
screen = '' # 屏幕
gameTetris = Tetris()# 繪制游戲地圖
def drawMap():# 畫上邊框for i in range(MAX_I - 4):# 畫右邊myRect(screen, COLORS[2], [0, i * SIZE, SIZE, SIZE])# 畫左邊myRect(screen, COLORS[2], [(MAX_J + 1) * SIZE, i * SIZE, SIZE, SIZE])# 畫下面for i in range(MAX_J + 2):myRect(screen, COLORS[2], [i * SIZE, (MAX_I - 4) * SIZE, SIZE, SIZE])# 給地圖涂色for i in range(4, MAX_I):for j in range(MAX_J):t = gameMap[i][j] - 10 if gameMap[i][j] > 10 else gameMap[i][j]myRect(screen, COLORS[t], [(j + 1) * SIZE, (i - 4) * SIZE, SIZE, SIZE])# 文字內容,下一個方塊drawMyText(screen, "下一塊:", 305, 18, 15, (255, 255, 255))# 繪制下一塊方塊startX = 270startY = 30nextType, nextColor = gameTetris.nextTetris()# 顯示下一個方塊的背景pygame.draw.rect(screen, COLORS[5], [startX, startY, SIZE * 4, SIZE * 4], 1)mySize = SIZE * 0.8 # 縮小下一個方塊大小# 根據形狀,修改方塊的位置(居中)if nextType == 0:startX += (SIZE * 4 - mySize) / 2startY += (SIZE * 4 - mySize * 4) / 2elif nextType == 2 or nextType == 3:startX += (SIZE * 4 - mySize * 2) / 2startY += (SIZE * 4 - mySize * 3) / 2elif nextType == 4 or nextType == 5 or nextType == 6:startX += (SIZE * 4 - mySize * 3) / 2startY += (SIZE * 4 - mySize * 2) / 2elif nextType == 1:startX += (SIZE * 4 - mySize * 4) / 2startY += (SIZE * 4 - mySize * 4) / 2# 繪制下一個方塊for i in range(4):for j in range(4):# 繪制有圖形地方if tetrises[nextType][i][j] == 1:# 根據不同的myRect(screen, COLORS[nextColor], [startX + j * mySize, startY + i * mySize, mySize, mySize])color = (255, 0, 0)# 繪制分數scoreText = "分數:{}".format(score)drawMyText(screen, scoreText, 300, 120, 15, color)# 等級drawMyText(screen, "等級:{}".format(level), 300, 140, 15, color)color = (255, 0, 255)# 繪制音樂textMusic = "音樂:"if isMusic:textMusic += "開"else:textMusic += '關'drawMyText(screen, textMusic, 300, 220, 15, color)# 繪制游戲暫停textPause = "暫停:"if isPause:textPause += "開"else:textPause += '關'drawMyText(screen, textPause, 300, 240, 15, color)# 繪制鍵值color = (130, 205, 255)drawMyText(screen, "↑ 旋轉90度", 300, 300, 15, color)drawMyText(screen, "← 向左移動", 300, 320, 15, color)drawMyText(screen, "→ 向右移動", 300, 340, 15, color)drawMyText(screen, "↓ 快速向下", 300, 360, 15, color)# 繪制背景startY = 410color = (255, 255, 0)flower = "★"drawMyText(screen, flower, 300, startY + 0, 15, color)flower = "★★"drawMyText(screen, flower, 300, startY + 20, 15, color)flower = "★★★"drawMyText(screen, flower, 300, startY + 40, 15, color)# 游戲初始化
def initGame():# 將地圖倒數對大行全部賦值為-1for j in range(MAX_J):gameMap[MAX_I][j] = -1# 將地圖倒數最大列全部賦值為-1for i in range(MAX_I):gameMap[i][MAX_J] = -1# 分數初始化score = 0# 加載背景音樂文件pygame.mixer.music.load('music/bgm.mp3')if isMusic:# 播放背景音樂,第一個參數為播放的次數(-1表示無限循環),第二個參數是設置播放的起點(單位為秒)pygame.mixer.music.play(-1, 0.0)# 繪制有邊框矩形
def myRect(screen, colorRect, pos, lineColor=COLORS[0], bold=1):pygame.draw.rect(screen, colorRect, pos)pygame.draw.rect(screen, lineColor, pos, bold)# 繪制我的文字
def drawMyText(screen, text, x, y, bold, fColor, bColor=None):# 通過字體文件獲得字體對象fontObj = pygame.font.Font('font/SIMYOU.TTF', bold)# 配置要顯示的文字textSurfaceObj = fontObj.render(text, False, fColor, bColor)# 獲得要顯示的對象的recttextRectObj = textSurfaceObj.get_rect()# 設置顯示對象的坐標textRectObj.center = (x, y)# 繪制字體screen.blit(textSurfaceObj, textRectObj)# 游戲主體
def my_game():# 得到屏幕global screen, isMusic, isPausescreen = pygame.display.set_mode((350, 465))# 設置標題pygame.display.set_caption('俄羅斯方塊')# 初始化游戲initGame()# 用于控制下落速度count = 0while True:if count == 3 + level * 2:if not isPause: # 只有暫停是 False 才能下移if gameTetris.moveDown() == 1:gameTetris.stopTetris()gameTetris.createTetris()count = 0# 事件判斷for event in pygame.event.get():if event.type == pygame.QUIT:# 停止播放背景音樂pygame.mixer.music.stop()pygame.quit()exit()elif event.type == pygame.MOUSEBUTTONDOWN:# 控制音樂開關if 270 < event.pos[0] < 330 and 210 < event.pos[1] < 225:# 處理音樂開關if isMusic:# 停止播放背景音樂pygame.mixer.music.stop()else:# 播放背景音樂,第一個參數為播放的次數(-1表示無限循環),第二個參數是設置播放的起點(單位為秒)pygame.mixer.music.play(-1, 0.0)isMusic = not isMusic# 處理游戲暫停開關if 270 < event.pos[0] < 327 and 233 < event.pos[1] < 248:isPause = not isPauseif isPause:pygame.display.set_caption('俄羅斯方塊:已暫停')else:pygame.display.set_caption('俄羅斯方塊')elif event.type == pygame.KEYDOWN:if event.key == K_UP:gameTetris.change()keyPressList = pygame.key.get_pressed()if keyPressList[K_DOWN]:gameTetris.moveDown()elif keyPressList[K_LEFT]:gameTetris.moveLeft()elif keyPressList[K_RIGHT]:gameTetris.moveRight()screen.fill((0, 0, 0))drawMap()pygame.display.update()count += 1pygame.time.Clock().tick(10)if __name__ == '__main__':my_game()