俄罗斯方块(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 = x
self.y = y
self.shape = shape
self.color = random.choice(COLORS) # You can choose different colors for each shape
self.rotation = 0
创建 Tetris 类
俄罗斯方块类将处理主要的游戏逻辑。它将包含用于创建新棋子、检查走棋是否有效、清线、锁定棋子、更新游戏状态和绘制游戏的方法。在脚本中添加以下代码:
class Tetris:
def __init__(self, width, height):
self.width = width
self.height = height
self.grid = [[0 for _ in range(width)] for _ in range(height)]
self.current_piece = self.new_piece()
self.game_over = False
self.score = 0 # Add score attribute
让我们来制作一个新的 Tetromino 棋子:
def new_piece(self):
# Choose a random shape
shape = random.choice(SHAPES)
# Return a new Tetromino object
return 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 False
except IndexError:
return False
return True
clear_lines() 方法会清除已满的行,并返回已清除的行数,以便我们稍后计算分数:
def clear_lines(self):
"""Clear the lines that are full and return the number of cleared lines"""
lines_cleared = 0
for i, row in enumerate(self.grid[:-1]):
if all(cell != 0 for cell in row):
lines_cleared += 1
del 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 score
lines_cleared = self.clear_lines()
self.score += lines_cleared * 100 # Update the score based on the number of cleared lines
# Create a new piece
self.current_piece = self.new_piece()
# Check if the game is over
if not self.valid_move(self.current_piece, 0, 0, 0):
self.game_over = True
return 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 += 1
else:
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 pygame
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('Tetris')
# Create a clock object
clock = pygame.time.Clock()
# Create a Tetris object
game = Tetris(WIDTH // GRID_SIZE, HEIGHT // GRID_SIZE)
fall_time = 0
fall_speed = 50 # You can adjust this value to change the falling speed, it's in milliseconds
您可以调整 fall_speed 来降低它(使其更快),或增加它使其更慢。
下面是游戏循环:
while True:
# Fill the screen with black
screen.fill(BLACK)
for event in pygame.event.get():
# Check for the QUIT event
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
# Check for the KEYDOWN event
if 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 left
if 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 right
if event.key == pygame.K_DOWN:
if game.valid_move(game.current_piece, 0, 1, 0):
game.current_piece.y += 1 # Move the piece down
if event.key == pygame.K_UP:
if game.valid_move(game.current_piece, 0, 0, 1):
game.current_piece.rotation += 1 # Rotate the piece
if 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 bottom
game.lock_piece(game.current_piece) # Lock the piece in place
# Get the number of milliseconds since the last frame
delta_time = clock.get_rawtime()
# Add the delta time to the fall time
fall_time += delta_time
if fall_time >= fall_speed:
# Move the piece down
game.update()
# Reset the fall time
fall_time = 0
# Draw the score on the screen
draw_score(screen, game.score, 10, 10)
# Draw the grid and the current piece
game.draw(screen)
if game.game_over:
# Draw the "Game Over" message
draw_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 event
if event.type == pygame.KEYDOWN:
# Create a new Tetris object
game = Tetris(WIDTH // GRID_SIZE, HEIGHT // GRID_SIZE)
# Update the display
pygame.display.flip()
# Set the framerate
clock.tick(60)
主游戏循环是一个 while 循环,不断执行以下步骤:
- 用黑色填充屏幕,清空屏幕。
- 处理 Pygame 事件,如退出游戏或处理移动和旋转俄罗斯方块棋子的键盘输入。
- 更新游戏状态,包括将当前棋子向下移动一行,并在棋子到达底部时将其锁定。
- 在屏幕上绘制当前分数、游戏网格和当前棋子。
- 如果游戏结束,显示 "游戏结束 "信息。
- 更新 Pygame 显示屏,并使用时钟对象控制帧频。
最后的话
就是这样!俄罗斯方块游戏的简单实现现在应该可以运行了。您可以运行脚本来玩游戏,并对游戏玩法、图形或控件进行任何所需的定制。