最新消息:欢迎光临 魔力 • Python!大家可以点开导航菜单中的【学习目录】,这个目录类似图书目录,更加方便学习!

练习项目21:使用python制作游戏(下)

Python教程 小楼一夜听春语 7172浏览 0评论

这一篇教程,我们来完成游戏的主程序模块。

最终编写完的游戏会有如下界面。

游戏界面汇总图:

接下来,根据上一篇教程的结构,我们编写每一个类的代码。

一、State类

从游戏界面汇总图中,我们能够看出,游戏中会有暂停状态(除关卡界面之外的界面)和运行状态(关卡界面)。

State类就是将这两个状态进行抽象,包括。

  • 无论那个状态都需要对退出游戏的指令进行处理;
  • 无论哪个状态都要显示界面内容。

对于退出游戏指令的处理,我们需要定义一个处理方法;

而显示内容我们需要定义两个方法,因为两个状态的内容显示要有不同的处理。

示例代码:

class State:
    """泛型游戏状态类,可以处理事件并在给定的表面上显示自身。"""

    def handle(self, event):
        """处理退出事件。"""
        if event.type == QUIT:
            sys.exit()
        if event.type == KEYDOWN and event.key == K_ESCAPE:
            sys.exit()

    def paused_display(self, screen):
        """暂停时显示。"""
        r = config.getint('Screen', 'red')
        b = config.getint('Screen', 'blue')
        g = config.getint('Screen', 'green')
        screen.fill((r, b, g))  # 屏幕填充颜色
        pygame.display.flip()  # 刷新显示屏幕

    def display(self, screen):  # 默认不作处理,由子类Level重写。
        """刷新显示时。"""
        pass

二、Level类

这个类继承State类,主要对关卡运行时进行相应的处理。

具体实现过程,大家可以通过注释理解。

示例代码:

class Level(State):
    """游戏等级,用于计算共有多少敌机落下,移动游戏对象以及其他与游戏相关的逻辑任务。"""

    def __init__(self, number=1):
        """初始化关卡的等级和游戏对象。"""
        self.number = number  # 初始化关卡级别
        self.remaining = config.getint('Level', 'pre_level')  # 初始化关卡敌机数量

        speed = config.getint('Speed', 'drop_speed')  # 获取设置文件中的敌机速度数值
        speed += (self.number - 1) * config.getint('Speed', 'speed_increase')  # 计算当前关卡敌机速度数值
        self.enemy = objects.Enemy(speed)  # 创建敌方飞机游戏对象
        self.plane = objects.Plane()  # 创建己方飞机对象
        self.sprites = pygame.sprite.RenderUpdates(self.enemy, self.plane)  # 创建游戏对象集添加游戏对象

    def update(self, game):
        """关卡运行,并进行游戏结束或通过关卡的处理。"""
        self.sprites.update()  # 刷新游戏对象
        if self.plane.touches(self.enemy):  # 如果碰撞到敌方飞机
            game.next_state = GameOver()  # 变更状态为游戏结束
        elif self.enemy.landed:  # 如果敌方飞机从屏幕底边移出
            self.enemy.reset()  # 重置敌方飞机位置
            self.remaining -= 1  # 敌机数量减少1架
            if self.remaining == 0:  # 如果敌机数量为0
                game.next_state = LevelCleared(self.number)  # 变更状态为清空关卡

    def display(self, screen):
        """关卡运行时的屏幕显示。"""
        r = config.getint('Screen', 'red')
        b = config.getint('Screen', 'blue')
        g = config.getint('Screen', 'green')
        screen.fill((r, b, g))  # 关卡运行时为屏幕填充颜色
        updates = self.sprites.draw(screen)  # 绘制屏幕外观
        pygame.display.update(updates)  # 刷新屏幕外观

三、Paused类

这个类继承自State类,是其它暂停状态类的超类。

暂停状态基本都是白色的屏幕和黑色的文字内容。

当然,也有例外。

欢迎界面做了两种不同的方案,方案1只有一张图片,方案2既有文字又有图片。

根据这些情况,我们分析一下如何处理。

  • 如果只有图片直接绘制图片;
  • 如果只有文字则在屏幕中心绘制文字;
  • 如果既有文字又有图片,将图片绘制在文字上方,间隔20像素。

具体实现过程,请大家参考代码中的注释理解。

示例代码:

class Paused(State):
    """暂停游戏的状态,按任意键或点击鼠标退出暂停状态。"""
    next_state = None  # 存储下一个游戏状态
    finish = False  # 记录是否完成按键或鼠标点击
    image = None  # 存储屏幕中显示的图片
    text = ''  # 存储屏幕中显示的文字

    def handle(self, event):
        """处理按任意键继续游戏。"""
        State.handle(self, event)
        if event.type in [MOUSEBUTTONDOWN, KEYDOWN]:  # 如果捕获鼠标点击和按键的事件
            self.finish = True  # 记录完成操作

    def update(self, game):
        """按任意键时进入下一个游戏状态。"""
        if self.finish:  # 如果按键或鼠标点击完成
            game.next_state = self.next_state()  # 将变量next_state中类的实例化,存入Game类进行处理。

    def paused_display(self, screen):
        """暂停时显示的处理。"""

        State.paused_display(self, screen)  # 重载超类中的方法
        font = pygame.font.SysFont('SimHei', config.getint('Screen', 'font_size'))  # 设置使用系统字体(用于支持中文)
        lines = self.text.strip().splitlines()  # 获取子类中设置的文字内容并分行
        height = len(lines) * font.get_linesize()  # 计算文字高度
        center, middle = screen.get_rect().center  # 获取屏幕中心坐标值
        top = middle - height // 2  # 计算显示内容中心点的y轴坐标
        if self.image:  # 如果有图片
            image = pygame.image.load(self.image).convert_alpha()  # 载入图片
            img_rect = image.get_rect()  # 创建矩形
            top += img_rect.height // 2  # 重新计算顶部y轴坐标
            if self.text:  # 如果有文字
                img_rect.midbottom = center, top - 20  # 设置图片位置为中心且底边与文字顶部间隔20像素。
            else:  # 否则
                img_rect.center = center, middle  # 设置图片中心点为屏幕中心点
            screen.blit(image, img_rect)  # 屏幕填充图片

        antialias = True  # 设置抗锯齿(平滑文字)
        black = 0, 0, 0  # 设置文字颜色
        for line in lines:
            text = font.render(line.strip(), antialias, black)  # 创建单行文字对象
            txt_rect = text.get_rect()  # 创建矩形
            txt_rect.midtop = center, top  # 设置文字显示位置
            screen.blit(text, txt_rect)  # 屏幕填充文字
            top += font.get_linesize()  # 逐行下移文字位置

        pygame.display.flip()  # 刷新屏幕显示

四、Paused类的子类(StartUp、Info、LevelCleared、GameOver)

这几个子类都比较简单,主要是定义暂停状态下屏幕中显示的内容。

其中,略有不同的是LevelCleared类,在这个类中要进行下一个关卡的实例化。

另外,为了能够方便更换欢迎界面的方案,在配置文件(config.ini)中我们新增一项配置内容。

示例代码:

[Welcome]
skin = 1

各个Paused类的子类实现,大家可以根据下方代码中的注释理解。

示例代码:

class Info(Paused):
    """游戏信息。"""
    next_state = Level  # 将类存入变量,以便在Paused类中实例化。
    text = '''控制移动你的飞机,
           不要被敌机撞到它。'''

class StartUp(Paused):
    """进入游戏。"""
    next_state = Info  # 将类存入变量,以便在Paused类中实例化。
    if config.getint('Welcome', 'skin'):  # 读取配置文件中欢迎界面的方案设置
        text = ''
        image = config.get('Image', 'welcome')
    else:
        text = '老司机开飞机'
        image = config.get('Image', 'plane')

class LevelCleared(Paused):
    """游戏过关。"""

    def __init__(self, number):
        """过关信息。"""
        self.number = number  # 获取当前关卡级别
        self.text = '''恭喜闯过第 %d 关,
        点击继续下一关。''' % self.number

    def next_state(self):
        """创建下一关卡。"""
        return Level(self.number + 1)  # 返回下移关卡的对象

class GameOver(Paused):
    """游戏结束。"""
    next_state = Level  # 将类存入变量,以便在Paused类中实例化。
    text = '游戏结束!'

五、Game类

这个类负责处理游戏的整个运行过程。

class Game:
    """负责主事件循环的游戏对象,完成在不同状态间切换的任务。"""

    def __init__(self):
        """初始化。"""
        self.state = None  # 初始化当前状态
        self.next_state = StartUp()  # 初始化下一个状态为开始游戏的状态

    def run(self):
        """运行游戏。"""
        pygame.init()  # 游戏初始化
        flag = 0  # 记录是否全屏的标志
        if config.getint('Screen', 'full_screen'):  # 如果配置文件中设置为全屏
            flag = FULLSCREEN  # 标志设置为全屏
        screen_size = (config.getint('Screen', 'width'), config.getint('Screen', 'height'))  # 读取配置文件中的屏幕尺寸设置
        screen = pygame.display.set_mode(screen_size, flag)  # 设置屏幕外观模式
        pygame.display.set_caption('老司机开飞机')  # 设置游戏窗口标题(非全屏时显示)
        pygame.mouse.set_visible(False)  # 隐藏鼠标指针显示
        while True:
            pygame.time.Clock().tick(200)  # 设置每秒帧数
            if self.state != self.next_state:  # 如果有新的游戏状态
                self.state = self.next_state  # 切换到新的状态
                self.state.paused_display(screen)  # 屏幕中显示暂停状态的内容(切换状态会进入暂停状态)
            for event in pygame.event.get():  # 获取事件
                self.state.handle(event)  # 处理事件
            self.state.update(self)  # 游戏对象的当前状态内容刷新
            self.state.display(screen)  # 屏幕中显示刷新后的状态内容

到这里,我们就完成了这个项目所有代码的编写。

大家可以运行程序,查看一下是否能够正确执行。

示例代码:

if __name__ == '__main__':
    game = Game()
    game.run()

本节练习源代码:【点此下载

转载请注明:魔力Python » 练习项目21:使用python制作游戏(下)

头像
发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网站 (可选)

网友最新评论 (4)

  1. 头像
    没有图片
    谢文东7年前 (2018-02-24)回复
    • 小楼一夜听春语
      在本节练习源代码的压缩包中
      小楼一夜听春语7年前 (2018-02-24)回复
  2. 头像
    大神这是啥情况呀,老报错,运行不了 enemy_image = pygame.image.load('enemy.png').convert_alpha() # 载入图片 pygame.error: Couldn't open enemy.png
    xtggbmdk5年前 (2020-01-07)回复
    • 小楼一夜听春语
      先换张图片试试看
      小楼一夜听春语5年前 (2020-01-17)回复