俄罗斯方块(Tetris)是一款经典的益智游戏,游戏的目的是将落下的几何图形片(称为 “俄罗斯方块”)排列起来,填满水平线,不留空隙。当一条线被完全填满时,它就被清除了,玩家就能获得分数。随着四角棋的下落速度加快,游戏的挑战性也逐渐增加,及时排列四角棋也变得更加困难。
在本教程中,我们将指导您使用 PyGame 逐步创建一个简单的俄罗斯方块游戏。我们将介绍以下内容:
- 设置开发环境并安装 Pygame
- 创建游戏窗口并定义俄罗斯方块棋子
- 实现俄罗斯方块和俄罗斯方块类以管理游戏逻辑
- 处理用户输入和游戏事件
- 在屏幕上绘制游戏状态、分数和游戏结束信息
- 执行主游戏循环
在本教程结束时,您将拥有一个功能齐全的俄罗斯方块游戏,并可以进行游戏和自定义。这个项目还将帮助您了解使用 Python 和 Pygame 开发游戏的基础知识,并为创建其他类型的游戏打下基础。下面是它的外观:
入门
安装好 Python 后,打开终端或命令提示符,输入以下命令安装 Pygame 库:
$ pip install pygame
创建游戏窗口
现在我们的开发环境已经准备就绪,让我们开始创建游戏窗口并定义四面体形状。
首先,我们需要导入俄罗斯方块游戏所需的模块。在 Python 脚本中添加以下几行:
import sys
import pygame
import random
要使用 Pygame 库,我们需要对其进行初始化。在 import 语句后添加以下一行:
pygame.init()
定义屏幕尺寸、颜色和四角棋形状
接下来,我们需要为游戏定义一些常量,如屏幕尺寸、网格大小、颜色和四角棋的形状。在脚本中添加以下几行:
# Screen dimensions
WIDTH, HEIGHT = 800, 600
GRID_SIZE = 25# Colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
GREEN = (0, 255, 0)
COLORS = [RED, BLUE, GREEN]# Tetromino shapes
SHAPES = [[['.....','.....','.....','OOOO.','.....'],['.....','..O..','..O..','..O..','..O..']],[['.....','.....','..O..','.OOO.','.....'],['.....','..O..','.OO..','..O..','.....'],['.....','.....','.OOO.','..O..','.....'],['.....','..O..','..OO.','..O..','.....']],[['.....','.....','..OO.','.OO..','.....'],['.....','.....','.OO..','..OO.','.....'],['.....','.O...','.OO..','..O..','.....'],['.....','..O..','.OO..','.O...','.....']],[['.....','..O..','..O.','..OO.','.....'],['.....','...O.','.OOO.','.....','.....'],['.....','.OO..','..O..','..O..','.....'],['.....','.....','.OOO.','.O...','.....']],
]
在这里,我们定义了游戏窗口的宽度和高度、四角棋的网格大小以及游戏使用的颜色(白色、黑色和红色)。SHAPES
列表包含以字符串表示的四面体形状,您可以随意添加/编辑。您还可以随意调整颜色和网格大小。
实现 Tetromino 和 Tetris 类以管理游戏逻辑
现在,我们已经定义了游戏窗口和俄罗斯方块形状,让我们创建两个类来管理游戏逻辑:Tetromino 类和 Tetris 方块类。
创建 Tetromino 类
Tetromino 类将表示单个四面体,并具有位置、形状、颜色和旋转属性。在脚本中添加以下代码:
class Tetromino:def __init__(self, x, y, shape):self.x = xself.y = yself.shape = shapeself.color = random.choice(COLORS) # You can choose different colors for each shapeself.rotation = 0
创建 Tetris 类
俄罗斯方块类将处理主要的游戏逻辑。它将包含用于创建新棋子、检查走棋是否有效、清线、锁定棋子、更新游戏状态和绘制游戏的方法。在脚本中添加以下代码:
class Tetris:def __init__(self, width, height):self.width = widthself.height = heightself.grid = [[0 for _ in range(width)] for _ in range(height)]self.current_piece = self.new_piece()self.game_over = Falseself.score = 0 # Add score attribute
让我们来制作一个新的 Tetromino 棋子:
def new_piece(self):# Choose a random shapeshape = random.choice(SHAPES)# Return a new Tetromino objectreturn Tetromino(self.width // 2, 0, shape)
接下来,valid_move() 方法会检查棋子是否能移动到指定位置:
def valid_move(self, piece, x, y, rotation):"""Check if the piece can move to the given position"""for i, row in enumerate(piece.shape[(piece.rotation + rotation) % len(piece.shape)]):for j, cell in enumerate(row):try:if cell == 'O' and (self.grid[piece.y + i + y][piece.x + j + x] != 0):return Falseexcept IndexError:return Falsereturn True
clear_lines() 方法会清除已满的行,并返回已清除的行数,以便我们稍后计算分数:
def clear_lines(self):"""Clear the lines that are full and return the number of cleared lines"""lines_cleared = 0for i, row in enumerate(self.grid[:-1]):if all(cell != 0 for cell in row):lines_cleared += 1del self.grid[i]self.grid.insert(0, [0 for _ in range(self.width)])return lines_cleared
现在是锁定棋子的方法:
def lock_piece(self, piece):"""Lock the piece in place and create a new piece"""for i, row in enumerate(piece.shape[piece.rotation % len(piece.shape)]):for j, cell in enumerate(row):if cell == 'O':self.grid[piece.y + i][piece.x + j] = piece.color# Clear the lines and update the scorelines_cleared = self.clear_lines()self.score += lines_cleared * 100 # Update the score based on the number of cleared lines# Create a new pieceself.current_piece = self.new_piece()# Check if the game is overif not self.valid_move(self.current_piece, 0, 0, 0):self.game_over = Truereturn lines_cleared
update() 函数将四叶草向下移动一格:
def update(self):"""Move the tetromino down one cell"""if not self.game_over:if self.valid_move(self.current_piece, 0, 1, 0):self.current_piece.y += 1else:self.lock_piece(self.current_piece)
最后,我们制作了绘制游戏网格和当前棋子的方法:
def draw(self, screen):"""Draw the grid and the current piece"""for y, row in enumerate(self.grid):for x, cell in enumerate(row):if cell:pygame.draw.rect(screen, cell, (x * GRID_SIZE, y * GRID_SIZE, GRID_SIZE - 1, GRID_SIZE - 1))if self.current_piece:for i, row in enumerate(self.current_piece.shape[self.current_piece.rotation % len(self.current_piece.shape)]):for j, cell in enumerate(row):if cell == 'O':pygame.draw.rect(screen, self.current_piece.color, ((self.current_piece.x + j) * GRID_SIZE, (self.current_piece.y + i) * GRID_SIZE, GRID_SIZE - 1, GRID_SIZE - 1))
为在游戏中绘制文本创建辅助函数
既然我们已经创建了俄罗斯方块类,那么让我们来创建两个绘制文本的辅助函数,一个用于显示得分,另一个用于显示 “Game Over!”:
def draw_score(screen, score, x, y):"""Draw the score on the screen"""font = pygame.font.Font(None, 36)text = font.render(f"Score: {score}", True, WHITE)screen.blit(text, (x, y))def draw_game_over(screen, x, y):"""Draw the game over text on the screen"""font = pygame.font.Font(None, 48)text = font.render("Game Over", True, RED)screen.blit(text, (x, y))
这些函数需要四个参数(draw_game_over()需要三个):
- screen:要绘制的 Pygame 表面。
- score:当前游戏得分。
- x:分数显示位置的 x 坐标。
- y:分数显示位置的 y 坐标。
这些函数使用 pygame.font.Font(None, 36) 创建字体对象,使用 font.render() 方法渲染得分文本,然后使用 screen.blit() 将文本显示在屏幕上。
使游戏循环
在运行循环之前,我们首先初始化 Pygame,创建时钟对象,然后创建游戏屏幕和游戏,即俄罗斯方块对象:
def main():# Initialize pygamescreen = pygame.display.set_mode((WIDTH, HEIGHT))pygame.display.set_caption('Tetris')# Create a clock objectclock = pygame.time.Clock()# Create a Tetris objectgame = Tetris(WIDTH // GRID_SIZE, HEIGHT // GRID_SIZE)fall_time = 0fall_speed = 50 # You can adjust this value to change the falling speed, it's in milliseconds
您可以调整 fall_speed 来降低它(使其更快),或增加它使其更慢。
下面是游戏循环:
while True:# Fill the screen with blackscreen.fill(BLACK) for event in pygame.event.get():# Check for the QUIT eventif event.type == pygame.QUIT:pygame.quit()sys.exit()# Check for the KEYDOWN eventif event.type == pygame.KEYDOWN:if event.key == pygame.K_LEFT:if game.valid_move(game.current_piece, -1, 0, 0):game.current_piece.x -= 1 # Move the piece to the leftif event.key == pygame.K_RIGHT:if game.valid_move(game.current_piece, 1, 0, 0):game.current_piece.x += 1 # Move the piece to the rightif event.key == pygame.K_DOWN:if game.valid_move(game.current_piece, 0, 1, 0):game.current_piece.y += 1 # Move the piece downif event.key == pygame.K_UP:if game.valid_move(game.current_piece, 0, 0, 1):game.current_piece.rotation += 1 # Rotate the pieceif event.key == pygame.K_SPACE:while game.valid_move(game.current_piece, 0, 1, 0):game.current_piece.y += 1 # Move the piece down until it hits the bottomgame.lock_piece(game.current_piece) # Lock the piece in place# Get the number of milliseconds since the last framedelta_time = clock.get_rawtime() # Add the delta time to the fall timefall_time += delta_time if fall_time >= fall_speed:# Move the piece downgame.update()# Reset the fall timefall_time = 0# Draw the score on the screendraw_score(screen, game.score, 10, 10)# Draw the grid and the current piecegame.draw(screen)if game.game_over:# Draw the "Game Over" messagedraw_game_over(screen, WIDTH // 2 - 100, HEIGHT // 2 - 30) # Draw the "Game Over" message# You can add a "Press any key to restart" message here# Check for the KEYDOWN eventif event.type == pygame.KEYDOWN:# Create a new Tetris objectgame = Tetris(WIDTH // GRID_SIZE, HEIGHT // GRID_SIZE)# Update the displaypygame.display.flip()# Set the framerateclock.tick(60)
主游戏循环是一个 while 循环,不断执行以下步骤:
- 用黑色填充屏幕,清空屏幕。
- 处理 Pygame 事件,如退出游戏或处理移动和旋转俄罗斯方块棋子的键盘输入。
- 更新游戏状态,包括将当前棋子向下移动一行,并在棋子到达底部时将其锁定。
- 在屏幕上绘制当前分数、游戏网格和当前棋子。
- 如果游戏结束,显示 "游戏结束 "信息。
- 更新 Pygame 显示屏,并使用时钟对象控制帧频。
最后的话
就是这样!俄罗斯方块游戏的简单实现现在应该可以运行了。您可以运行脚本来玩游戏,并对游戏玩法、图形或控件进行任何所需的定制。