外星人入侵保卫家园游戏计划:
屏幕底部会出现飞船,玩家通过左右箭头控制飞船,空格键发射子弹。
屏幕上方不断有敌人入侵,玩家需要歼灭入侵者保护家园。
如果敌人到达了地面,则游戏失败退出。
编码之前,首先要安装 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是最佳实践)
新建一个 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
飞船已经画上了
此时 develop 上的分支的代码 就是基本的初始结构,随后会通过不同分支来实现各部分的功能。
尤其在大项目里,重构是十分有必要的。重构是为了让代码结构更具有可读性,可维护性。重构不会改变原来的行为。
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
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