Pygame入门 2022 使用精灵类重构

Posted 一只大鸽子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Pygame入门 2022 使用精灵类重构相关的知识,希望对你有一定的参考价值。

前面3章已经完成了游戏,这章使用OOP风格重构游戏,然后给游戏添加一点音乐。

用精灵类重构

如果你完成了前3章的代码,应该会发现代码很乱。想更改某个代码?查找困难、修改起来更困难!不断添加的新功能让我们的代码越来越复杂,难以阅读。

考虑使用OOP重构代码,将游戏元素用类组织起来。
Pygame提供了Sprite,便于我们控制游戏中的元素:

Sprite(精灵)类,可以看成是surface+rectangle的组合,并且绘制和更新起来非常容易。

想要绘制一个精灵,只需要:
1.创建sprite;
2.将sprite放到Group或GroupSingle中;
3.通过group进行 draw/update
(和把大象放进冰箱里一样简单)

Group,是用于装精灵的容器。
GroupSingle,只能装一个精灵。

下面,我们用精灵重构玩家的属性和方法:
继承精灵类,并重写其方法。
__init__(self)
在初始化方法中,初始化素材,初始image和rect。注意,这里image和rect是固定写法,Sprite更新时会用到这两个名称。另外初始化方法中要调用父类的初始化方法。

update(self):
角色更新的逻辑。

class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        walk_1 = pygame.image.load(graphics/Player/player_walk_1.png).convert_alpha()
        walk_2 = pygame.image.load(graphics/Player/player_walk_2.png).convert_alpha()
        self.walk = [walk_1, walk_2]
        self.index = 0
        self.jump = pygame.image.load(graphics/Player/jump.png).convert_alpha()

        self.image = self.walk[self.index]
        self.rect = self.image.get_rect(midbottom=(80, 300))
        self.gravity = 0

        self.jump_sound = pygame.mixer.Sound(audio/jump.mp3)
        self.jump_sound.set_volume(0.5)

    def input(self):
        keys = pygame.key.get_pressed()
        if keys[pygame.K_SPACE] and self.rect.bottom >= 300:
            self.jump_sound.play()
            self.gravity -= 20

    def apply_gravity(self):
        self.gravity += 1
        self.rect.y += self.gravity
        if self.rect.bottom >= 300:
            self.rect.bottom = 300
            self.gravity = 0

    def animation_state(self):
        if self.rect.bottom < 300:
            self.image = self.jump
        else:
            self.index += 0.1
            if self.index >= len(self.walk):
                self.index = 0
            self.image = self.walk[int(self.index)]

    def update(self):
        self.input()
        self.apply_gravity()
        self.animation_state()

然后需要将其放到Group/GroupSingle中:

player = pygame.sprite.GroupSingle()
player.add(Player())

最后在游戏的主循环中,调用Group的draw和update方法。

player.draw(screen)
player.update()

类似地,可以创建蜗牛和苍蝇。它们都是一种障碍,可以创建一个类Obstacle来表示。
然后放到Group中:

obstacle_group = pygame.sprite.Group()
...
if event.type == obstacle_timer:
    obstacle_group.add(Obstacle(random.choice([fly, snail, snail, snail])))

在游戏的主循环中,调用Group的draw和update方法。

obstacle_group.draw(screen)
obstacle_group.update()

使用精灵后,检测它们之间的碰撞很简单,只需要使用pygame.sprite中的碰撞检测即可:

def collision_sprite():
    if pygame.sprite.spritecollide(player.sprite, obstacle_group, False):
        obstacle_group.empty()
        return False
    return True

添加音乐

播放音乐相当简单,加载、播放。
这里我们播放一个背景音乐。

bg_music = pygame.mixer.Sound(audio/张震岳 - 迷途羔羊.mp3)
bg_music.set_volume(0.5) # 设置音量50%
bg_music.play(loops=-1)  # loops为播放重复的次数,-1 表示永远循环播放。

可以在玩家跳跃时添加一个音效。

# in __init__()
self.jump_sound = pygame.mixer.Sound(audio/jump.mp3)
# in input()
if keys[pygame.K_SPACE] and self.rect.bottom >= 300:
    self.jump_sound.play()

完整代码:

import random
import pygame
from sys import exit

class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        walk_1 = pygame.image.load(graphics/Player/player_walk_1.png).convert_alpha()
        walk_2 = pygame.image.load(graphics/Player/player_walk_2.png).convert_alpha()
        self.walk = [walk_1, walk_2]
        self.index = 0
        self.jump = pygame.image.load(graphics/Player/jump.png).convert_alpha()

        self.image = self.walk[self.index]
        self.rect = self.image.get_rect(midbottom=(80, 300))
        self.gravity = 0

        self.jump_sound = pygame.mixer.Sound(audio/jump.mp3)
        self.jump_sound.set_volume(0.5)

    def input(self):
        keys = pygame.key.get_pressed()
        if keys[pygame.K_SPACE] and self.rect.bottom >= 300:
            self.jump_sound.play()
            self.gravity -= 20

    def apply_gravity(self):
        self.gravity += 1
        self.rect.y += self.gravity
        if self.rect.bottom >= 300:
            self.rect.bottom = 300
            self.gravity = 0

    def animation_state(self):
        if self.rect.bottom < 300:
            self.image = self.jump
        else:
            self.index += 0.1
            if self.index >= len(self.walk):
                self.index = 0
            self.image = self.walk[int(self.index)]

    def update(self):
        self.input()
        self.apply_gravity()
        self.animation_state()

class Obstacle(pygame.sprite.Sprite):
    def __init__(self, type):
        super().__init__()
        if type == fly:
            fly_frame_1 = pygame.image.load(graphics/Fly/Fly1.png).convert_alpha()
            fly_frame_2 = pygame.image.load(graphics/Fly/Fly2.png).convert_alpha()
            self.frames = [fly_frame_1, fly_frame_2]
            y_pos = 210
        elif type == snail:
            snail_frame_1 = pygame.image.load(graphics/snail/snail1.png).convert_alpha()
            snail_frame_2 = pygame.image.load(graphics/snail/snail2.png).convert_alpha()
            self.frames = [snail_frame_1, snail_frame_2]
            y_pos = 300
        self.index = 0
        self.image = self.frames[self.index]
        self.rect = self.image.get_rect(midbottom=(random.randint(900, 1100), y_pos))

    def animation_state(self):
        self.index += 0.1
        if self.index >= len(self.frames):
            self.index = 0
        self.image = self.frames[int(self.index)]

    def update(self):
        self.animation_state()
        self.rect.x -= 6
        self.destroy()

    def destroy(self):
        if self.rect.x <= -100:
            self.kill()

def display_score():
    # 显示分数
    current = pygame.time.get_ticks() // 1000 - start_time
    score_surf = test_font.render(f"Score:current", False, (64, 64, 64))
    screen.blit(score_surf, score_surf.get_rect(center=(400, 50)))
    return current

def collision_sprite():
    if pygame.sprite.spritecollide(player.sprite, obstacle_group, False):
        obstacle_group.empty()
        return False
    return True

# 初始化 引擎
pygame.init()
# 设置屏幕
screen = pygame.display.set_mode((800, 400))  # 宽度800,高度400
pygame.display.set_caption(Runner)  # 设置标题
# 时钟
clock = pygame.time.Clock()
# 字体
test_font = pygame.font.Font(font/Pixeltype.ttf, 50)
# 背景
sky_surf = pygame.image.load(graphics/Sky.png).convert()
ground_surf = pygame.image.load(graphics/ground.png).convert()
# 得分
score_surf = test_font.render("My game", False, Black)
score_rect = score_surf.get_rect(center=(400, 50))
# 游戏简介
game_name_surf = test_font.render("Runner", False, Black)
game_name_rect = game_name_surf.get_rect(center=(400, 80))
game_message_surf = test_font.render("Press space to run", False, (111, 196, 169))
game_message_rect = game_message_surf.get_rect(center=(400, 320))
# 人物画
player_stand_surf = pygame.image.load(graphics/Player/player_stand.png).convert_alpha()
player_stand_surf = pygame.transform.rotozoom(player_stand_surf, 0, 2)
player_stand_rect = player_stand_surf.get_rect(midbottom=(400, 300))

# Timer
obstacle_timer = pygame.USEREVENT + 1
pygame.time.set_timer(obstacle_timer, 800)

player = pygame.sprite.GroupSingle()
player.add(Player())
obstacle_group = pygame.sprite.Group()

bg_music = pygame.mixer.Sound(audio/张震岳 - 迷途羔羊.mp3)
bg_music.set_volume(0.5)
bg_music.play(loops=-1)

start_time = 0
score = 0
game_activate = False
while True:
    # 获取用户输入
    for event in pygame.event.get():
        # 用户点击退出,关闭游戏
        if event.type == pygame.QUIT:
            pygame.quit()
            exit()
        if game_activate:
            if event.type == obstacle_timer:
                obstacle_group.add(Obstacle(random.choice([fly, snail, snail, snail])))
        else:
            if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
                game_activate = True
                start_time = pygame.time.get_ticks() // 1000
    # 游戏运行:绘图,更新
    if game_activate:
        pygame.draw.rect(screen, #c0e8ec, score_rect)
        pygame.draw.rect(screen, #c0e8ec, score_rect, 10)
        screen.blit(sky_surf, (0, 0))  # 将test_surface放到screen上。(0,0):放置后test_surface的左上角位于screen的(0,0)处
        screen.blit(ground_surf, (0, 300))
        score = display_score()
        player.draw(screen)
        player.update()
        obstacle_group.draw(screen)
        obstacle_group.update()
        game_activate = collision_sprite()
    # 游戏结束:
    else:
        screen.fill((94, 129, 162))
        screen.blit(player_stand_surf, player_stand_rect)
        screen.blit(game_name_surf, game_name_rect)
        if score == 0:
            screen.blit(game_message_surf, game_message_rect)
        else:
            score_message = test_font.render(fYour score:score, False, (111, 196, 169))
            score_message_rect = score_message.get_rect(center=(400, 320))
            screen.blit(score_message, score_message_rect)

    pygame.display.update()
    clock.tick(60)  # 不超过60 fps

以上是关于Pygame入门 2022 使用精灵类重构的主要内容,如果未能解决你的问题,请参考以下文章

PyGame快速入门

pygame 快速入门

0x01 [游戏制作]精灵动画类的实现--基于python3.7和pygame

pygame 的精灵使用

改变精灵动画速度(pygame)

pygame精灵组和读取图片