PHP前端开发

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

百变鹏仔 3小时前 #Python
文章标签 互动游戏

概述

这是有关使用 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 &gt;= obj.top:                return 'top'            if ball.centerx = obj.bottom:                return 'bottom'            if ball.centerx <h3>用球拍击球</h3><p></p><p>当球击中球拍时,它会弹开。如果它击中桨的顶部,它将弹回来,但保持相同的水平速度分量。 </p><p><span>立即学习</span>“<a href="https://pan.quark.cn/s/00968c3c2c15" style="text-decoration: underline !important; color: blue; font-weight: bolder;" rel="nofollow" target="_blank">Python免费学习笔记(深入)</a>”;</p><p>但是,如果它击中桨的一侧,它将弹到另一侧(左或右)并继续向下运动,直到击中地板。该代码使用了 intersect 函数()。</p><pre class="brush:python;toolbal:false;"># Hit paddles = self.ball.speededge = 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 -= 1elif self.paddle.moving_left:	speed_x += 1self.ball.speed = speed_x, speed_yelif edge in ('left', 'right'):self.ball.speed = (-s[0], s[1])

落地

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

# Hit floorif self.ball.top &gt; c.screen_height:    self.lives -= 1if self.lives == 0:	self.game_over = Trueelse:	self.create_ball()

撞击天花板和墙壁

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

# Hit ceilingif self.ball.top  c.screen_width:self.ball.speed = (-s[0], s[1])

打砖块

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

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

# Hit brickfor brick in self.bricks:    edge = intersect(brick, self.ball)if not edge:	continueself.bricks.remove(brick)self.objects.remove(brick)self.score += self.points_per_brickif 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 pygamefrom game_object import GameObjectfrom text_object import TextObjectimport config as cclass 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 属性始终返回与按钮当前状态匹配的颜色,因此玩家可以清楚地看到按钮处于活动状态:

@propertydef 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_running 和 start_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 = Trueself.start_level = Truedef on_quit(button):self.game_over = Trueself.is_game_running = False

显示和隐藏游戏菜单

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

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

结论

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

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

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