使用Python 3和Pygame构建游戏:第4部分

总览

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

在这一部分中,我们将看到如何检测碰撞以及当球撞击桨,砖块,墙壁,天花板和地板等各种物体时发生的情况。 最后,我们将回顾游戏UI的重要主题,尤其是如何使用我们自己的自定义按钮创建菜单。

碰撞检测

在游戏中,事物相互碰撞。 突破没有什么不同。 通常是碰到东西的球。 handle_ball_collisions()方法具有一个称为intersect()的嵌套函数,该函数用于测试球是否击中了对象以及它在何处击中了对象。 如果球没有击中对象,则返回“ left”,“ right”,“ top”,“ bottom”或“ None”。

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.left:
                return 'left'
            else:
                return 'right'

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

用球拍击球

当球击中桨时,球反弹。 如果它碰到了桨的顶部,它将回弹,但保持相同的水平速度分量。

但是,如果它碰到了桨的侧面,它将弹向相反的一侧(向左或向右)并继续向下运动,直到碰到地板。 该代码使用相交的function()。

# 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 < 0 or self.ball.right > 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])

编程游戏菜单

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

制作按钮

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)

单击“播放”按钮时,将调用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列表中时,菜单可见。 当它们被删除时,它是隐藏的。 就如此容易。

可以使用其自己的表面创建一个嵌套菜单,以呈现诸如按钮等之类的子组件,然后只需添加/删除该菜单组件,但是此简单菜单不需要它。

结论

在这一部分中,我们介绍了碰撞检测以及当球撞击桨,砖块,墙壁,天花板和地板等各种物体时发生的情况。 此外,我们还创建了带有自定义按钮的菜单,这些按钮可在命令中隐藏和显示。

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

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

翻译自: https://code.tutsplus.com/tutorials/building-games-with-python-3-and-pygame-part-4--cms-30084

你可能感兴趣的:(游戏,python,游戏开发,pygame,css)