使用 Python 3 和 Pygame 创建互动游戏:第 4 部分

2023年 8月 29日 55.1k 0

使用 Python 3 和 Pygame 创建互动游戏:第 4 部分

概述

这是有关使用 Python 3 和 Pygame 制作游戏的五部分教程系列中的第四部分。在第三部分中,我们深入研究了 Breakout 的核心,学习了如何处理事件,了解了主要的 Breakout 类,并了解了如何移动不同的游戏对象。

在这一部分中,我们将了解如何检测碰撞以及当球撞击各种物体(如桨、砖块、墙壁、天花板和地板)时会发生什么。最后,我们将回顾游戏 UI 的重要主题,特别是如何使用我们自己的自定义按钮创建菜单。

碰撞检测

在游戏中,事物会相互碰撞。突破也不例外。大多数情况下是球撞到了东西。 handle_ball_collisions() 方法有一个嵌套函数,名为 intersect(),用于测试球是否击中物体以及击中物体的位置。如果球没有击中物体,则返回“左”、“右”、“上”、“下”或“无”。

def handle_ball_collisions(self):
def intersect(obj, ball):
edges = dict(
left=Rect(obj.left, obj.top, 1, obj.height),
right=Rect(obj.right, obj.top, 1, obj.height),
top=Rect(obj.left, obj.top, obj.width, 1),
bottom=Rect(obj.left, obj.bottom, obj.width, 1))
collisions = set(edge for edge, rect in edges.items() if
ball.bounds.colliderect(rect))
if not collisions:
return None

if len(collisions) == 1:
return list(collisions)[0]

if 'top' in collisions:
if ball.centery >= obj.top:
return 'top'
if ball.centerx = obj.bottom:
return 'bottom'
if ball.centerx < obj.left:
return 'left'
else:
return 'right'

登录后复制

用球拍击球

当球击中球拍时,它会弹开。如果它击中桨的顶部,它将弹回来,但保持相同的水平速度分量。

但是,如果它击中桨的一侧,它将弹到另一侧(左或右)并继续向下运动,直到击中地板。该代码使用了 intersect 函数()。

# Hit paddle
s = self.ball.speed
edge = intersect(self.paddle, self.ball)
if edge is not None:
self.sound_effects['paddle_hit'].play()
if edge == 'top':
speed_x = s[0]
speed_y = -s[1]
if self.paddle.moving_left:
speed_x -= 1
elif self.paddle.moving_left:
speed_x += 1
self.ball.speed = speed_x, speed_y
elif edge in ('left', 'right'):
self.ball.speed = (-s[0], s[1])

登录后复制

落地

当球拍在下降过程中错过球(或者球击中球拍的侧面)时,球将继续下落并最终击中地板。此时,玩家失去了生命,并且球被重新创建,因此游戏可以继续。当玩家生命耗尽时,游戏结束。

# Hit floor
if self.ball.top > c.screen_height:
self.lives -= 1
if self.lives == 0:
self.game_over = True
else:
self.create_ball()

登录后复制

撞击天花板和墙壁

当球撞到墙壁或天花板时,它只会弹回来。

# Hit ceiling
if self.ball.top < 0:
self.ball.speed = (s[0], -s[1])

# Hit wall
if self.ball.left c.screen_width:
self.ball.speed = (-s[0], s[1])

登录后复制

打砖块

当球击中砖块时,这是《Breakout》中的一个重大事件 - 砖块消失,玩家获得一分,球反弹回来,并且会发生一些其他事情(声音效果,可能还有特殊效果),我稍后再讨论。

为了确定砖块是否被击中,代码会检查是否有任何砖块与球相交:

# Hit brick
for brick in self.bricks:
edge = intersect(brick, self.ball)
if not edge:
continue

self.bricks.remove(brick)
self.objects.remove(brick)
self.score += self.points_per_brick

if edge in ('top', 'bottom'):
self.ball.speed = (s[0], -s[1])
else:
self.ball.speed = (-s[0], s[1])

登录后复制

对游戏菜单进行编程

大多数游戏都有一些用户界面。 Breakout 有一个简单的菜单,其中有两个按钮,分别是“PLAY”和“QUIT”。该菜单在游戏开始时出现,并在玩家单击“开始”时消失。让我们看看按钮和菜单是如何实现的以及它们如何与游戏集成。

制作按钮

Pygame 没有内置的 UI 库。有第三方扩展,但我决定为菜单构建自己的按钮。按钮是具有三种状态的游戏对象:正常、悬停和按下。正常状态是鼠标不在按钮上时,悬停状态是鼠标在按钮上但未按下鼠标左键时。按下状态是指鼠标位于按钮上方且玩家按下鼠标左键时。

该按钮被实现为一个矩形,其上显示有背景颜色和文本。该按钮还接收一个 on_click 函数(默认为 noop lambda 函数),该函数在单击按钮时被调用。

import pygame

from game_object import GameObject
from text_object import TextObject
import config as c

class Button(GameObject):
def __init__(self,
x,
y,
w,
h,
text,
on_click=lambda x: None,
padding=0):
super().__init__(x, y, w, h)
self.state = 'normal'
self.on_click = on_click

self.text = TextObject(x + padding,
y + padding, lambda: text,
c.button_text_color,
c.font_name,
c.font_size)

def draw(self, surface):
pygame.draw.rect(surface,
self.back_color,
self.bounds)
self.text.draw(surface)

登录后复制

按钮处理自己的鼠标事件并根据这些事件更改其内部状态。当按钮处于按下状态时,收到 MOUSEBUTTONUP 事件,表示玩家点击了按钮,并调用 on_click() 函数。

def handle_mouse_event(self, type, pos):
if type == pygame.MOUSEMOTION:
self.handle_mouse_move(pos)
elif type == pygame.MOUSEBUTTONDOWN:
self.handle_mouse_down(pos)
elif type == pygame.MOUSEBUTTONUP:
self.handle_mouse_up(pos)

def handle_mouse_move(self, pos):
if self.bounds.collidepoint(pos):
if self.state != 'pressed':
self.state = 'hover'
else:
self.state = 'normal'

def handle_mouse_down(self, pos):
if self.bounds.collidepoint(pos):
self.state = 'pressed'

def handle_mouse_up(self, pos):
if self.state == 'pressed':
self.on_click(self)
self.state = 'hover'

登录后复制

用于绘制背景矩形的 back_color 属性始终返回与按钮当前状态匹配的颜色,因此玩家可以清楚地看到按钮处于活动状态:

@property
def back_color(self):
return dict(normal=c.button_normal_back_color,
hover=c.button_hover_back_color,
pressed=c.button_pressed_back_color)[self.state]

登录后复制

创建菜单

create_menu() 函数创建一个带有两个按钮的菜单,其中包含文本“PLAY”和“QUIT”。它有两个嵌套函数,名为 on_play()on_quit() ,它提供给相应的按钮。每个按钮都添加到 objects 列表(待绘制)以及 menu_buttons 字段。

def create_menu(self):
for i, (text, handler) in enumerate((('PLAY', on_play),
('QUIT', on_quit))):
b = Button(c.menu_offset_x,
c.menu_offset_y + (c.menu_button_h + 5) * i,
c.menu_button_w,
c.menu_button_h,
text,
handler,
padding=5)
self.objects.append(b)
self.menu_buttons.append(b)
self.mouse_handlers.append(b.handle_mouse_event)

登录后复制

当点击 PLAY 按钮时,会调用 on_play() ,这会从 objects 列表中删除按钮,这样就不再绘制它们了。此外,触发游戏开始的布尔字段 is_game_runningstart_level 设置为 True。

当点击退出按钮时,is_game_running 设置为 False (有效暂停游戏)并且 game_over 设置为 True,触发最终游戏序列。

def on_play(button):
for b in self.menu_buttons:
self.objects.remove(b)

self.is_game_running = True
self.start_level = True

def on_quit(button):
self.game_over = True
self.is_game_running = False

登录后复制

显示和隐藏游戏菜单

显示和隐藏菜单是隐式的。当按钮位于 objects 列表中时,菜单可见;当它们被删除时,它就被隐藏了。就如此容易。

可以创建一个具有自己的表面的嵌套菜单,该表面呈现按钮等子组件,然后只需添加/删除该菜单组件,但这个简单的菜单不需要它。

结论

在这一部分中,我们介绍了碰撞检测以及当球撞击各种物体(例如桨、砖块、墙壁、天花板和地板)时会发生什么。此外,我们还创建了自己的菜单,其中包含根据命令隐藏和显示的自定义按钮。

在本系列的最后一部分中,我们将研究最终游戏,密切关注得分和生命、音效和音乐。

然后,我们将开发一套复杂的特效系统,为游戏增添趣味。最后,我们将讨论未来的方向和潜在的改进。

以上就是使用 Python 3 和 Pygame 创建互动游戏:第 4 部分的详细内容,更多请关注每日运维网(www.mryunwei.com)其它相关文章!

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论