使用我们的 Python 代码生成器,为您的编码项目开个好头。非常适合您需要快速解决方案的时候使用。别等了,今天就试试吧!
编程涉及您有效处理代码的能力。这种能力能让你开发出有价值、发人深省的应用程序。
构建游戏项目是学习代码操作的最佳途径之一。在 Python 中,用于构建交互式游戏的流行库是 Pygame。
在本教程中,您将学习如何用 Python 构建一个 Breakout 游戏。这是一个经典游戏,其特点是有一个可移动的球拍、一个弹跳球和多个高架砖块。
游戏的目的是使用球来摧毁所有砖块,同时使用球拍来控制球。
要求
要学习本教程,您需要具备以下条件:
-
Pygame:运行下面命令安装pygame
pip install pygame
-
Freesound 账户: 当游戏中发生某些事件时,我们需要添加声音。Freesound 有一个免费音轨库,您可以从中获取游戏音效。您需要创建一个账户才能下载声音。
配置游戏设置
第一步是在项目文件夹中创建 settings.py 文件。该文件将包含游戏的所有设置和变量。以下是 settings.py 文件的内容:
# settings.py
import pygame as pg
# Screen settings
WIDTH = 550
HEIGHT = 600
BG_COLOR = "purple"
# Text color
color = "white"
# Paddle settings
paddle_x = 250
paddle_y = 550
paddle_width = 100
paddle_height = 20
# Ball settings
ball_x = 250
ball_y = 540
ball_x_speed = 2
ball_y_speed = 2
ball_radius = 5
ball_color = "grey"
# Text settings
text_x = 300
# Bricks settings
brick_width = 40
brick_height = 20
如前所述,闯关游戏包含一个球拍、一个球、一组砖块和一个记分板。因此,上述设置将用于设置游戏中每个物体的大小、尺寸、位置(x、y)和其他特征。
创建游戏窗口
我们必须设置游戏屏幕,指定合适的尺寸、颜色等。新建一个名为 main.py 的文件。该文件将作为游戏的主脚本。在该文件中添加以下代码:
# main.py
import pygame as pg
from settings import *
# Initialize pygame
pg.init()
screen = pg.display.set_mode((WIDTH, HEIGHT))
pg.display.set_caption("Breakout Game")
clock = pg.time.Clock()
running = True
while running:
screen.fill(BG_COLOR)
# Check for quit game
for event in pg.event.get():
if event.type == pg.QUIT:
running = False
pg.display.flip()
# To adjust the game frame rate
clock.tick(60)
上面的代码就像是在 pygame 中设置游戏窗口的模板。我们首先初始化了 pygame,并使用 settings.py 文件中的变量设置了窗口尺寸。
Clock() 类用于管理每秒帧频。这可以确保游戏运行速度不会太快,并且在所有系统上都保持一致。为了保持窗口打开,我们需要创建一个 while 循环,将游戏中的每个事件都添加到该循环中。
最后,我们建立一个循环,当用户关闭游戏窗口时退出游戏。pg.display.flip()方法可确保定期更新游戏中的每个对象和事件。
创建球拍
屏幕目前是空白的,因此我们需要添加对象。我们要添加的第一个对象是球拍。要添加球拍,需要新建一个名为 paddle.py 的 python 文件。我们将在该文件中创建 Paddle 类。
由于我们的项目需要添加球拍、球、砖块等对象。建议我们将它们各自分成独立的类。
在 paddle.py 文件中添加以下代码:
# paddle.py
import pygame as pg
from settings import paddle_height, paddle_width
class Paddle:
def __init__(self, x, y):
self.x = x
self.y = y
self.width = paddle_width
self.height = paddle_height
self.rect = pg.Rect(self.x, self.y, self.width, self.height)
self.color = pg.Color("white")
def appear(self, screen):
pg.draw.rect(screen, self.color, self.rect)
def move_right(self):
if self.rect.x + self.width <= 550:
self.rect.x += 2
def move_left(self):
if self.rect.x >= 0:
self.rect.x -= 2
在上面的代码中,我们创建了一个 Paddle 类,并设置了它的属性,如屏幕上的 x 和 y 位置、高度、宽度和颜色。桨由 pygame 矩形对象创建,它由 self.rect 属性表示。
appear() 方法会将球拍显示在屏幕上。
另外两个方法–move_right() 和 move_left()–分别通过向右和向左移动两步来增加球拍的运动。这些方法还确保球拍只能在屏幕尺寸范围内移动。因此,如果您尝试将球拍移动到屏幕之外,它将不会移动。
我们需要在主脚本中添加桨叶对象。打开 main.py 文件并执行以下操作:
-
导入paddle类并创建一个paddle对象
from paddle import Paddle ... # OBJECTS pad = Paddle(paddle_x, paddle_y) ...
-
在 while 循环中添加 appear() 方法,如下所示:
while running: ... pad.appear(screen) ...
-
检查按键是否将球向右或向左移动:
while running: ... # Check for key presses keys = pg.key.get_pressed() if keys[pg.K_RIGHT]: pad.move_right() if keys[pg.K_LEFT]: pad.move_left() ...
现在,您的 main.py 文件应该如下所示:
# main.py
import pygame as pg
from paddle import Paddle
from settings import *
pg.init()
screen = pg.display.set_mode((WIDTH, HEIGHT))
pg.display.set_caption("Breakout Game")
clock = pg.time.Clock()
# OBJECTS
pad = Paddle(paddle_x, paddle_y)
running = True
while running:
screen.fill(BG_COLOR)
pad.appear(screen)
# Check for quit game
for event in pg.event.get():
if event.type == pg.QUIT:
running = False
# Check for key presses
keys = pg.key.get_pressed()
if keys[pg.K_RIGHT]:
pad.move_right()
if keys[pg.K_LEFT]:
pad.move_left()
pg.display.flip()
clock.tick(60)
运行程序。你应该会看到屏幕上出现一个类似下面这样的划面:
使用左右箭头键控制球拍。
创建弹跳球
像制作球拍一样,新建一个名为 ball.py 的文件,并创建一个球类。在该文件中添加以下代码:
# ball.py
import pygame as pg
from settings import ball_x_speed, ball_y_speed, ball_radius, ball_color
class Ball:
def __init__(self, x, y, screen):
self.x = x
self.y = y
self.screen = screen
self.radius = ball_radius
self.color = pg.Color(ball_color)
self.x_speed = ball_x_speed
self.y_speed = ball_y_speed
def move(self):
pg.draw.circle(self.screen, self.color, [self.x, self.y], self.radius)
self.y -= self.y_speed
self.x -= self.x_speed
在上面的代码中,我们为球创建了一个类,并设置了它的属性。move() 函数将球绘制在屏幕上指定的坐标处,并添加运动。它通过减小球的 x 和 y 值来实现这一目的。
请按照以下步骤将球添加到屏幕上:
- 打开 main.py 文件
- 导入 Ball 类并创建一个球对象:
...
from ball import Ball
...
# OBJECTS
ball = Ball(ball_x, ball_y, screen)
- 在 while 循环中添加 .move() 方法:
running = True
while running:
...
ball.move()
...
这将确保球的位置在屏幕上定期更新。
添加小球弹跳逻辑
小球会不断滚出屏幕。最初,它应该在碰到屏幕或球拍边缘(屏幕底部除外)时弹起。我们需要在 Ball() 类中添加新方法来实现这一点。下面是这些方法:
...
def bounce_x(self):
self.x_speed *= -1
def bounce_y(self):
self.y_speed *= -1
def check_for_contact_on_x(self):
if self.x - self.radius <= 0 or self.x + self.radius >= self.screen.get_width():
self.bounce_x()
def check_for_contact_on_y(self):
if self.y + self.radius <= 0:
self.bounce_y()
bounce_x() 和 bounce_y() 方法分别实现了 x 轴和 y 轴的小球弹跳逻辑。
check_for_contact_on_y()和 check_for_contact_on_x() 方法使用球的半径以及当前的 x 和 y 位置比较球与屏幕边缘的距离。
这些方法将球的当前位置乘以-1,从而创建一个反向位置。例如,如果小球正在向上移动,然后撞到了屏幕的右侧,我们需要通过减小小球的 x 和 y 值让它弹回左侧。唯一的办法就是将其乘以-1,这样它就会变成负值并开始向左移动。
同样,如果小球向上移动并撞击到屏幕的左边,我们需要通过增加 x 和 y 的值来使它弹向右边。
由于 x 值已为负数,要使其为正数的唯一方法就是乘以-1,因此小球开始向右移动。
让我们在 while 循环中反映这些变化,如下所示:
running = True
while running:
...
ball.move()
...
# Check if ball hits the x-axis above
ball.check_for_contact_on_x()
# Check if ball hits y-axis on the side
ball.check_for_contact_on_y()
# Check if ball hits paddle
if (pad.rect.y < ball.y + ball.radius < pad.rect.y + pad.height
and
pad.rect.x < ball.x + ball.radius < pad.rect.x + pad.width):
ball.bounce_y()
ball.y = pad.y - ball.radius
...
当球碰到屏幕边缘或球拍时会弹起。
为游戏添加砖块
闯关游戏中的砖块总是在球拍上方。它们通常以不同的颜色出现。我们需要进行一些数学计算才能正确添加砖块。别着急,这又不是什么火箭科学。我会告诉你如何操作。
新建一个名为 bricks.py 的 Python 文件,并添加一个名为 Bricks 的类:
# bricks.py
import random
import pygame as pg
class Bricks:
def __init__(self, screen, width, height):
self.screen = screen
self.width = width
self.height = height
self.random_colors = ['blue', 'yellow', 'red', 'green', 'orange']
self.bricks = []
self.brick_colors = []
self.set_values()
def set_values(self):
y_values = [int(y) for y in range(100, 200, 25)]
x_values = [int(x) for x in range(10, 550, 42)]
y_index = 0
self.loop(x_values, y_values, y_index)
def loop(self, x_values, y_values, y_index):
for n in x_values:
# Check if it is the last position in the x_values list.
if n == x_values[-1]:
# Check if all the positions in the y_values has been occupied
if y_index < len(y_values) - 1:
y_index += 1
# Run the method again if there are still vacant positions.
self.loop(x_values, y_values, y_index)
# Create new bricks
else:
x = n
y = y_values[y_index]
brick = pg.Rect(x, y, self.width, self.height)
self.bricks.append(brick)
self.brick_colors.append(random.choice(self.random_colors))
def show_bricks(self):
for loop in range(len(self.bricks)):
brick = self.bricks[loop]
color = self.brick_colors[loop]
pg.draw.rect(self.screen, color, brick)
在砖块类中,我们添加了砖块属性和其他属性,其中包括以下内容:
self.random_colors: 这是砖块可以选择的颜色列表
self.bricks: 这是一个包含为游戏生成的所有砖块的列表
self.brick_colors: 该列表将包含游戏中创建的每个砖块的已选颜色。
set_values() 方法将为 x 轴和 y 轴创建一个值列表。y_index 将用于访问列表中 Y 轴的每个值。但现在,它的值是 0。
loop() 方法需要三个位置参数。它们就是我们之前添加到 set_values() 方法中的变量。
现在,loop() 方法中的代码要做什么?
我们已经有了砖块的 x 和 y 位置列表。现在需要做的就是将它们放置在每个点上。一种方法是在 y 轴上选择一个位置,然后沿着这个位置排列砖块,每块砖块都有一个 x 值,该值来自 x 的生成位置列表。
当某个 y 轴被填满时,y_index 会增加 1。这样,它就会在下一个循环中移动到下一个 y 轴。如此循环下去,直到所有的 y 轴都被填满。然后,每块砖都会被添加到 self.bricks 列表中。
最后,show_bricks() 方法通过循环 self.bricks 列表在屏幕上显示砖块,并从 self.brick_colors 列表中为每个砖块赋予一种颜色。
现在,让我们把它添加到游戏循环中。请按照以下步骤操作:
- 导入 Bricks 类并创建一个砖块对象
...
from bricks import Bricks
...
# OBJECTS
bricks = Bricks(screen, brick_width, brick_height)
...
- 在while循环中添加show_bricks()方法:
running = True
while running:
...
bricks.show_bricks()
...
运行程序,你会看到五颜六色的砖块排列在屏幕上,如下图所示:
检查球何时击中砖块
根据游戏规则,一旦球击中砖块,砖块就会碎裂并消失。我们可以通过检查小球是否与砖块相撞来实现这一功能。在 while 循环中添加一个新的 if 代码块来实现这一功能:
...
running = True
while running:
...
# Check if ball hits brick
for brick in bricks.bricks:
if brick.collidepoint(ball.x, ball.y - ball.radius) or brick.collidepoint(ball.x, ball.y + ball.radius):
bricks.bricks.remove(brick)
ball.bounce_y()
...
在上面的代码中,砖块被击中后会从列表中移除,而球会反弹回来。
更新分数
下一步是记录每次成功击球的得分,然后在球拍未击中球时减少试验次数/生命值。
新建一个名为 scores.py 的文件。在该文件中,创建一个 Scoreboard 类来处理分数。下面是 ScoreBoard 类:
# scores.py
import pygame as pg
class ScoreBoard:
def __init__(self, x, color, screen):
self.screen = screen
self.color = color
self.x = x
self.score = 0
self.high_score = 0
self.trials = 2
self.font = pg.font.SysFont("calibri", 20)
def show_scores(self):
score_text = self.font.render(f"Score: {self.score}", True, self.color)
high_score_text = self.font.render(f"High Score: {self.high_score}", True, self.color)
trials_text = self.font.render(f"Trials: X{self.trials}", True, self.color)
score_text_rect = score_text.get_rect(topleft=(self.x, 10))
high_score_text_rect = high_score_text.get_rect(topleft=(self.x, 26))
trials_text_rect = trials_text.get_rect(topleft=(self.x, 42))
self.screen.blit(score_text, (self.x, 10))
self.screen.blit(high_score_text, (self.x, 26))
self.screen.blit(trials_text, (self.x, 42))
def is_game_over(self):
if self.trials == 0:
return True
return False
def game_over(self):
game_over_color = 'red'
game_over_font = pg.font.SysFont("calibri", 30)
game_over_text = game_over_font.render(f"Game Over! Click '0' to restart.", True, game_over_color)
game_over_rect = game_over_text.get_rect(topright=(50, 300))
self.screen.blit(game_over_text, (50, 300))
self.record_high_score()
def success(self):
game_success_color = 'green'
game_success_font = pg.font.SysFont("calibri", 30)
game_success_text = game_success_font.render(f"You won! Click '0' to restart.", True, game_success_color)
game_success_rect = game_success_text.get_rect(topleft=(50, 300))
self.screen.blit(game_success_text, (50, 300))
self.record_high_score()
def set_high_score(self):
try:
with open("records.txt", mode="r") as file:
lines = file.readlines()
except FileNotFoundError:
with open("records.txt", mode="w") as data:
data.write("0")
score = 0
else:
score = lines[0]
self.high_score = int(score)
def record_high_score(self):
if self.score > self.high_score:
with open("records.txt", mode="w") as file:
file.write(f"{self.score}")
在这段代码中,我们设置了记分板属性,如字体、高度等。show_scores() 方法会在屏幕上显示分数、高分和试验。
与之前的示例一样,导入 Scores 类并创建一个新的分数对象。在 while 循环中添加以下代码:
...
running = True
while running:
...
# Check if ball hits brick
for brick in bricks.bricks:
if brick.collidepoint(ball.x, ball.y - ball.radius) or brick.collidepoint(ball.x, ball.y + ball.radius):
bricks.bricks.remove(brick)
ball.bounce_y()
# Increase scores by 1
score.score += 1
# Check if ball falls off
if ball.y + ball.radius >= 580:
ball.y = pad.y - ball.radius
pg.time.delay(2000)
score.trials -= 1
ball.bounce_y()
...
这样,如果球击中砖块,您的得分就会增加 1 分。此外,如果球掉到球拍之外,您的得分也会减少。下图显示了记分板:
检查游戏结束条件
当没有试验次数或所有砖块都被摧毁时,游戏就结束了。我们必须创建一个条件,当试验次数或砖块数量等于 0 时结束游戏。
在 ScoreBoard 类中,有三个方法,即 game_over()、is_game_over() 和 success()。
is_game_over() 和 game_over() 方法会在用户没有试玩次数(即输了)时调用,而 success() 方法则会在用户打碎所有砖块时调用。每个方法都会显示描述每个事件的文本。
最后两个方法 set_high_score() 和 record_high_score() 增加了游戏的连续性。目前,您只能玩一会儿,您的分数不会被记录下来。这两个方法将在 .txt 文档中记录您的最高分,从而改变这一状况。这就是为什么要将它们添加到 success() 和 game_over() 方法中,以便在游戏结束时更新高分。
因此,当您重新开始游戏时,您的最高分就会出现在记分板上。让我们更新主脚本,以包含新的更新。将下面的代码添加到 main.py 文件中:
...
# OBJECTS
...
score = ScoreBoard(text_x, color, screen)
score.set_high_score
running = True
while running:
...
# Check if there are more trials
if score.is_game_over():
score.game_over()
# Check if all bricks are broken
elif len(bricks.bricks) == 0:
score.success()
else:
ball.move()
...
在上面的代码中,我们更新了 while 循环,这样如果剩余试验次数等于 0 或用户打破了所有砖块,小球就会停止移动。这就是 ball.move() 方法被移到 if 代码块中的原因。因此,当满足任何一个条件时,小球都会停止移动。
下图显示了用户输掉比赛时的情况:
下图显示了用户获胜时的图像:
为游戏添加声音
下一步是为游戏添加声音。我们将为游戏中的某些事件添加声音
正如本教程开头所述,Freesound.org 有一个庞大的背景音效库可供使用。我选择了本教程将使用的音频文件。不过,你也可以随意选择。下面列出了游戏中将使用的音频文件以及使用它们的位置:
最适合 pygame 的音频文件格式是 .ogg。它能将音频文件打包成较小的尺寸,从而确保传输速度。你可以使用免费的在线音频转换器将音频文件转换为 .ogg。
转换完这些文件后,就可以通过以下步骤将其用于游戏:
- 在项目中创建一个名为 audio 的单独文件夹。
- 将音频文件添加到该文件夹中。
- 打开 settings.py 文件并在 Pygame 中初始化声音
# settings.py
...
# Initialize Sound
pg.mixer.init()
# Audio files
pad_hit = pg.mixer.Sound('audio/pad_hit.ogg')
brick_breaking = pg.mixer.Sound("audio/brick_breaking.ogg")
game_end = pg.mixer.Sound("audio/game_end_.ogg")
dropping_ball = pg.mixer.Sound("audio/dropping_ball.ogg")
win_game = pg.mixer.Sound("audio/win_game.ogg")
...
上面的代码初始化了项目的音频。打开 main.py 文件,为特定事件添加声音,如下所示:
# main.py
...
sound_played = False
running = True
while running:
...
# Check if there are more trials
if score.is_game_over():
if not sound_played:
# Sound added
pg.mixer.Sound.play(game_end)
sound_played = True
score.game_over()
# Check if all bricks are broken
elif len(bricks.bricks) <= 0:
if not sound_played:
# Sound added
pg.mixer.Sound.play(win_game)
sound_played = True
score.success()
else:
ball.move()
...
# Check if ball falls off
if ball.y + ball.radius >= 580:
# Sound added
pg.mixer.Sound.play(dropping_ball)
ball.y = pad.y - ball.radius
pg.time.delay(2000)
score.trials -= 1
ball.bounce_y()
# Check if ball hits paddle
if (pad.rect.y < ball.y + ball.radius < pad.rect.y + pad.height
and
pad.rect.x < ball.x + ball.radius < pad.rect.x + pad.width):
# Sound added
pg.mixer.Sound.play(pad_hit)
ball.bounce_y()
ball.y = pad.y - ball.radius
# Check if ball hits brick
for brick in bricks.bricks:
if brick.collidepoint(ball.x, ball.y - ball.radius) or brick.collidepoint(ball.x, ball.y + ball.radius):
# Sound added
pg.mixer.Sound.play(brick_breaking)
bricks.bricks.remove(brick)
ball.bounce_y()
score.score += 1
...
添加重启键
这个游戏的最后一步是允许用户在输或赢时重启游戏。我们需要添加一个按键监听器事件,以便在用户点击 "0 "时重启游戏。下面是代码:
...
sound_played = False
running = True
while running:
...
# Check for key presses
...
# Restart game
if keys[pg.K_0]:
if score.is_game_over():
score.score = 0
score.trials = 5
score.sound_played = False
bricks.bricks.clear()
bricks.set_values()
...
这段代码会将所有游戏变量重置为默认设置。
结论
这个项目是开始用 Python 构建游戏的绝佳方式。你已经学会了如何使用 Pygame 库构建一个完整的桌面游戏。我们还添加了声音,使游戏更具娱乐性。
现在,你可以自信地自己制作类似的项目了。请在评论区告诉我你对这个游戏的看法,以及你可能有的其他问题。
感谢大家花时间阅读我的文章,你们的支持是我不断前进的动力。期望未来能为大家带来更多有价值的内容,请多多关注我的动态!