0 导入Pygame包
不知道为啥我的Pycharm导入包一直会出现问题,最后使用anaconda powershell prompt导入可以成功。
pip install pygame
1开始游戏项目
开发游戏《外星人入侵》手续创建一个空的Pygame窗口,供后面来绘制后续元素,如飞船和外星人。这里还将让这个游戏响应用户输入、设置背景色以及加载飞船图像。
1.1创建Pygame窗口以及响应用户输入
alien_invasion.py
:
import sys
import pygame
from settings import Settings
def run_game():
# 初始化游戏并创建一个屏幕对象
pygame.init() #初始化背景设置,让Pygame能正确工作
ai_setting = Settings()
screen = pygame.display.set_mode((ai_setting.screen_width, ai_setting.screen_height)) #创建一个名为scree,宽1200,高800像素的游戏窗口,display.set_mode返回surface表示整个游戏窗口
pygame.display.set_caption("Alien Invasion")
#开始游戏的主循环
while True:
#监视键盘和鼠标事件
for event in pygame.event.get(): #所有键盘和鼠标事件都将促使for循环运行
if event.type == pygame.QUIT:
sys.exit() #调用sys.exit()退出游戏
#每次循环都重绘屏幕
screen.fill(ai_setting.bg_color)
#让最近绘制的屏幕可见
pygame.display.flip()
run.game()
对象screen是一个surface。在pygame中,surface时屏幕的一部分,用于显示游戏元素。在这个游戏中,每个元素(如外星人或飞船)都是一个surface。
在Pygame库中,Surface 对象是一个非常核心的概念,它代表了一个二维的矩形画布,可以在上面绘制图像、图形、文本等内容。每个 Surface 都有自己的像素格式和尺寸,它们可以被用来表示游戏中的任何可见元素,比如背景、角色、物品等。
display.set_mode()
返回的suface表示整个窗口
1.2设置背景色
1.3创建设置类
settings.py
:
class Settings():
"""存储《外星人入侵》的所有设置的类"""
def __init__(self):
"""初始化游戏的设置"""
#屏幕设置
self.screen_width = 1200 #屏幕宽800
self.screen_height = 800 #屏幕高800
self.bg_color = (230,230,230) #浅灰色
2添加飞船图像
2.1创建Ship类
ship.py
:
import pygame
class Ship():
def __init__(self,screen):
"""初始化飞船并设置其初始位置"""
self.screen = screen
#加载飞船图像并获取其外接矩形
self.image = pygame.image.load('image/ship.bmp')
self.rect = self.image.get_rect() #2 使用get_rect()获取相应surface的属性rect
self.screen_rect = screen.get_rect() #3 将屏幕的矩形存储在self.screen_rect中
#将每艘新飞船放在屏幕底部中央
self.rect.centerx = self.screen_rect.centerx #4 将飞船中心的x坐标(self.rect.centerx)设置为表示屏幕的矩形的属性centerx
self.rect.bottom = self.screen_rect.bottom #将飞船下边缘的y坐标设置为表示屏幕的矩形的属性bottom
def blitme(self):
"""在指定位置绘制飞船"""
self.screen.blit(self.image, self.rect) #5 根据self.rect指定的位置将图像绘制到屏幕上
rect:矩形
get_rect()
可获取相应surface的属性rect。Pygame能够像处理矩形(rect对象)一样处理游戏元素。
处理rect对象时,可使用矩形四角和中心的x和y坐标。可通过设置这些值制定矩形的位置。
要将游戏元素居中,可设置相应rect对象的属性center、centerx、centery。
要让游戏元素与屏幕边缘对齐,可使用属性top、bottom、left或者right。
要调整游戏的水平或垂直位置,可使用属性x或y,它们分别是矩形左上角的x和y坐标。
在Pygame中,原点(0,0)位于屏幕左上角,向右下方移动时,坐标值将增大。在1200×800的屏幕上,原点位于左上角,而右下角的坐标为(1200,800)
2.2在屏幕上绘制飞船
更新alien_invasion.py
:,使其创建一艘飞船,并调用其方法blitme()
:
import sys
import pygame
from settings import Settings
from ship import Ship
def run_game():
# 初始化游戏并创建一个屏幕对象
pygame.init() #初始化背景设置,让Pygame能正确工作
ai_setting = Settings()
screen = pygame.display.set_mode((ai_setting.screen_width, ai_setting.screen_height)) #创建一个名为scree,宽1200,高800像素的游戏窗口,display.set_mode返回surface表示整个游戏窗口
pygame.display.set_caption("Alien Invasion")
# 更新的部分 创建一首飞船
ship = Ship(screen)
#开始游戏的主循环
while True:
#监视键盘和鼠标事件
for event in pygame.event.get(): #所有键盘和鼠标事件都将促使for循环运行
if event.type == pygame.QUIT:
sys.exit() #调用sys.exit()退出游戏
#每次循环都重绘屏幕
screen.fill(ai_setting.bg_color)
ship.blitme() #确保飞船绘制在屏幕上,并出现在背景前面
#让最近绘制的屏幕可见
pygame.display.flip()
run_game()
运行结果:
3重构:模块game_functions
大型项目中需要在添加新代码前重构既有代码。
重构旨在简化既有的代码,是指更容易扩展。
game_functions
模块存储运行的函数,通过创建game_functions
,可避免alien_invasion.py
过长。
3.1函数check_events()
将check_events()
放在一个名为game_functions
的模块中:
import sys
import pygame
def check_events():
"""响应按键和鼠标事件"""
for event in pygame.event.get(): # 所有键盘和鼠标事件都将促使for循环运行
if event.type == pygame.QUIT:
sys.exit() # 调用sys.exit()退出游戏
那么alien_invasion.py
也需要修改:
import sys
import pygame
from settings import Settings
from ship import Ship
import game_function as gf
def run_game():
# 初始化游戏并创建一个屏幕对象
pygame.init() #初始化背景设置,让Pygame能正确工作
ai_setting = Settings()
screen = pygame.display.set_mode((ai_setting.screen_width, ai_setting.screen_height)) #创建一个名为scree,宽1200,高800像素的游戏窗口,display.set_mode返回surface表示整个游戏窗口
pygame.display.set_caption("Alien Invasion")
# 更新的部分 创建一首飞船
ship = Ship(screen)
#开始游戏的主循环
while True:
#监视键盘和鼠标事件
gf.check_events()
#每次循环都重绘屏幕
screen.fill(ai_setting.bg_color)
ship.blitme() #确保飞船绘制在屏幕上,并出现在背景前面
#让最近绘制的屏幕可见
pygame.display.flip()
run_game()
3.2函数update_screen()
将更新屏幕的代码放在update_screen()
函数中。
import sys
import pygame
def check_events():
"""响应按键和鼠标事件"""
for event in pygame.event.get(): # 所有键盘和鼠标事件都将促使for循环运行
if event.type == pygame.QUIT:
sys.exit() # 调用sys.exit()退出游戏
def update_screen(ai_settings, screen, ship):
"""更新屏幕上的图像,并切换到新屏幕"""
# 每次循环都重绘屏幕
screen.fill(ai_settings.bg_color)
ship.blitme() # 确保飞船绘制在屏幕上,并出现在背景前面
# 让最近绘制的屏幕可见
pygame.display.flip()
那么alien_invasion.py
也需要修改:
import sys
import pygame
from settings import Settings
from ship import Ship
import game_function as gf
def run_game():
# 初始化游戏并创建一个屏幕对象
pygame.init() #初始化背景设置,让Pygame能正确工作
ai_setting = Settings()
screen = pygame.display.set_mode((ai_setting.screen_width, ai_setting.screen_height)) #创建一个名为scree,宽1200,高800像素的游戏窗口,display.set_mode返回surface表示整个游戏窗口
pygame.display.set_caption("Alien Invasion")
# 更新的部分 创建一首飞船
ship = Ship(screen)
#开始游戏的主循环
while True:
#监视键盘和鼠标事件
gf.check_events()
#更新屏幕上的图像,并切换到新屏幕
gf.update_screen(ai_setting, screen, ship)
run_game()
将飞船设置在屏幕最中间:
ship.py
:
#将每艘新飞船放在屏幕底部中央
self.rect.centerx = self.screen_rect.centerx #4 将飞船中心的x坐标(self.rect.centerx)设置为表示屏幕的矩形的属性centerx
self.rect.centery = self.screen_rect.centery # 4 将飞船中心的y坐标(self.rect.centery)设置为表示屏幕的矩形的属性centery
#self.rect.bottom = self.screen_rect.bottom #将飞船下边缘的y坐标设置为表示屏幕的矩形的属性bottom
4 驾驶飞船
让玩家能左右移动飞船,即用户按左键或右键时作出响应。
4.1响应按键
事件通过方法pygame.event.get()
获取,因此在函数check_events():
中,需要指定要检查哪些类型的事件。每次按键都被注册为一个KEYDOWN事件。
检测到KEYDOWN事件时,需要检查按下的是否是特定的键,如果按下右箭头键,增大飞船的rect.centerx
值,将飞船向右移动。
keyup与keydown事件的触发响应
①keyup触发场景:
当用户按下键,再次抬起时,被触发
②keydown触发场景:
当用户按下键时,立刻被触发
game_functions
代码:
def check_events(ship):
"""响应按键和鼠标事件"""
for event in pygame.event.get(): # 所有键盘和鼠标事件都将促使for循环运行
if event.type == pygame.QUIT:
sys.exit() # 调用sys.exit()退出游戏
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
# 向右移动飞船
ship.rect.centerx += 1
elif event.key == pygame.K_LEFT:
# 向左移动飞船
ship.rect.centerx -= 1
但是上面代码在右键按住不动的时候没有效果。
那么alien_invasion.py
也需要修改:
while True:
#此次更新 监视键盘和鼠标事件
gf.check_events(ship)
#更新屏幕上的图像,并切换到新屏幕
gf.update_screen(ai_setting, screen, ship)
4.2允许不断移动
游戏希望玩家按住右箭头不放的时候,飞船不断向右移动,直到玩家松开为止。
因此让游戏检测pygame.KEYUP
事件,以便玩家松开箭头时能知道这一点,然后结合使用KEYUP和KEYDOWN事件,以及一个名为moving_right
的标志实现持续移动。
飞船不动时,标志moving_right
将为False,用户按下键盘右键时,标志设置为True,而用户松开时,将标志重新设置为False。
飞船的属性都由Ship类控制,因此将此类添加一个名为moving_right
的属性和一个名为update()
的方法。方法update()
检查标志moving_right
的状态,如过标志位True,调整飞船的位置。
ship.py
:
import pygame
class Ship():
def __init__(self,screen):
"""初始化飞船并设置其初始位置"""
self.screen = screen
#加载飞船图像并获取其外接矩形
self.image = pygame.image.load('images/ship.bmp')
self.rect = self.image.get_rect() #2 使用get_rect()获取相应surface的属性rect
self.screen_rect = screen.get_rect() #3 将屏幕的矩形存储在self.screen_rect中
#将每艘新飞船放在屏幕底部中央
self.rect.centerx = self.screen_rect.centerx #4 将飞船中心的x坐标(self.rect.centerx)设置为表示屏幕的矩形的属性centerx
#self.rect.centery = self.screen_rect.centery # 将飞船中心的y坐标(self.rect.centery)设置为表示屏幕的矩形的属性centery
self.rect.bottom = self.screen_rect.bottom #将飞船下边缘的y坐标设置为表示屏幕的矩形的属性bottom
# 新加 移动标志
self.moving_right = False
self.moving_left = False
# 新加
def update(self):
"""根据移动标志调整飞船位置"""
if self.moving_right:
self.rect.centerx += 1
elif self.moving_left:
self.rect.centerx -= 1
def blitme(self):
"""在指定位置绘制飞船"""
self.screen.blit(self.image, self.rect) #5 根据self.rect指定的位置将图像绘制到屏幕上
接下来修改check_events(ship)
方法,使玩家按下右按键时将moving_right
设置为True,并在玩家松开右按键时设置为False。
game_functions
代码:
def check_events(ship):
"""响应按键和鼠标事件"""
for event in pygame.event.get(): # 所有键盘和鼠标事件都将促使for循环运行
if event.type == pygame.QUIT:
sys.exit() # 调用sys.exit()退出游戏
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
# 向右移动飞船
ship.moving_right = True
elif event.key == pygame.K_LEFT:
# 向左移动飞船
ship.moving_left = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
ship.moving_right = False
elif event.key == pygame.K_LEFT:
ship.moving_left = False
最后,修改alien_invasion.py
中的while循环,以便每次执行循环时都调用飞船的方法update()
。
while True:
#监视键盘和鼠标事件
gf.check_events(ship)
ship.update()
#更新屏幕上的图像,并切换到新屏幕
gf.update_screen(ai_setting, screen, ship)
现在运行alien_invasion.py
就会发现按住右箭头,飞船将不断往右移动,知道松开为止,左箭头同理。
4.3调整飞船的速度
首先在settings.py
中设置飞船的速度:
class Settings():
"""存储《外星人入侵》的所有设置的类"""
def __init__(self):
"""初始化游戏的设置"""
#屏幕设置
self.screen_width = 1200 #屏幕宽1200
self.screen_height = 800 #屏幕高800
#self.bg_color = (0, 0, 255) #蓝色
self.bg_color = (230, 230, 230) # 浅灰色
self.ship_speed_factor = 1.5 #设置飞船速度为1.5
然后改变ship.py
中update()
方法:
import pygame
class Ship():
def __init__(self, ai_settings, screen): #1
"""初始化飞船并设置其初始位置"""
self.screen = screen
self.ai_settings = ai_settings #2
#加载飞船图像并获取其外接矩形
self.image = pygame.image.load('images/ship.bmp')
self.rect = self.image.get_rect() # 使用get_rect()获取相应surface的属性rect
self.screen_rect = screen.get_rect() # 将屏幕的矩形存储在self.screen_rect中
#将每艘新飞船放在屏幕底部中央
self.rect.centerx = self.screen_rect.centerx # 将飞船中心的x坐标(self.rect.centerx)设置为表示屏幕的矩形的属性centerx
#self.rect.centery = self.screen_rect.centery # 将飞船中心的y坐标(self.rect.centery)设置为表示屏幕的矩形的属性centery
self.rect.bottom = self.screen_rect.bottom #将飞船下边缘的y坐标设置为表示屏幕的矩形的属性bottom
# 在飞船的属性center中加入小数
self.center = float(self.rect.centerx) #3
#移动标志
self.moving_right = False
self.moving_left = False
def update(self):
"""根据移动标志调整飞船位置"""
if self.moving_right:
self.center += self.ai_settings.ship_speed_factor #4
elif self.moving_left:
self.center -= self.ai_settings.ship_speed_factor
#根据self.center更新rect对象
self.rect.centerx = self.center #5
在1处,在__init__
列表中增加了形参ai_settings,让飞船能够获取其速度。在2处,将形参ai_settings存储属性中,以便在update()中使用。
建议调整飞船位置时,将增加或减去一个单位为像素的小数值,因此需要将位置存储在一个能够存储小数值的变量中。使用小数设置rect的属性。但rect将只存储这个值得整数部分,为了准确地存储飞船的位置,定义一个可以存储小数值的新属性self.center(3处)。并在update中更新self.center(见4)。更新后,再根据它控制飞船位置的self.rect.centerx
(见5)self.rect.centerx
将只存储飞船的整数部分,但对于显示飞船而言问题不大。
在alien_invasion.py
中创建Ship实例时,需要传入实参ai_settings
:
# 创建飞船
ship = Ship(ai_setting, screen)
4.4限制飞船的活动范围
当前,若玩家一致按住箭头不妨,飞船将移动到屏幕外。下面修复这个问题,让飞船移动到屏幕边缘后停止移动。
为此,修改Ship
类中的方法update()
:
def update(self):
"""根据移动标志调整飞船位置"""
if self.moving_right and self.rect.right < self.screen_rect.right:
self.center += self.ai_settings.ship_speed_factor
elif self.moving_left and self.rect.left > self.screen_rect.left:
self.center -= self.ai_settings.ship_speed_factor
#根据self.center更新rect对象
self.rect.centerx = self.center
4.6重构check_events()
将响应按键按下和响应按键松开设置为新函数。
def check_keydown_events(event, ship):
"""响应按键按下"""
if event.key == pygame.K_RIGHT:
# 向右移动飞船
ship.moving_right = True
elif event.key == pygame.K_LEFT:
# 向左移动飞船
ship.moving_left = True
def check_keyup_events(event, ship):
"""响应按键松开"""
if event.key == pygame.K_RIGHT:
ship.moving_right = False
elif event.key == pygame.K_LEFT:
ship.moving_left = False
def check_events(ship):
"""响应按键和鼠标事件"""
for event in pygame.event.get(): # 所有键盘和鼠标事件都将促使for循环运行
if event.type == pygame.QUIT:
sys.exit() # 调用sys.exit()退出游戏
elif event.type == pygame.KEYDOWN:
check_keydown_events(event, ship)
elif event.type == pygame.KEYUP:
check_keyup_events(event, ship)
5 射击
添加射击功能,编写玩家按空格键时发射子弹(小矩形)的代码。子弹将在屏幕中向上穿行,抵达屏幕边缘后消失。
5.1添加子弹设置
更新setting.py
,在方法__init__()
末尾存储新类Bullet所需的值。
class Settings():
"""存储《外星人入侵》的所有设置的类"""
def __init__(self):
"""初始化游戏的设置"""
#屏幕设置
self.screen_width = 1200 #屏幕宽1200
self.screen_height = 800 #屏幕高800
#self.bg_color = (0, 0, 255) #蓝色
self.bg_color = (230, 230, 230) # 浅灰色
#飞船设置
self.ship_speed_factor = 1.5 #设置飞船速度为1.5
#子弹设置
self.bullet_speed_factor = 1
self.bullet_width = 3
self.bullet_hight = 15
self.bullet_color = 60, 60, 60
5.2创建Bullet类
bullet.py
前半部分:
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
"""一个对飞船发射的子弹进行管理的类"""
def __init__(self, ai_settings, screen, ship):
"""在飞船所处的位置创建一个子弹对象"""
super().__init__()
self.screen = screen
# 在(0,0)处创建一个表示子弹的矩形,在设置正确的位置
self.rect = pygame.Rect(0, 0, ai_settings.bullet_width, ai_settings.bullet_hight) #1
self.rect.centerx = ship.centerx #2
self.rect.top = ship.top #3
#存储用小数表示的子弹位置
self.y = float(self.rect.y) #4
self.color = ai_settings.bullet_color
self.speed_factor = ai_settings.bullet_speed_factor
Bullet类继承了从模块pygame.sprite
中导入的Sprite
类。通过使用精灵,可将游戏中相关元组编组,进而同时操作编组中的所有元素。
1处创建了子弹的属性rect。子弹并非基于图像的,因此必须使用pygame.Rect()
类从空白开始创建一个矩形。
Rect(left, top, width, height)
下面是bullet.py
的后半部分——方法update()和draw_bullet():
def update(self):
"""向上移动子弹"""
#更新表示子弹位置的小数值
self.y -= self.speed_factor
#更新表示子弹位置的rect的位置
self.rect.y = self.y
def draw_bullet(self):
"""在屏幕上绘制子弹"""
pygame.draw.rect(self.screen,self.color, self.rect)
需要绘制子弹时,调用draw_bullet()
。
5.3将子弹存储在编组中
首先在alien_invasion.py
中创建一个编组(group),用于存储所有有效的子弹,以便能够管理发射出去的子弹。这个编组将是pygame.sprite.Group
类中的一个实例。pygame.sprite.Group
类似于列表,但提供了有助于开发游戏的格外功能。
在主循环中使用这个编组在屏幕上绘制子弹,已更新每颗子弹的位置。
import sys
import pygame
from settings import Settings
from ship import Ship
import game_function as gf
from pygame.sprite import Group
def run_game():
# 初始化游戏并创建一个屏幕对象
pygame.init() #初始化背景设置,让Pygame能正确工作
ai_setting = Settings()
screen = pygame.display.set_mode((ai_setting.screen_width, ai_setting.screen_height)) #创建一个名为scree,宽1200,高800像素的游戏窗口,display.set_mode返回surface表示整个游戏窗口
pygame.display.set_caption("Alien Invasion")
# 创建飞船
ship = Ship(ai_setting, screen)
#创建一组用于存储子弹的编组
bullets = Group() #创建一个Group实例
#开始游戏的主循环
while True:
#监视键盘和鼠标事件
gf.check_events(ai_setting,screen, ship, bullets)
ship.update()
bullets.update()
#更新屏幕上的图像,并切换到新屏幕
gf.update_screen(ai_setting, screen, ship)
run_game()
5.4开火
game_funcrion.py
修改:
import sys
import pygame
from ship import Ship
from bullet import Bullet
def check_keydown_events(event, ai_settings, screen, ship, bullets):
"""响应按键按下"""
if event.key == pygame.K_RIGHT:
# 向右移动飞船
ship.moving_right = True
elif event.key == pygame.K_LEFT:
# 向左移动飞船
ship.moving_left = True
elif event.key == pygame.K_SPACE:
#创建一颗子弹,用将其加入到编组bullets中
new_bullet = Bullet(ai_settings, screen, ship)
bullets.add(new_bullet)
def check_keyup_events(event, ship):
"""响应按键松开"""
if event.key == pygame.K_RIGHT:
ship.moving_right = False
elif event.key == pygame.K_LEFT:
ship.moving_left = False
def check_events(ai_settings, screen, ship, bullets):
"""响应按键和鼠标事件"""
for event in pygame.event.get(): # 所有键盘和鼠标事件都将促使for循环运行
if event.type == pygame.QUIT:
sys.exit() # 调用sys.exit()退出游戏
elif event.type == pygame.KEYDOWN:
check_keydown_events(event, ai_settings, screen, ship, bullets)
elif event.type == pygame.KEYUP:
check_keyup_events(event, ship)
def update_screen(ai_settings, screen, ship, bullets):
"""更新屏幕上的图像,并切换到新屏幕"""
# 每次循环都重绘屏幕
screen.fill(ai_settings.bg_color)
#在飞船和外星人后面重绘所有子弹
for bullet in bullets.sprites():
bullet.draw_bullet()
ship.blitme() # 确保飞船绘制在屏幕上,并出现在背景前面
# 让最近绘制的屏幕可见
pygame.display.flip()
5.5删除已消失的子弹
当前子弹抵达屏幕后消失是哪位Pygame无法再屏幕外面绘制它们,这些子弹实际上仍然存在,它们的y坐标为负数,且越来越小,会持续小航内存和处理能力。
需要删除已经消失的子弹
alien_invasion.py
:
bullets.update()
#删除已经消失的子弹
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
print(len(bullets))
#更新屏幕上的图像,并切换到新屏幕
gf.update_screen(ai_setting, screen, ship, bullets)
print(len(bullets))
语句用于检查当前还有多少子弹。
5.6限制子弹的数量
settings.py
:
#子弹设置
self.bullet_speed_factor = 1
self.bullet_width = 3
self.bullet_hight = 15
self.bullet_color = 60, 60, 60
self.bullet_allowed = 3 #允许子弹的最大数量
修改game_function.py
中的check_keydown_events
:
def check_keydown_events(event, ai_settings, screen, ship, bullets):
"""响应按键按下"""
if event.key == pygame.K_RIGHT:
# 向右移动飞船
ship.moving_right = True
elif event.key == pygame.K_LEFT:
# 向左移动飞船
ship.moving_left = True
elif event.key == pygame.K_SPACE:
#创建一颗子弹,用将其加入到编组bullets中
if len(bullets) < ai_settings.bullet_allowed:
new_bullet = Bullet(ai_settings, screen, ship)
bullets.add(new_bullet)
5.7创建函数update_bullets()
使主函数变简单,在game_function.py
中增加update_bullets()
:
def update_bullets(bullets):
bullets.update()
# 删除已经消失的子弹
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
alien_invasion.py
:
while True:
#监视键盘和鼠标事件
gf.check_events(ai_setting, screen, ship, bullets)
ship.update()
gf.update_bullets(bullets)
#更新屏幕上的图像,并切换到新屏幕
gf.update_screen(ai_setting, screen, ship, bullets)
5.8创建函数fire_bullet()
将发射子弹的代码移到一个独立的函数中,这样在check_keydown_events
中只需要使用一行代码发射子弹。
def fire_bullet(bullets, ai_settings, screen, ship):
# 创建一颗子弹,用将其加入到编组bullets中
if len(bullets) < ai_settings.bullet_allowed:
new_bullet = Bullet(ai_settings, screen, ship)
bullets.add(new_bullet)
def check_keydown_events(event, ai_settings, screen, ship, bullets):
"""响应按键按下"""
if event.key == pygame.K_RIGHT:
# 向右移动飞船
ship.moving_right = True
elif event.key == pygame.K_LEFT:
# 向左移动飞船
ship.moving_left = True
elif event.key == pygame.K_SPACE:
#创建一颗子弹,用将其加入到编组bullets中
fire_bullet(bullets, ai_settings, screen, ship)
6回顾(整体代码在这里)
6.1 alien_invasion.py
主文件alien_invasion.py
包括一些列整个游戏都需要封装的对象:存储在ai_setting
中的设置,存储在screen
中的主显示surface以及一个飞船实例、游戏的主循环,这是一个调用check_events
、ship.update()
、update_screen()
的while循环。
import sys
import pygame
from settings import Settings
from ship import Ship
import game_function as gf
from pygame.sprite import Group
def run_game():
# 初始化游戏并创建一个屏幕对象
pygame.init() #初始化背景设置,让Pygame能正确工作
ai_setting = Settings()
screen = pygame.display.set_mode((ai_setting.screen_width, ai_setting.screen_height)) #创建一个名为scree,宽1200,高800像素的游戏窗口,display.set_mode返回surface表示整个游戏窗口
pygame.display.set_caption("Alien Invasion")
# 创建飞船
ship = Ship(ai_setting, screen)
#创建一组用于存储子弹的编组
bullets = Group() #创建一个Group实例
#开始游戏的主循环
while True:
#监视键盘和鼠标事件
gf.check_events(ai_setting, screen, ship, bullets)
ship.update()
gf.update_bullets(bullets)
#更新屏幕上的图像,并切换到新屏幕
gf.update_screen(ai_setting, screen, ship, bullets)
run_game()
6.2 settings.py
文件settings.py
包含Settings
类,这个类只包含方法__init__()
,它初始化控制游戏外观和飞船速度、子弹的属性。
class Settings():
"""存储《外星人入侵》的所有设置的类"""
def __init__(self):
"""初始化游戏的设置"""
#屏幕设置
self.screen_width = 1200 #屏幕宽1200
self.screen_height = 800 #屏幕高800
#self.bg_color = (0, 0, 255) #蓝色
self.bg_color = (230, 230, 230) # 浅灰色
#飞船设置
self.ship_speed_factor = 1.5 #设置飞船速度为1.5
#子弹设置
self.bullet_speed_factor = 1
self.bullet_width = 3
self.bullet_hight = 15
self.bullet_color = 60, 60, 60
self.bullet_allowed = 3 #允许子弹的最大数量
6.3 game_function.py
文件game_function.py
包含一系列函数,游戏的大部分工作由他们完成。函数check_events()
检测相关的事件,如按键和松开,并使用辅助函数check_keydown_events()
和check_keyup_events()
处理这些事件。这些函数管理飞船的移动,以及子弹的发射,模块game_function.py
还包含函数update_screen()
,用于在每次执行主循环时重绘屏幕。
import sys
import pygame
from ship import Ship
from bullet import Bullet
def update_bullets(bullets):
bullets.update()
# 删除已经消失的子弹
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
def fire_bullet(bullets, ai_settings, screen, ship):
# 创建一颗子弹,用将其加入到编组bullets中
if len(bullets) < ai_settings.bullet_allowed:
new_bullet = Bullet(ai_settings, screen, ship)
bullets.add(new_bullet)
def check_keydown_events(event, ai_settings, screen, ship, bullets):
"""响应按键按下"""
if event.key == pygame.K_RIGHT:
# 向右移动飞船
ship.moving_right = True
elif event.key == pygame.K_LEFT:
# 向左移动飞船
ship.moving_left = True
elif event.key == pygame.K_SPACE:
#创建一颗子弹,用将其加入到编组bullets中
fire_bullet(bullets, ai_settings, screen, ship)
def check_keyup_events(event, ship):
"""响应按键松开"""
if event.key == pygame.K_RIGHT:
ship.moving_right = False
elif event.key == pygame.K_LEFT:
ship.moving_left = False
def check_events(ai_settings, screen, ship, bullets):
"""响应按键和鼠标事件"""
for event in pygame.event.get(): # 所有键盘和鼠标事件都将促使for循环运行
if event.type == pygame.QUIT:
sys.exit() # 调用sys.exit()退出游戏
elif event.type == pygame.KEYDOWN:
check_keydown_events(event, ai_settings, screen, ship, bullets)
elif event.type == pygame.KEYUP:
check_keyup_events(event, ship)
def update_screen(ai_settings, screen, ship, bullets):
"""更新屏幕上的图像,并切换到新屏幕"""
# 每次循环都重绘屏幕
screen.fill(ai_settings.bg_color)
#在飞船和外星人后面重绘所有子弹
for bullet in bullets.sprites():
bullet.draw_bullet()
ship.blitme() # 确保飞船绘制在屏幕上,并出现在背景前面
# 让最近绘制的屏幕可见
pygame.display.flip()
6.4 ship.py
文件ship.py
包含Ship
类,这个类包含__init__()
方法、管理飞船位置的方法update()
和在屏幕上绘制飞船的方法blitme()
。表示飞船的图像存储在文件夹images下的文件ship.bmp中。
import pygame
class Ship():
def __init__(self, ai_settings, screen):
"""初始化飞船并设置其初始位置"""
self.screen = screen
self.ai_settings = ai_settings
#加载飞船图像并获取其外接矩形
self.image = pygame.image.load('images/ship.bmp')
self.rect = self.image.get_rect() #2 使用get_rect()获取相应surface的属性rect
self.screen_rect = screen.get_rect() #3 将屏幕的矩形存储在self.screen_rect中
#将每艘新飞船放在屏幕底部中央
self.rect.centerx = self.screen_rect.centerx #4 将飞船中心的x坐标(self.rect.centerx)设置为表示屏幕的矩形的属性centerx
#self.rect.centery = self.screen_rect.centery # 将飞船中心的y坐标(self.rect.centery)设置为表示屏幕的矩形的属性centery
self.rect.bottom = self.screen_rect.bottom #将飞船下边缘的y坐标设置为表示屏幕的矩形的属性bottom
# 在飞船的属性center中加入小数
self.center = float(self.rect.centerx)
#移动标志
self.moving_right = False
self.moving_left = False
def update(self):
"""根据移动标志调整飞船位置"""
if self.moving_right and self.rect.right < self.screen_rect.right:
self.center += self.ai_settings.ship_speed_factor
elif self.moving_left and self.rect.left > self.screen_rect.left:
self.center -= self.ai_settings.ship_speed_factor
#根据self.center更新rect对象
self.rect.centerx = self.center
def blitme(self):
"""在指定位置绘制飞船"""
self.screen.blit(self.image, self.rect) #5 根据self.rect指定的位置将图像绘制到屏幕上
6.5 bullet.py
创建子弹以及子弹更新和绘制子弹的函数。
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
"""一个对飞船发射的子弹进行管理的类"""
def __init__(self, ai_settings, screen, ship):
"""在飞船所处的位置创建一个子弹对象"""
super().__init__()
self.screen = screen
# 在(0,0)处创建一个表示子弹的矩形,在设置正确的位置
self.rect = pygame.Rect(0, 0, ai_settings.bullet_width, ai_settings.bullet_hight)
self.rect.centerx = ship.rect.centerx
self.rect.top = ship.rect.top
#存储用小数表示的子弹位置
self.y = float(self.rect.y)
self.color = ai_settings.bullet_color
self.speed_factor = ai_settings.bullet_speed_factor
def update(self):
"""向上移动子弹"""
#更新表示子弹位置的小数值
self.y -= self.speed_factor
#更新表示子弹位置的rect的位置
self.rect.y = self.y
def draw_bullet(self):
"""在屏幕上绘制子弹"""
pygame.draw.rect(self.screen,self.color, self.rect)