12.1 规划项目
开发大型项目时,制定好计划后再动手编写代码很重要,本项目要实现的功能有:
- 玩家可以控制出现在屏幕底部的飞船,飞船可以左右移动和射击
- 一群外星人出现在屏幕上部,并向下方移动
- 玩家的任务是射击消灭外星人
- 一波外星人消灭后,下一波外星人向下移动的速度会更快
- 外星人撞到飞船或到达屏幕底部,玩家失败
- 玩家的飞船有三条命
12.2 安装 Pygame
使用 pycharm 开发游戏
-
创建一个 alien_invasion 的项目
-
安装 pygame ,文件 -> 设置 -> 项目:alein_invasion -> python 解释器 -> ‘+’
-
勾选对勾,点安装包
-
显示安装成功
-
使用代码验证安装成功(代码在运行图片后,直接复制即可)
import pygame, sys
from pygame.locals import *
white = 255,255,255
black = 0,0,0
pygame.init()
screen = pygame.display.set_mode((600, 500))
myfront = pygame.font.Font(None,60)
textImage = myfront.render("Hello Pygame", True, black)
while True:
for event in pygame.event.get():
if event.type in (QUIT, KEYDOWN):
sys.exit()
screen.fill(white)
screen.blit(textImage, (100, 100))
pygame.display.update()
12.3 开始游戏项目
首先创建一个游戏窗口,供之后来绘制游戏元素
12.3.1 创建 Pygame 窗口及响应用户输入
新建一个 alien_invasion.py 文件,录入如下代码
# alien_invasion.py
import sys
import pygame
class AlienInvasion:
'''管理游戏资源和行为的类'''
def __init__(self):
'''初始化游戏并创建游戏资源'''
pygame.init() # 初始化背景设置
self.screen = pygame.display.set_mode((1200, 800)) # 创建一个显示窗口,并赋值给 self.screen
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()
运行结果如下:(红色的1200和800是我写上去的,不是运行效果)
12.3.2 设置背景色
在 _ _ init _ _ (self) 方法末尾设置背景色
在pygame中,颜色是以RGB值指定的,三原色分别是红、绿、蓝,每个的取值是0-255
我们选取(230,230,230),并赋值给 bg_color
调用方法fill() 让背景色填充屏幕,它只有一个参数,即只接受一种颜色
# alien_invasion.py
import sys
import pygame
class AlienInvasion:
'''管理游戏资源和行为的类'''
def __init__(self):
'''初始化游戏并创建游戏资源'''
pygame.init() # 初始化背景设置
self.screen = pygame.display.set_mode((1200, 800)) # 创建一个显示窗口,并赋值给 self.screen
pygame.display.set_caption("Alien Invasion") #窗口显示标题
self.bg_color = (230, 230, 230) #设置背景颜色
def run_game(self):
'''开始游戏的主循环'''
while True:
# 监控键盘和鼠标事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
# 每次循环都重绘屏幕
self.screen.fill(self.bg_color)
# 显示最近绘制的屏幕
pygame.display.flip()
if __name__ == '__main__':
# 创建游戏实例,并运行游戏
ai = AlienInvasion()
ai.run_game()
运行结果如下,发现背景颜色变成灰色了
12.3.3 创建设置类
随着引入的功能越来越多,我们要设置的东西也越多,比如上文中的屏幕尺寸、背景色等,所以我们要单独编写一个 settings.py 的模块,用来存储游戏的所有设置,方便后续修改
创建一个settings.py的文件
# settings.py
class Settings:
'''存储游戏中所有设置的类'''
def __init__(self):
'''初始化游戏设置'''
#设置屏幕大小和背景色
self.screen_width = 1200
self.screen_height = 800
self.bg_color = (230, 230, 230)
修改 alien_invaison.py中的代码
- 增加 settings 模块中的 Settings 类
- 创建一个settings 实例
- 将屏幕宽、高移入设置中
- 将背景颜色移入设置中
# alien_invaison.py
import sys
import pygame
from settings import Settings
class AlienInvasion:
'''管理游戏资源和行为的类'''
def __init__(self):
'''初始化游戏并创建游戏资源'''
pygame.init() # 初始化背景设置
self.settings = Settings() # 创建一个settings 实例
self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height)) # 创建一个显示窗口,并赋值给 self.screen
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()
修改后,效果不变,但代码更有条理性,执行效果如下:
12.4 添加飞船图像
在项目文件夹(alien_invasion)中创建一个新文件,命名为 images ,并将下载的飞船图片放入其中,图片命名为:ship.bmp
注:使用 .png 图片可能会报错,建议使用 .bmp 图片
12.4.1 创建 Ship 类
有了飞船图片后,我们要将其显示到屏幕上
创建 ship.py 文件,在其中创建 Ship 类,负责管理飞船的大部分行为
ship.py文件
# 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(r"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)
12.4.2在屏幕上绘制飞船
调用ship.blitme()方法,将飞船显示到屏幕上
修改后的alien_invasion.py文件如下
# alien_invasion.py
import sys
import pygame
from settings import Settings
from ship import Ship
class AlienInvasion:
'''管理游戏资源和行为的类'''
def __init__(self):
'''初始化游戏并创建游戏资源'''
pygame.init() # 初始化背景设置
self.settings = Settings() # 创建一个settings 实例
self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height)) # 创建一个显示窗口,并赋值给 self.screen
pygame.display.set_caption("Alien Invasion") #窗口显示标题
self.ship = Ship(self)
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)
self.ship.blitme()
# 显示最近绘制的屏幕
pygame.display.flip()
if __name__ == '__main__':
# 创建游戏实例,并运行游戏
ai = AlienInvasion()
ai.run_game()
执行效果如下:
12.5 重构
在大型项目中,在添加新代码前要重构既有代码,旨在简化既有代码的结构,使其更容易扩展
run_game()的方法越来越长,可以拆分成两个辅助方法: _check_event() 和 _update_screen() ;辅助方法在类中执行任务,但并不是通过实例调用的,辅助方法的名称以单下划线开头
12.5.1 方法 _check_events()
将管理事件的代码移到 _check_events() 中
12.5.2方法 _update_screen()
将检查是否单击了关闭窗口按钮的代码移到 _update_screen() 中
12.5.1和12.5.2修改后的代码如下:
# alien_invasion.py
import sys
import pygame
from settings import Settings
from ship import Ship
class AlienInvasion:
'''管理游戏资源和行为的类'''
def __init__(self):
'''初始化游戏并创建游戏资源'''
pygame.init() # 初始化背景设置
self.settings = Settings() # 创建一个settings 实例
self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height)) # 创建一个显示窗口,并赋值给 self.screen
pygame.display.set_caption("Alien Invasion") #窗口显示标题
self.ship = Ship(self)
def run_game(self):
'''开始游戏的主循环'''
while True:
self._check_event() # 监控键盘和鼠标事件
self._update_screen() # 更新屏幕上的图像,并更新到新屏幕
# 显示最近绘制的屏幕
pygame.display.flip()
def _check_event(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()
if __name__ == '__main__':
# 创建游戏实例,并运行游戏
ai = AlienInvasion()
ai.run_game()
运行程序后,显示效果与12.4.2相同
12.6 驾驶飞船
玩家可以通过左右键移动飞船
12.6.1 响应按键
各种事件可以通过方法 pygame.event.get()获取,按键可以被注册为 KEYDOWN 事件
在 alien_invasion.py 文件中,增加“按右键,飞船向右移动功能”,移动速度为1个像素
在_check_event()中添加 elif 代码块
# alien_invasion.py
import sys
import pygame
from settings import Settings
from ship import Ship
class AlienInvasion:
'''管理游戏资源和行为的类'''
def __init__(self):
'''初始化游戏并创建游戏资源'''
pygame.init() # 初始化背景设置
self.settings = Settings() # 创建一个settings 实例
self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height)) # 创建一个显示窗口,并赋值给 self.screen
pygame.display.set_caption("Alien Invasion") #窗口显示标题
self.ship = Ship(self)
def run_game(self):
'''开始游戏的主循环'''
while True:
self._check_event() # 监控键盘和鼠标事件
self._update_screen() # 更新屏幕上的图像,并更新到新屏幕
# 显示最近绘制的屏幕
pygame.display.flip()
def _check_event(self):
# 监控键盘和鼠标事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
self.ship.rect.x += 1 #检测到按下右键,飞船右移一个像素
def _update_screen(self):
'''更新屏幕上的图像,并更新到新屏幕'''
self.screen.fill(self.settings.bg_color)
self.ship.blitme()
if __name__ == '__main__':
# 创建游戏实例,并运行游戏
ai = AlienInvasion()
ai.run_game()
按下一次右键,飞船移动一个像素,执行效果见下图
12.6.2 允许持续移动
我们希望按住右键不放时,飞船不断向右移动,直到松开右键为止
在飞船的属性中增加标志位 moving_right == False ,在飞创的方法中增加 update() ,用来检查moveing_right 的状态,如果为True,就调整飞船位置
对 ship.py 进行修改
# 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(r"images/ship.bmp")
self.rect = self.image.get_rect()
# 对于每艘新飞船,将其放在屏幕底部的中央
self.rect.midbottom = self.screen_rect.midbottom
# 增加移动标记
self.moving_right == False
def update(self):
'''根据移动标记状态调整调整飞船位置'''
if self.moving_right:
self.rect.x += 1
def blitme(self):
'''在指定位置绘制飞船'''
self.screen.blit(self.image, self.rect)
修改alien_invasion.py 文件中的 _check_events(),使玩家按下右键时 moving_right 为True,松开为False,最后在 run_game(self) 函数中调用 self.ship.update()函数
# alien_invasion.py
import sys
import pygame
from settings import Settings
from ship import Ship
class AlienInvasion:
'''管理游戏资源和行为的类'''
def __init__(self):
'''初始化游戏并创建游戏资源'''
pygame.init() # 初始化背景设置
self.settings = Settings() # 创建一个settings 实例
self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height)) # 创建一个显示窗口,并赋值给 self.screen
pygame.display.set_caption("Alien Invasion") #窗口显示标题
self.ship = Ship(self)
def run_game(self):
'''开始游戏的主循环'''
while True:
self._check_event() # 监控键盘和鼠标事件
self.ship.update() # 飞船的位置信息更新
self._update_screen() # 更新屏幕上的图像,并更新到新屏幕
# 显示最近绘制的屏幕
pygame.display.flip()
def _check_event(self):
# 监控键盘和鼠标事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
self.ship.moving_right = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
self.ship.moving_right = False
def _update_screen(self):
'''更新屏幕上的图像,并更新到新屏幕'''
self.screen.fill(self.settings.bg_color)
self.ship.blitme()
if __name__ == '__main__':
# 创建游戏实例,并运行游戏
ai = AlienInvasion()
ai.run_game()
按住右键飞船持续向右移动,松开停止移动,可以移除右侧界面,执行结果如下图:
12.6.3 左右移动
同样的方法,增加向左移动功能
对 ship.py 文件的 _ _ init_ _ () 和 update() 方法进行修改
# 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(r"images/ship.bmp")
self.rect = self.image.get_rect()
# 对于每艘新飞船,将其放在屏幕底部的中央
self.rect.midbottom = self.screen_rect.midbottom
# 增加移动标记
self.moving_right = False
self.moving_left = False
def update(self):
'''根据移动标记状态调整调整飞船位置'''
if self.moving_right:
self.rect.x += 1
if self.moving_left:
self.rect.x -= 1
def blitme(self):
'''在指定位置绘制飞船'''
self.screen.blit(self.image, self.rect)
对 文件中的 _chek_events(self)方法进行修改
# alien_invasion.py
import sys
import pygame
from settings import Settings
from ship import Ship
class AlienInvasion:
'''管理游戏资源和行为的类'''
def __init__(self):
'''初始化游戏并创建游戏资源'''
pygame.init() # 初始化背景设置
self.settings = Settings() # 创建一个settings 实例
self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height)) # 创建一个显示窗口,并赋值给 self.screen
pygame.display.set_caption("Alien Invasion") #窗口显示标题
self.ship = Ship(self)
def run_game(self):
'''开始游戏的主循环'''
while True:
self._check_event() # 监控键盘和鼠标事件
self.ship.update() # 飞船的位置信息更新
self._update_screen() # 更新屏幕上的图像,并更新到新屏幕
# 显示最近绘制的屏幕
pygame.display.flip()
def _check_event(self):
# 监控键盘和鼠标事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
self.ship.moving_right = True
elif event.key == pygame.K_LEFT:
self.ship.moving_left = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
self.ship.moving_right = False
elif event.key == pygame.K_LEFT:
self.ship.moving_left = False
def _update_screen(self):
'''更新屏幕上的图像,并更新到新屏幕'''
self.screen.fill(self.settings.bg_color)
self.ship.blitme()
if __name__ == '__main__':
# 创建游戏实例,并运行游戏
ai = AlienInvasion()
ai.run_game()
执行代码发现,飞船可以左右持续移动了,但会超出屏幕界面,执行图如下:
12.6.4 调整飞船的速度
在settings.py中增加新属性
# settings.py
class Settings:
'''存储游戏中所有设置的类'''
def __init__(self):
'''初始化游戏设置'''
#设置屏幕大小和背景色
self.screen_width = 1200
self.screen_height = 800
self.bg_color = (230, 230, 230)
# 飞船设置
self.ship_speed = 2
在 ship.py 中,给Ship类添加属性settings,增加self.x属性,更新update()方法
# 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.settings = ai_game.settings
# 加载飞船图像并获取其外接矩形
self.image = pygame.image.load(r"images/ship.bmp")
self.rect = self.image.get_rect()
# 对于每艘新飞船,将其放在屏幕底部的中央
self.rect.midbottom = self.screen_rect.midbottom
#飞船属性中存储x的值
self.x = float(self.rect.x)
# 增加移动标记
self.moving_right = False
self.moving_left = False
def update(self):
'''根据移动标记状态调整调整飞船位置'''
if self.moving_right:
self.rect.x += self.settings.ship_speed
if self.moving_left:
self.rect.x -= self.settings.ship_speed
def blitme(self):
'''在指定位置绘制飞船'''
self.screen.blit(self.image, self.rect)
执行后,按左右键时飞船的移动速度明显加快
12.6.5 限制飞船的活动范围
飞船会飞到屏幕之外,修改使飞船只能在屏幕内移动,修改 ship.py 中的update()方法即可
# 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.settings = ai_game.settings
# 加载飞船图像并获取其外接矩形
self.image = pygame.image.load(r"images/ship.bmp")
self.rect = self.image.get_rect()
# 对于每艘新飞船,将其放在屏幕底部的中央
self.rect.midbottom = self.screen_rect.midbottom
#飞船属性中存储x的值
self.x = float(self.rect.x)
# 增加移动标记
self.moving_right = False
self.moving_left = False
def update(self):
'''根据移动标记状态调整调整飞船位置'''
if self.moving_right and self.rect.right < self.screen_rect.right:
self.rect.x += self.settings.ship_speed
if self.moving_left and self.rect.left > 0:
self.rect.x -= self.settings.ship_speed
def blitme(self):
'''在指定位置绘制飞船'''
self.screen.blit(self.image, self.rect)
执行程序,发现飞船不会跳出屏幕外了,效果如下图:
12.6.6重构_check_events()
方法_check_events()的代码越来越长,将其分解成两个方法,一个处理KEYDOWN事件,另一个处理KEYUP事件
在alien_invasion.py 文件中创建两个新的辅助方法:_check_keydown_events() 和_check_keyup_events()
# alien_invasion.py
import sys
import pygame
from settings import Settings
from ship import Ship
class AlienInvasion:
'''管理游戏资源和行为的类'''
def __init__(self):
'''初始化游戏并创建游戏资源'''
pygame.init() # 初始化背景设置
self.settings = Settings() # 创建一个settings 实例
self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height)) # 创建一个显示窗口,并赋值给 self.screen
pygame.display.set_caption("Alien Invasion") #窗口显示标题
self.ship = Ship(self)
def run_game(self):
'''开始游戏的主循环'''
while True:
self._check_event() # 监控键盘和鼠标事件
self.ship.update() # 飞船的位置信息更新
self._update_screen() # 更新屏幕上的图像,并更新到新屏幕
# 显示最近绘制的屏幕
pygame.display.flip()
def _check_event(self):
# 监控键盘和鼠标事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
self._check_keydown_events(event)
elif event.type == pygame.KEYUP:
self._check_keyup_events(event)
# 响应按键
def _check_keydown_events(self, event):
if event.key == pygame.K_RIGHT:
self.ship.moving_right = True
elif event.key == pygame.K_LEFT:
self.ship.moving_left = True
# 响应松开按键
def _check_keyup_events(self, event):
if event.key == pygame.K_RIGHT:
self.ship.moving_right = False
elif event.key == pygame.K_LEFT:
self.ship.moving_left = False
def _update_screen(self):
'''更新屏幕上的图像,并更新到新屏幕'''
self.screen.fill(self.settings.bg_color)
self.ship.blitme()
if __name__ == '__main__':
# 创建游戏实例,并运行游戏
ai = AlienInvasion()
ai.run_game()
修改后,执行效果与12.6.5相同
12.6.7 按Q键退出
添加一个快速结束游戏的快捷键:Q
在 文件的方法 _check_keydown_events(self, event) 中修改,修改后,按 'q‘ 立即退出游戏
# alien_invasion.py
import sys
import pygame
from settings import Settings
from ship import Ship
class AlienInvasion:
'''管理游戏资源和行为的类'''
def __init__(self):
'''初始化游戏并创建游戏资源'''
pygame.init() # 初始化背景设置
self.settings = Settings() # 创建一个settings 实例
self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height)) # 创建一个显示窗口,并赋值给 self.screen
pygame.display.set_caption("Alien Invasion") #窗口显示标题
self.ship = Ship(self)
def run_game(self):
'''开始游戏的主循环'''
while True:
self._check_event() # 监控键盘和鼠标事件
self.ship.update() # 飞船的位置信息更新
self._update_screen() # 更新屏幕上的图像,并更新到新屏幕
# 显示最近绘制的屏幕
pygame.display.flip()
def _check_event(self):
# 监控键盘和鼠标事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
self._check_keydown_events(event)
elif event.type == pygame.KEYUP:
self._check_keyup_events(event)
# 响应按键
def _check_keydown_events(self, event):
if event.key == pygame.K_RIGHT:
self.ship.moving_right = True
elif event.key == pygame.K_LEFT:
self.ship.moving_left = True
elif event.key == pygame.K_q:
sys.exit()
# 响应松开按键
def _check_keyup_events(self, event):
if event.key == pygame.K_RIGHT:
self.ship.moving_right = False
elif event.key == pygame.K_LEFT:
self.ship.moving_left = False
def _update_screen(self):
'''更新屏幕上的图像,并更新到新屏幕'''
self.screen.fill(self.settings.bg_color)
self.ship.blitme()
if __name__ == '__main__':
# 创建游戏实例,并运行游戏
ai = AlienInvasion()
ai.run_game()
12.6.8 在全屏模式下运行游戏
要实现全屏模式,可在 alien_invasion.py 文件的 _ _ init _ _ ()中修改
全屏显示,按 ”q“ 退出
# alien_invasion.py
import sys
import pygame
from settings import Settings
from ship import Ship
class AlienInvasion:
'''管理游戏资源和行为的类'''
def __init__(self):
'''初始化游戏并创建游戏资源'''
pygame.init() # 初始化背景设置
self.settings = Settings() # 创建一个settings 实例
# 独立窗口模式
#self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height)) # 创建一个显示窗口,并赋值给 self.screen
# 全屏模式
self.screen = pygame.display.set_mode((0,0), pygame.FULLSCREEN)
self.settings.screen_width = self.screen.get_rect().width
self.settings.screen_height = self.screen.get_rect().height
pygame.display.set_caption("Alien Invasion") #窗口显示标题
self.ship = Ship(self)
def run_game(self):
'''开始游戏的主循环'''
while True:
self._check_event() # 监控键盘和鼠标事件
self.ship.update() # 飞船的位置信息更新
self._update_screen() # 更新屏幕上的图像,并更新到新屏幕
# 显示最近绘制的屏幕
pygame.display.flip()
def _check_event(self):
# 监控键盘和鼠标事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
self._check_keydown_events(event)
elif event.type == pygame.KEYUP:
self._check_keyup_events(event)
# 响应按键
def _check_keydown_events(self, event):
if event.key == pygame.K_RIGHT:
self.ship.moving_right = True
elif event.key == pygame.K_LEFT:
self.ship.moving_left = True
elif event.key == pygame.K_q:
sys.exit()
# 响应松开按键
def _check_keyup_events(self, event):
if event.key == pygame.K_RIGHT:
self.ship.moving_right = False
elif event.key == pygame.K_LEFT:
self.ship.moving_left = False
def _update_screen(self):
'''更新屏幕上的图像,并更新到新屏幕'''
self.screen.fill(self.settings.bg_color)
self.ship.blitme()
if __name__ == '__main__':
# 创建游戏实例,并运行游戏
ai = AlienInvasion()
ai.run_game()
12.7 简单回顾
12.7.1 alien_invasion.py
主程序
12.7.2 settings.py
各种设置
12.7.3 ship.py
管理飞船的属性和方法
12.8 射击
恢复独立窗口(不全屏)显示,添加射击功能,子弹在屏幕中向上飞行,达到屏幕上边缘后消失
12.8.1 添加子弹设置
在 settings.py 的 _ _ init _ _()方法中增加子弹设置
# settings.py
class Settings:
'''存储游戏中所有设置的类'''
def __init__(self):
'''初始化游戏设置'''
#设置屏幕大小和背景色
self.screen_width = 1200
self.screen_height = 800
self.bg_color = (230, 230, 230)
# 飞船设置
self.ship_speed = 2
# 子弹设置
self.bullet_speed = 1.5
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color = (60, 60, 60)
12.8.2 创建Bullet类
新创建一个bullet.py文件
# bullet.py
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
# 在(0,0)创建一个子弹矩形,再设置到正确位置
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)
def update(self):
'''向上移动子弹'''
self.y -= self.settings.bullet_speed
self.rect.y = self.y # 更新子弹rect的位置
def draw_bullet(self):
'''在屏幕上绘制子弹'''
pygame.draw.rect(self.screen, self.color, self.rect)
12.8.3将子弹存储在编组中
在 文件的_ _ init _ _()创建一个实例,在 run_game() 中更新子弹的位置
# alien_invasion.py
import sys
import pygame
from settings import Settings
from ship import Ship
class AlienInvasion:
'''管理游戏资源和行为的类'''
def __init__(self):
'''初始化游戏并创建游戏资源'''
pygame.init() # 初始化背景设置
self.settings = Settings() # 创建一个settings 实例
# 独立窗口模式
self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height)) # 创建一个显示窗口,并赋值给 self.screen
# 全屏模式
'''
self.screen = pygame.display.set_mode((0,0), pygame.FULLSCREEN)
self.settings.screen_width = self.screen.get_rect().width
self.settings.screen_height = self.screen.get_rect().height'''
pygame.display.set_caption("Alien Invasion") #窗口显示标题
self.ship = Ship(self)
self.bullets = pygame.sprite.Group()
def run_game(self):
'''开始游戏的主循环'''
while True:
self._check_event() # 监控键盘和鼠标事件
self.ship.update() # 飞船的位置信息更新
self.bullets.update() #子弹的位置更新
self._update_screen() # 更新屏幕上的图像,并更新到新屏幕
# 显示最近绘制的屏幕
pygame.display.flip()
def _check_event(self):
# 监控键盘和鼠标事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
self._check_keydown_events(event)
elif event.type == pygame.KEYUP:
self._check_keyup_events(event)
# 响应按键
def _check_keydown_events(self, event):
if event.key == pygame.K_RIGHT:
self.ship.moving_right = True
elif event.key == pygame.K_LEFT:
self.ship.moving_left = True
elif event.key == pygame.K_q:
sys.exit()
# 响应松开按键
def _check_keyup_events(self, event):
if event.key == pygame.K_RIGHT:
self.ship.moving_right = False
elif event.key == pygame.K_LEFT:
self.ship.moving_left = False
def _update_screen(self):
'''更新屏幕上的图像,并更新到新屏幕'''
self.screen.fill(self.settings.bg_color)
self.ship.blitme()
if __name__ == '__main__':
# 创建游戏实例,并运行游戏
ai = AlienInvasion()
ai.run_game()
12.8.4 开火
为发射子弹,需要在 alien_invasion.py 中导入子弹类,并编写一个新方法_fire_bullet()
因为按下空格发射子弹,需要修改 _check_keydown_events()
确保子弹及时显示在屏幕上,需要修改_update_screen()
# alien_invasion.py
import sys
import pygame
from bullet import Bullet
from settings import Settings
from ship import Ship
class AlienInvasion:
'''管理游戏资源和行为的类'''
def __init__(self):
'''初始化游戏并创建游戏资源'''
pygame.init() # 初始化背景设置
self.settings = Settings() # 创建一个settings 实例
# 独立窗口模式
self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height)) # 创建一个显示窗口,并赋值给 self.screen
# 全屏模式
'''
self.screen = pygame.display.set_mode((0,0), pygame.FULLSCREEN)
self.settings.screen_width = self.screen.get_rect().width
self.settings.screen_height = self.screen.get_rect().height'''
pygame.display.set_caption("Alien Invasion") #窗口显示标题
self.ship = Ship(self)
self.bullets = pygame.sprite.Group()
def run_game(self):
'''开始游戏的主循环'''
while True:
self._check_event() # 监控键盘和鼠标事件
self.ship.update() # 飞船的位置信息更新
self.bullets.update() #子弹的位置更新
self._update_screen() # 更新屏幕上的图像,并更新到新屏幕
# 显示最近绘制的屏幕
pygame.display.flip()
def _check_event(self):
# 监控键盘和鼠标事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
self._check_keydown_events(event)
elif event.type == pygame.KEYUP:
self._check_keyup_events(event)
# 响应按键
def _check_keydown_events(self, event):
if event.key == pygame.K_RIGHT:
self.ship.moving_right = True
elif event.key == pygame.K_LEFT:
self.ship.moving_left = True
elif event.key == pygame.K_q:
sys.exit()
elif event.key == pygame.K_SPACE:
self._fire_bullet()
# 响应松开按键
def _check_keyup_events(self, event):
if event.key == pygame.K_RIGHT:
self.ship.moving_right = False
elif event.key == pygame.K_LEFT:
self.ship.moving_left = False
def _fire_bullet(self):
'''创建一颗子弹,并加入编组bullets中'''
new_bullet = Bullet(self)
self.bullets.add(new_bullet)
def _update_screen(self):
'''更新屏幕上的图像,并更新到新屏幕'''
self.screen.fill(self.settings.bg_color)
self.ship.blitme()
for bullet in self.bullets.sprites():
bullet.draw_bullet()
if __name__ == '__main__':
# 创建游戏实例,并运行游戏
ai = AlienInvasion()
ai.run_game()
执行结果见下图
12.8.5 删除消失的子弹
我们需要删除消失的子弹(子弹移动到屏幕外,但依然存在),否则会消耗计算机内存
在 alien_invasion.py 文件的run_game()方法中,删除消失的子弹
注:for 循环在执行过程中,列表的长度不能变,所以需要创建一个副本
# alien_invasion.py
import sys
import pygame
from bullet import Bullet
from settings import Settings
from ship import Ship
class AlienInvasion:
'''管理游戏资源和行为的类'''
def __init__(self):
'''初始化游戏并创建游戏资源'''
pygame.init() # 初始化背景设置
self.settings = Settings() # 创建一个settings 实例
# 独立窗口模式
self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height)) # 创建一个显示窗口,并赋值给 self.screen
# 全屏模式
'''
self.screen = pygame.display.set_mode((0,0), pygame.FULLSCREEN)
self.settings.screen_width = self.screen.get_rect().width
self.settings.screen_height = self.screen.get_rect().height'''
pygame.display.set_caption("Alien Invasion") #窗口显示标题
self.ship = Ship(self)
self.bullets = pygame.sprite.Group()
def run_game(self):
'''开始游戏的主循环'''
while True:
self._check_event() # 监控键盘和鼠标事件
self.ship.update() # 飞船的位置信息更新
self.bullets.update() #子弹的位置更新
# 删除消失的子弹
for bullet in self.bullets.copy():
if bullet.rect.bottom <= 0:
self.bullets.remove(bullet)
# print(len(self.bullets)) 验证子弹消失代码,在终端窗口观察
self._update_screen() # 更新屏幕上的图像,并更新到新屏幕
# 显示最近绘制的屏幕
pygame.display.flip()
def _check_event(self):
# 监控键盘和鼠标事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
self._check_keydown_events(event)
elif event.type == pygame.KEYUP:
self._check_keyup_events(event)
# 响应按键
def _check_keydown_events(self, event):
if event.key == pygame.K_RIGHT:
self.ship.moving_right = True
elif event.key == pygame.K_LEFT:
self.ship.moving_left = True
elif event.key == pygame.K_q:
sys.exit()
elif event.key == pygame.K_SPACE:
self._fire_bullet()
# 响应松开按键
def _check_keyup_events(self, event):
if event.key == pygame.K_RIGHT:
self.ship.moving_right = False
elif event.key == pygame.K_LEFT:
self.ship.moving_left = False
def _fire_bullet(self):
'''创建一颗子弹,并加入编组bullets中'''
new_bullet = Bullet(self)
self.bullets.add(new_bullet)
def _update_screen(self):
'''更新屏幕上的图像,并更新到新屏幕'''
self.screen.fill(self.settings.bg_color)
self.ship.blitme()
for bullet in self.bullets.sprites():
bullet.draw_bullet()
if __name__ == '__main__':
# 创建游戏实例,并运行游戏
ai = AlienInvasion()
ai.run_game()
12.8.6 限制子弹数量
需要限制子弹的数量,鼓励玩家有效射击
在 settings.py 中,设置存储子弹最大存储量
# settings.py
class Settings:
'''存储游戏中所有设置的类'''
def __init__(self):
'''初始化游戏设置'''
#设置屏幕大小和背景色
self.screen_width = 1200
self.screen_height = 800
self.bg_color = (230, 230, 230)
# 飞船设置
self.ship_speed = 2
# 子弹设置
self.bullet_speed = 0.5
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color = (60, 60, 60)
self.bullet_allowed = 3
在 alien_invasion.py 中检查子弹是否小于限定值,小于则产生新子弹,在_fire_bullet中修改
# alien_invasion.py
import sys
import pygame
from bullet import Bullet
from settings import Settings
from ship import Ship
class AlienInvasion:
'''管理游戏资源和行为的类'''
def __init__(self):
'''初始化游戏并创建游戏资源'''
pygame.init() # 初始化背景设置
self.settings = Settings() # 创建一个settings 实例
# 独立窗口模式
self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height)) # 创建一个显示窗口,并赋值给 self.screen
# 全屏模式
'''
self.screen = pygame.display.set_mode((0,0), pygame.FULLSCREEN)
self.settings.screen_width = self.screen.get_rect().width
self.settings.screen_height = self.screen.get_rect().height'''
pygame.display.set_caption("Alien Invasion") #窗口显示标题
self.ship = Ship(self)
self.bullets = pygame.sprite.Group()
def run_game(self):
'''开始游戏的主循环'''
while True:
self._check_event() # 监控键盘和鼠标事件
self.ship.update() # 飞船的位置信息更新
self.bullets.update() #子弹的位置更新
# 删除消失的子弹
for bullet in self.bullets.copy():
if bullet.rect.bottom <= 0:
self.bullets.remove(bullet)
# print(len(self.bullets)) 验证子弹消失代码,在终端窗口观察
self._update_screen() # 更新屏幕上的图像,并更新到新屏幕
# 显示最近绘制的屏幕
pygame.display.flip()
def _check_event(self):
# 监控键盘和鼠标事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
self._check_keydown_events(event)
elif event.type == pygame.KEYUP:
self._check_keyup_events(event)
# 响应按键
def _check_keydown_events(self, event):
if event.key == pygame.K_RIGHT:
self.ship.moving_right = True
elif event.key == pygame.K_LEFT:
self.ship.moving_left = True
elif event.key == pygame.K_q:
sys.exit()
elif event.key == pygame.K_SPACE:
self._fire_bullet()
# 响应松开按键
def _check_keyup_events(self, event):
if event.key == pygame.K_RIGHT:
self.ship.moving_right = False
elif event.key == pygame.K_LEFT:
self.ship.moving_left = False
def _fire_bullet(self):
'''创建一颗子弹,并加入编组bullets中'''
if len(self.bullets) < self.settings.bullet_allowed:
new_bullet = Bullet(self)
self.bullets.add(new_bullet)
def _update_screen(self):
'''更新屏幕上的图像,并更新到新屏幕'''
self.screen.fill(self.settings.bg_color)
self.ship.blitme()
for bullet in self.bullets.sprites():
bullet.draw_bullet()
if __name__ == '__main__':
# 创建游戏实例,并运行游戏
ai = AlienInvasion()
ai.run_game()
执行程序,发现只能发射3枚子弹,效果见下图:
12.8.7 创建方法 _update_bullets()
代码可以正常执行后,进行重构,将run_game()方法中与子弹相关代码放在一个新方法_update_bullets()中
在 alien_invasion.py 中创建_update_bullets()方法,并修改
# alien_invasion.py
import sys
import pygame
from bullet import Bullet
from settings import Settings
from ship import Ship
class AlienInvasion:
'''管理游戏资源和行为的类'''
def __init__(self):
'''初始化游戏并创建游戏资源'''
pygame.init() # 初始化背景设置
self.settings = Settings() # 创建一个settings 实例
# 独立窗口模式
self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height)) # 创建一个显示窗口,并赋值给 self.screen
# 全屏模式
'''
self.screen = pygame.display.set_mode((0,0), pygame.FULLSCREEN)
self.settings.screen_width = self.screen.get_rect().width
self.settings.screen_height = self.screen.get_rect().height'''
pygame.display.set_caption("Alien Invasion") #窗口显示标题
self.ship = Ship(self)
self.bullets = pygame.sprite.Group()
def run_game(self):
'''开始游戏的主循环'''
while True:
self._check_event() # 监控键盘和鼠标事件
self.ship.update() # 飞船的位置信息更新
self._update_bullets() # 更新子弹信息并删除消失的子弹
self._update_screen() # 更新屏幕上的图像,并更新到新屏幕
# 显示最近绘制的屏幕
pygame.display.flip()
def _update_bullets(self):
'''更新子弹的位置并删除消失的子弹'''
self.bullets.update() # 子弹的位置更新
# 删除消失的子弹
for bullet in self.bullets.copy():
if bullet.rect.bottom <= 0:
self.bullets.remove(bullet)
# print(len(self.bullets)) 验证子弹消失代码,在终端窗口观察
def _check_event(self):
# 监控键盘和鼠标事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
self._check_keydown_events(event)
elif event.type == pygame.KEYUP:
self._check_keyup_events(event)
# 响应按键
def _check_keydown_events(self, event):
if event.key == pygame.K_RIGHT:
self.ship.moving_right = True
elif event.key == pygame.K_LEFT:
self.ship.moving_left = True
elif event.key == pygame.K_q:
sys.exit()
elif event.key == pygame.K_SPACE:
self._fire_bullet()
# 响应松开按键
def _check_keyup_events(self, event):
if event.key == pygame.K_RIGHT:
self.ship.moving_right = False
elif event.key == pygame.K_LEFT:
self.ship.moving_left = False
def _fire_bullet(self):
'''创建一颗子弹,并加入编组bullets中'''
if len(self.bullets) < self.settings.bullet_allowed:
new_bullet = Bullet(self)
self.bullets.add(new_bullet)
def _update_screen(self):
'''更新屏幕上的图像,并更新到新屏幕'''
self.screen.fill(self.settings.bg_color)
self.ship.blitme()
for bullet in self.bullets.sprites():
bullet.draw_bullet()
if __name__ == '__main__':
# 创建游戏实例,并运行游戏
ai = AlienInvasion()
ai.run_game()
运行程序,执行效果与12.8.6相同
12.9 小结
略