python实现相空间重构_Python 写个小游戏(1)

外星人入侵保卫家园游戏计划:

屏幕底部会出现飞船,玩家通过左右箭头控制飞船,空格键发射子弹。

屏幕上方不断有敌人入侵,玩家需要歼灭入侵者保护家园。

如果敌人到达了地面,则游戏失败退出。

开始配置

编码之前,首先要安装 Pygame

python3 -m pip install --user pygame

Collecting pygame
  Downloading https://files.pythonhosted.org/packages/32/37/453bbb62f90feff2a2b75fc739b674319f5f6a8789d5d21c6d2d7d42face/pygame-1.9.6-cp37-cp37m-macosx_10_11_intel.whl (4.9MB)
     |████████████████████████████████| 4.9MB 6.4MB/s
Installing collected packages: pygame
Successfully installed pygame-1.9.6

安装成功后便可以开始写代码,首先写一个空的窗口来展示这个游戏,新建文件 alien_invasion.py

import sys
import pygame

class AlienInvasion:
    def __init__(self):
        pygame.init()

        self.screen = pygame.display.set_mode((1200, 800))
        pygame.display.set_caption("Alien Invasion")

    def run_game(self):
        while True:
            for event in pygame.event.get()
                if event.type == pygame.QUIT:
                    sys.exit()

            pygame.display.flip()

if __name__ == '__main__':
    ai = AlienInvasion()
    ai.run_game()

总共16行代码:

前两行,我们引入 sys 和 pygame 模块,pygame 模块里包含了我们创造游戏要用到的方法,sys 模块帮助我们退出游戏。

第3行 我们定义一个类 AlienInvasion

第4-7行到在类的初始化方法__init__里,设置游戏的背景和标题,随后在第6行设置的背景上我们会添加其他的游戏元素。参数 (1200, 800) 是一个tuple类型,表示长1200pixel 宽800pixel

新建设置类

设置类是管理所有设置参数的地方,统一放在一个文件方便日后的修改维护。新建一个 settings.py 文件

class Settings:

    def __init__(self):
        self.screen_width = 1200
        self.screen_height = 800
        self.bg_color = (230, 230, 230)

创建完后在 alien_invasion.py 中引入该设置类,同时替换之前定义的值。

import sys
import pygame

from settings import Settings #新增

class AlienInvasion:
    def __init__(self):
        pygame.init()
        self.settings = Settings() #新增

        self.screen=pygame.display.set_mode(
            (self.settings.screen_width, self.settings.screen_height)) #新增
        pygame.display.set_caption("Alien Invasion")

    def run_game(self):
        while True:
            for event in pygame.event.get():
                if event.type==pygame.QUIT:
                    sys.exit()
                self.screen.fill(self.settings.bg_color) #新增
                pygame.display.flip()

if __name__ == '__main__':
    ai = AlienInvasion()
    ai.run_game()

添加飞船图片

新建images文件夹,添加图片 ship.bmp (图片的格式可以自由选择,但是Pygame 默认加载bmp格式,所以使用bmp是最佳实践)

7e7b0b5ba3861c73b63c6885bcfa5d32.bmp

新建一个 ship.py文件

import pygame

class Ship: 
    def __init__(self, ai_game):
        self.screen = ai_game.screen
        self.screen_rect = ai_game.screen.get_rect()

        self.image = pygame.image.load('images/ship.bmp')
        self.rect = self.image.get_rect()

        self.rect.midbottom = self.screen_rect.midbottom

    def blitme(self):
        self.screen.blit(self.image, self.rect)

将飞船放到游戏画板上

……
from ship import Ship

……
self.ship = Ship(self)

……
self.ship.blitme()

具体改动源码可参考 https://github.com/zhu-ting/python/tree/develop/pygame

飞船已经画上了

python实现相空间重构_Python 写个小游戏(1)_第1张图片

此时 develop 上的分支的代码 就是基本的初始结构,随后会通过不同分支来实现各部分的功能。

重构 run_game 方法

尤其在大项目里,重构是十分有必要的。重构是为了让代码结构更具有可读性,可维护性。重构不会改变原来的行为。

    def run_game(self):
        while True:
            self._check_events()
            self._update_screen()

    def _check_events(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
                
    def _update_screen(self):
        self.screen.fill(self.settings.bg_color)
        self.ship.blitme()

        pygame.display.flip()  

详细的更改可参考 https://github.com/zhu-ting/python/pull/1/files

开始开飞船(实现左右箭头控制移动)

现在要飞船能够响应键盘控制

    def _check_events(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            elif event.type == pygame.KEYDOWN:
                if event.type == pygame.K_RIGHT:
                    self.ship.rect.x += 1

在 _check_events 方法里面检测键盘是否为右箭头➡️ 是的话就把飞船的横坐标轴加一

现在运行程序可以看到每一次点击右箭头,飞船都会向右移动一下

但是我们希望按着右箭头可以连续移动,所以可以添加借助 pygame.KEYUP

向右移动具体代码 https://github.com/zhu-ting/python/pull/2/files

同理,向左移动 https://github.com/zhu-ting/python/pull/3/files

调整移动速度,任何大于 1 的值都会加快移动速度,可以自行设置节奏 https://github.com/zhu-ting/python/pull/4/files

但是此时一直向右移动,飞船会从屏幕上消失,所以需要设置边界,控制飞船的移动范围 https://github.com/zhu-ting/python/pull/5/files

最后再重构 _check_events 方法 https://github.com/zhu-ting/python/pull/6/files

  • 把键盘事件分割,拆分成 _check_keydown_events 和 _check_keyup_events
  • 添加退出,点击Q退出游戏

PS 设置为全屏模式 在macOS系统上可能体验会更好

发射子弹

可以移动飞船之后,要实现发射子弹的功能。首先在设置文件添加

        # Bullet settings
        self.bullet_speed = 1
        self.bullet_width = 3
        self.bullet_height = 15
        self.bullet_color = (60, 60, 60)

然后新建子弹类

import pygame
from pygame.sprite import Sprite

class Bullet(Sprite):
    def __init__(self, ai_game):
        super().__init__()
        self.screen = ai_game.screen
        self.settings = ai_game.settings
        self.color = self.settings.bullet_color

        self.rect = pygame.Rect(0, 0, self.settings.bullet_width,
            self.settings.bullet.height)
        self.rect.midtop = ai_game.ship.rect.midtop

        self.y = float(self.rect.y)

子弹Bullet类继承自Sprite类,该Sprite类是从pygame.sprite模块引入。

使用Sprite类时,可以把相关元素放到一组,然后一次性对一组元素进行操作。

第9 行子弹是长方形所以可以基于 pygame.Rect() 构建

第11 行表示子弹的中间对齐飞船的中间,这样看起来就好像是子弹从飞船里发射出去。

第二部分画出子弹

    def update(self):
        self.y -= self.settings.bullet_speed
        self.rect.y = self.y

    def draw_bullet(self):
        pygame.draw.rect(self.screen, self.color)

更新 update 方法管理子弹的位置,draw_bullet 方法则会描画出子弹。

接下来要实现点击空格发射子弹的功能:在键盘按下事件里面调用发射子弹的方法,但是不需要在 _check_keyup_events 方法里面进行处理。

添加子弹,按空格键发射子弹完成 https://github.com/zhu-ting/python/pull/8/files

管理子弹(删除旧的子弹/限制子弹数量)

现在已经可以移动飞船,并且发射任意数量的子弹,子弹在到达屏幕上空的时候消失了,但它们其实并没有真正被销毁,只是离开了屏幕看不见了。没有删除的子弹会占用内存,导致游戏越来越慢,所以我们要检测出不再屏幕的子弹,手动删除。

删除不在屏幕上的子弹并且限制子弹的数量 https://github.com/zhu-ting/python/pull/9/files

总结:本篇主要介绍了如何计划一个游戏并且使用 Pygame 搭建基本骨架,学会了使用 settings 文件来统筹配置,学会了设置背景颜色等,知道了如何在屏幕上画飞船,如何控制飞船的移动,创建了子弹也学会了如何移除它们,同时练习了重构方法。

下一篇会继续介绍外星人敌人入侵。

参考

https://nostarch.com/pythoncrashcourse2e

https://www.pygame.org/docs/

https://www.pygame.org/news

你可能感兴趣的:(python实现相空间重构)