武装飞船
1.规划项目
在游戏<<外星人入侵>>中,玩家控制着一艘最初出现在屏幕底部中央的飞船.玩家可以使用箭头键左右移动飞船,还可以使用空格键进行射击.游戏开始时,一群外星人出现在天空中,它们在屏幕中向下移动.玩家的任务是射杀这些外星人.玩家将所有外星人都消灭干净后,将出现以群新的外星人,他们移动的速度更快.只要有歪心何忍撞到了玩家的飞船,或者到达了屏幕底部,玩家就损失一艘飞船.玩家损失三艘飞船后,游戏结束.
2.开始游戏项目
2.1 创建Pygame窗口以及响应用户输入
游戏的基本结构:
import sys
import pygame
def run_game():
"""初始化游戏并创建一个屏幕对象"""
pygame.init()
screen = pygame.display.set_mode((1200,800))
pygame.display.set_caption("Alien Invation")
# 开始游戏的主循环
while True:
# 监视键盘和鼠标事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
# 让最近回值的屏幕可见
pygame.display.flip()
run_game()
首先,我们导入了模块sys和pygame.模块pygame包含开发游戏所需的功能.玩家退出时,我们将使用模块sys来退出游戏
游戏<<外星人入侵>>开头是run_game()函数.
(1)pygame.init()初始化背景设置,让Pygame能正确工作
(2)pygame.display.set_mode((1200,800))来创建一个名为screen的显示窗口,这个游戏的所有图形元素都将在其中绘制.实参(1200,800)是个元组,指定了游戏窗口的尺寸.通过将这些尺寸值传递给pygame.display.set_mode()创建一个一个宽1200像素,高800像素的游戏窗口.
(3)对象screen是一个surface.在Pygame中,surface是屏幕的一部分,用于显示游戏元素.在这个游戏中,每个元素(如外星人或飞船)都是一个surface
(4)这个游戏是由一个while循环控制,其中包含一个事件循环以及管理屏幕更新的代码.事件是由用户玩游戏时执行的操作,如按键或者移动鼠标.为了让程序响应事件,我们编写一个事件循环,以侦听事件,并根据发生的事件执行响应任务.
(5)为了访问Pygame检测到的事件,我们使用方法 pygame.event.get().所有键盘和鼠标都将促进这个事件.
(6)调用pygame.display.flip(),命令Pygame让最近绘制的屏幕可见.在这里,每执行一次while循环时都绘制一个空屏幕,并擦去旧屏幕,以显示元素的新位置,并在原来的位置隐藏元素,从而营造平滑移动的效果
2.2 设置背景色
import sys
import pygame
def run_game():
"""初始化游戏并创建一个屏幕对象"""
pygame.init()
screen = pygame.display.set_mode((1200,800))
pygame.display.set_caption("Alien Invation")
# 设置背景色
bg_color = (230,230,230)
# 开始游戏的主循环
while True:
# 监视键盘和鼠标事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
# 每次循环都重绘屏幕
screen.fill(bg_color)
# 让最近绘制的屏幕可见
pygame.display.flip()
run_game()
首先,我么创建了一种背景色,并将其存储在bg_color中.该颜色只需指定一次,因此我们在进入主循环前指定它
我们调用方法screen.fill(),用背景色填充屏幕;这个方法只接受一个实参:一种颜色
2.3 创建设置类
每次给游戏添加新功能时,通常也将引入一些新设置.下面编写一个名为settings的模块,其中包含一个名为settings的类,用于将所有设置存储在一个地方,以免再代码中到处添加设置.这让函数调用更加简单,且在项目增大时修改游戏外观更容易,要修改欧系,只需改setting.py当中的一些值,无需寻找散步再文件中的不同设置
class Settings():
"""存储 外星人入侵 的所有设置的类"""
def __init__(self):
""" 初始化游戏的设置"""
# 屏幕设置
self.screen_width = 1200
self.screen_height = 800
self.bg_color = (230,230,230)
ai_settings = Settings()
screen = pygame.display.set_mode((self.screen_width,self.screen_height))
---
screen.fill(ai_settings.bg_color)
3.添加飞船图像
3.1 创建Ship类
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()
self.screen_rect = screen.get_rect()
#将每艘新飞船放在屏幕底部中央
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
def blitme(self):
"""在指定位置绘制飞船"""
self.rect.blit(self.image,self.rect)
首先我们导入了模块pygame,Ship的方法__init__()接受两个参数:引用self和screen,然后后者指定了要将飞船绘制到什么地方.
为加载图像,我们调用了pygame.image.load(),这个函数返回一个表示飞船的surface,而我们将这个飞船存储到了self.image中
加载图像后,我们使用self.image.get_rect获取相应的属性rect(矩形).处理rect对象时,可使用据行的四角和中兴的x和y的坐标.要将游戏元素居中,可设置响应rect对象的属性center,centerx,centery…
我们将把飞船放在屏幕底部中央.首先将表示屏幕的矩形存储到self.screen_rect中,然后再将其放置底部中央
我们定义了方法blitme(),它根据self.rect指定位置将图像绘制在屏幕上.
3.2 在屏幕上绘制飞船
import sys
from settings import Settings
from ship import Ship
import pygame
def run_game():
"""初始化游戏并创建一个屏幕对象"""
pygame.init()
ai_settings = Settings()
screen = pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
pygame.display.set_caption("Alien Invation")
# 创建一艘飞船
ship = Ship(screen)
# 开始游戏的主循环
while True:
# 监视键盘和鼠标事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
# 每次循环都重绘屏幕
screen.fill(ai_settings.bg_color)
ship.blitme()
# 让最近绘制的屏幕可见
pygame.display.flip()
run_game()
4.重构:模块game_functions
在大型项目中,经常需要添加新代码前重构既有代码.重构旨在简化既有代码的结构,使其更容易拓展.在此,我们创建一个名为game_functions的新模块,它将存储大量让游戏运行的函数.
4.1 函数check_events()
我们首先把管理事件的代码移动到一个名为check_events()的函数中,以简化run_game()并隔离事件管理循环.通过隔离事件循环,可将事件管理与游戏的其他方面(如屏幕更新)分离
import sys
import pygame
def check_events():
"""响应按键和鼠标事件"""
for eventt in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
这个模块导入了事件检查循环要用的sys和pygame.当前check_events()不需要任何形参,其复制了alien_events()的调用
4.2 函数update_screen()
def update_screen(ai_settings,screen,ship):
"""更新屏幕上的图像,并切换到新屏幕"""
# 每次循环都重绘屏幕
screen.fill(ai_settings.bg_color)
ship.blitme()
# 让最近绘制的屏幕可见
pygame.display.flip()
5. 驾驶飞船
下面让玩家能够左右移动飞船,在用户按住左或右箭头时作出响应.
5.1 按键响应
每当用户按键时,都将在Pygame中注册一个新事件.事件是通过方法pygame.event.get()获取的,因此在函数check_events()中我们需要指定检查哪些类型的事件.每次按键都被注册为一个KEYDOWN事件.
检查KEYDOWN事件时,我们需要检查按下的是否时特定的键.更新check_events()函数
def check_events(ship):
"""响应按键和鼠标事件"""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
#向右移动飞船
ship.rect.centerx += 1
5.2 不断移动
玩家按住右箭头不放时,我们希望飞船不断地向右移动,直到玩家松开为止.
我们将让游戏检测pygame.KEYUP事件,以便玩家松开右箭头时我们能够知道这一点;然后我们结合使用KEYDOWN和KEYUP事件,以及一个moving_right的标志来实现持续移动
飞船不动时,标志moving_right为False,玩家按下右箭头时,标志为True,玩家松开时,标志为False.
飞船的属性都是由ship类控制,因此我们将给这个类添加一个名为moving_right的属性和一个名为update()的方法,update用于检验标志moving_right的状态
5.3 左右移动、调整飞船速度、限制飞船活动范围、重构check_events()
—ship.py
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()
self.screen_rect = screen.get_rect()
#将每艘新飞船放在屏幕底部中央
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
#在飞船的属性center中存储小数值
self.center = float(self.rect.centerx)
#移动标志
self.moving_right = False
self.moving_left = False
def update(self):
"""根据移动标志调整飞船的位置"""
#更新center值,而不是rect
if self.moving_right and self.rect.right < self.screen_rect.right:
self.center += self.ai_settings.ship_speed_factor
if self.moving_left and self.rect.left > 0:
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)
—game_function.py
import sys
import pygame
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():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
check_keydown_events(event,ship)
elif event.type == pygame.KEYUP:
check_keyup_events(event,ship)
def update_screen(ai_settings,screen,ship):
"""更新屏幕上的图像,并切换到新屏幕"""
# 每次循环都重绘屏幕
screen.fill(ai_settings.bg_color)
ship.blitme()
# 让最近绘制的屏幕可见
pygame.display.flip()
—alien_invasion.py
import sys
from settings import Settings
from ship import Ship
import game_functions as gf
import pygame
def run_game():
"""初始化游戏并创建一个屏幕对象"""
pygame.init()
ai_settings = Settings()
screen = pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
pygame.display.set_caption("Alien Invation")
# 创建一艘飞船
ship = Ship(ai_settings,screen)
# 开始游戏的主循环
while True:
gf.check_events(ship)
ship.update()
gf.update_screen(ai_settings,screen,ship)
run_game()
6 射击
下面添加射击功能,我们将编写玩家按空格键时发射子弹的代码.子弹将在屏幕中向上穿行,抵达屏幕上边缘后消失
6.1 添加子弹设置
—settings.py
#子弹设置
## 这些设置创建宽3像素、高15像素的深灰色子弹,子弹的速度比飞船稍低
self.bullet_speed_factor = 1
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color = 60,60,60
6.2 创建Bullet类
—bullet.py
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
"""一个对飞船发射子弹进行管理的类"""
def __init__(self,ai_settings,screen,ship):
"""在飞船所处位置创建一个子弹对象"""
supper(Bullet,self).__init__()
self.screen = screen
#在(0,0)处创建一个表示子弹的矩形,再设置正确的位置
self.rect = Pygame.Rect(0,0,ai_settings.bullet_width,
ai_settings.bullet_height)
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)
6.3 将子弹存储到编组中
在玩家每次按空格键时都射发出一颗子弹.
import sys
from settings import Settings
from ship import Ship
import game_functions as gf
from pygame.sprite import Group
import pygame
def run_game():
"""初始化游戏并创建一个屏幕对象"""
pygame.init()
ai_settings = Settings()
screen = pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
pygame.display.set_caption("Alien Invation")
# 创建一艘飞船
ship = Ship(ai_settings,screen)
#创建一个用于存储子弹的编组
bullets = Group()
# 开始游戏的主循环
while True:
gf.check_events(ai_settings,screen,ship,bullets)
ship.update()
bullets.update()
gf.update_screen(ai_settings,screen,ship,bullets)
run_game()
6.4 开火
import sys
import pygame
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)
bullet.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():
if event.type == pygame.QUIT:
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():
bullte.draw_bullet()
ship.blitme()
# 让最近绘制的屏幕可见
pygame.display.flip()
6.5 删除已经消失的子弹
#删除已经消失的子弹
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
print(len(bullets))
6.6 子弹设置
—game_function.py
import sys
import pygame
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:
fire_bullet(ai_settings,screen,ship,bullets)
def fire_bullet(ai_settings,screen,ship,bullets):
"""如果没有达到限制,就发射一颗子弹"""
#创建一颗子弹,并将其加入到编组bullets中
if len(bullets) < ai_settings.bullet_allowed:
new_bullet = Bullet(ai_settings,screen,ship)
bullet.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():
if event.type == pygame.QUIT:
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():
bullte.draw_bullet()
ship.blitme()
# 让最近绘制的屏幕可见
pygame.display.flip()
def update_bullets(bullets):
"""更新子弹位置,并删除已经消失的子弹"""
# 更新子弹的位置
bullets.update()
#删除已经消失的子弹
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
— alien_invasion.py
import sys
from settings import Settings
from ship import Ship
import game_functions as gf
from pygame.sprite import Group
import pygame
def run_game():
"""初始化游戏并创建一个屏幕对象"""
pygame.init()
ai_settings = Settings()
screen = pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
pygame.display.set_caption("Alien Invation")
# 创建一艘飞船
ship = Ship(ai_settings,screen)
#创建一个用于存储子弹的编组
bullets = Group()
# 开始游戏的主循环
while True:
gf.check_events(ai_settings,screen,ship,bullets)
ship.update()
gf.update_bullets(bullets)
gf.update_screen(ai_settings,screen,ship,bullets)
run_game()