Alien invasion外星人入侵
武装飞船
1、规划项目
开发大型项目时,做好规划后再动手编写项目很重要。规划可确保你不偏离轨道,从而提高项目成功的可能性。
开发出来的效果
在游戏《外星人入侵》中,玩家控制着一艘最初出现在屏幕底部中央的飞船。玩家可以使用箭头键左右移动飞船,还可以使用空格键进行射击。
游戏开始时,一群外星人出现在天空中,他们在屏幕中向下移动。玩家的任务是射杀这些外星人。
2、安装Pygame
使用pip安装python包
# 安装pip window和osx下载方式不同
python get-pip.py
# 使用 pip 下载pygame
python -m pip install --user pygame
3、开始游戏项目
创建Pygame窗口以及响应用户输入
创建空的Pygame的窗口,编写游戏基本结构
- 初始化背景设置
- 游戏主体
- 监听事件等
alien_invasion.py
import sys
import pygame
def run_game():
# 初始化游戏并创建一个屏幕对象
pygame.init()
screen = pygame.display.set_mode((1200, 800))
pygame.display.set_caption("Alien Invasion")
# 开始游戏的主循环
while True:
# 监视键盘和鼠标事件
for event in pygame.event.get():
if event.type == pygame.quit():
sys.exit()
# 让最近绘制的屏幕可见
pygame.display.flip()
run_game()
设置背景色
Pygame默认创建黑色屏幕
# 设置背景色 浅灰色
bg_color = (230, 230, 230)
# 每次循环时都重绘制屏幕
screen.fill(bg_color)
创建设置类
每次增加游戏新功能时,通常也将引入一些新设置,避免在项目中到处添加设置,项目增大时修改游戏外观更容易。要修改游戏,只需要修改settings.py的一些值,无需查找散步在文件中的不同设置。
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
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 Invasion")
# 开始游戏的主循环
while True:
# 监视键盘和鼠标事件
for event in pygame.event.get():
if event.type == pygame.quit():
sys.exit()
# 每次循环时都重绘制屏幕
screen.fill(ai_settings.bg_color)
# 让最近绘制的屏幕可见
pygame.display.flip()
调用pygame.init(), 再创建一个Settings实例,将其存储在变量ai_settings中
4、添加飞船图像
加载一幅图像,再使用Pygame方法blit()绘制它。
可以在此网站找 https://pixabay.com/
游戏中几乎可以使用任何图像文件,单使用位图(.bmp) 文件最为简单,Pygame默认加载位图。
选择图像时尽可能选择背景透明的图像。
[图片上传失败...(image-8cb399-1676467847121)]
创建Ship类
将其显示到屏幕上,它负责管理飞船的大部分行为
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()
self.screen_rect = screen.get_rect()
# 将每艘新飞船放在屏幕底部中央
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
def blitme(self):
"""在指定位置绘制飞船"""
self.screen.blit(self.image, self.rect)
__init__()
接受两个参数,引用self和screen,screen指定了将飞船绘制到什么地方
pygame.image.load()
加载图像 返回一个表示飞船的surface,存储到self.image中
self.rect.centerx
飞船中心的x坐标
self.rect.bottom
飞船下边缘的y坐标
blitme方法将图像绘制到屏幕上
在屏幕上绘制飞船
alien_invasion.py
import sys
import pygame
from settings import Settings
from ship import Ship
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 Invasion")
# 创建一艘飞船
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()
启动报错
"C:/Users/ss/code/PycharmProjects/crash/part2/alieninvasion/alien_invasion.py", line 36, in run_game
ship.blitme()
File "C:\Users\ss\code\PycharmProjects\crash\part2\alieninvasion\ship.py", line 29, in blitme
self.screen_rect.blit(self.image, self.rect)
AttributeError: 'pygame.Rect' object has no attribute 'blit'
Process finished with exit code 1
AttributeError: 'pygame.Rect' object has no attribute 'blit'
self.screen_rect
即 screen.get_rect()
不能加载到bilt
错误原因代码引用错,参考其他博主也是如此错的 是screen不是screen_rect
self.screen.blit(self.image, self.rect)
修改之后便可正常启动项目
5、重构: 模块game_functions
在大型项目中,经常需要在添加新代码前重构既有的代码
重构旨在简化既有代码的结构,使其更容易扩展。
当前项目使用模块game_function,避免alien_invasion太长,使逻辑更容易理解。
game_function.py
隔离事件管理循环,将事件管理与游戏的其他方法分离
import pygame
import sys
def check_event():
"""响应按键和鼠标事件"""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
def update_screen(ai_setting, screen, ship):
"""更新屏幕上的图像,并切换到新屏幕"""
# 每次循环时都重绘制屏幕
screen.fill(ai_settings.bg_color)
ship.blitme()
# 让最近绘制的屏幕可见
pygame.display.flip()
调用game_function.py 方法
import sys
import pygame
from settings import Settings
from ship import Ship
import game_functions as gf
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 Invasion")
# 创建一艘飞船
ship = Ship(screen)
# 开始游戏的主循环
while True:
# 监视键盘和鼠标事件
gf.check_event()
gf.update_screen(ai_settings, screen, ship)
run_game()
6、驾驶飞船
来让玩家能够左右移动飞船
在用户按左或者右箭头键时做出响应
在函数check_events() 中,指定事件类型,每次按下都被注册未一个KEYDOWN事件。
检测到KEYDOWN事件时,我们要检查按下的是否是特定的键。左、右等,就可以控制屏幕图像移动
响应按键
game_function.py
按下右箭头键,就增大飞船的rect.conterx值,飞船向右移动
def check_event(ship):
"""响应按键和鼠标事件"""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.type == pygame.K_RIGHT:
# 向右移动飞船
ship.rect.centex += 1
alien_invasion.py
# 开始游戏的主循环
while True:
# 监视键盘和鼠标事件
gf.check_event(ship)
gf.update_screen(ai_settings, screen, ship)
允许不断移动
玩家按住右箭头不妨时,我们希望飞船不断地向右移动,知道玩家松开为止
KEYDOWN和KEYUOP事件 键盘的按下与松开
我们用标志来实现持续移动,飞船不动时,标志moving_right将为False。
飞船的属性Ship添加一个moving_right的属性和一个为update()的方法
ship.py
class Ship():
def __init__(self, screen):
"""初始化飞船并设置其初始值位置"""
self.screen = screen
# 移动标志 鼠标按下事件
self.moving_right = False
def update(self):
"""根据移动标志调整飞船的位置"""
if self.moving_right:
self.rect.centerx += 1
alien_invasion.py
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 Invasion")
# 创建一艘飞船
ship = Ship(screen)
# 开始游戏的主循环
while True:
# 监视键盘和鼠标事件
gf.check_event(ship)
ship.update()
gf.update_screen(ai_settings, screen, ship)
run_game()
当前程序,运行起来之后,按住右箭头飞船将不断向右移动,知道松开为止。(也可以移动到屏幕外表,哈哈)
左右移动
同样的方式添加向作移动的逻辑
使用moving_left
def check_event(ship):
"""响应按键和鼠标事件"""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
print("键盘事件: " + str(event.key))
if event.key == pygame.K_RIGHT:
# 向右移动飞船
# ship.rect.centex += 1
ship.moving_right = True
if event.key == pygame.K_LEFT:
ship.moving_left = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
ship.moving_right = False
if event.key == pygame.K_LEFT:
ship.moving_left = False
class Ship():
def __init__(self, screen):
"""初始化飞船并设置其初始值位置"""
self.screen = screen
# 移动标志 鼠标按下事件
self.moving_right = False
self.moving_left = False
def update(self):
"""根据移动标志调整飞船的位置"""
if self.moving_right:
self.rect.centerx += 1
if self.moving_left:
self.rect.centerx -= 1
调整飞船的速度
当前执行while循环时,飞船最多移动1像素,但在Settings类中添加属性ship_speed_factor, 用于控制飞船的速度。
在settings.py 添加属性
class Settings():
"""储存《外星人入侵》的所有设置类"""
def __init__(self):
"""初始化游戏的设置"""
# 屏幕设置
self.screen_width = 1000
self.screen_height = 700
# 灰 230, 230, 230 蓝 R:0 G:191 B:243
self.bg_color = (0, 191, 243)
self.bg_image = 'images/stars.bmp'
# 飞船的设置
self.ship_speed_factor = 1.5
class Ship():
def __init__(self, ai_settings, screen):
"""初始化飞船并设置其初始值位置"""
self.screen = screen
self.ai_settings = ai_settings
# 在飞船的属性center中储存小数值
self.center = float(self.rect.centerx)
# 移动标志 鼠标按下事件
self.moving_right = False
self.moving_left = False
def update(self):
"""根据移动标志调整飞船的位置"""
if self.moving_right:
self.center += self.ai_settings.ship_speed_factor
if self.moving_left:
self.center -= self.ai_settings.ship_speed_factor
# 根据self.centerx 更新rect对象
self.rect.centerx = self.center
def blitme(self):
"""在指定位置绘制飞船"""
self.screen.blit(self.image, self.rect)
限制飞船的活动范围
当前飞船无线左右移动,只要不松开键盘,可以移动到宇宙之外
def update(self):
"""根据移动标志调整飞船的位置"""
# 限制飞船活动范围
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
向右移动不能超过屏幕最大宽度,向左移动要大于1
重构check_events()
把响应键按下和响应键松开独立出来
def check_event(ship):
"""响应按键和鼠标事件"""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
print("键盘事件: " + str(event.key))
check_keydown_event(event, ship)
elif event.type == pygame.KEYUP:
check_keyup_event(event, ship)
def check_keydown_event(event, ship):
"""键盘按下"""
if event.key == pygame.K_RIGHT:
# 向右移动飞船
# ship.rect.centex += 1
ship.moving_right = True
if event.key == pygame.K_LEFT:
ship.moving_left = True
def check_keyup_event(event, ship):
"""键盘松开"""
if event.key == pygame.K_RIGHT:
ship.moving_right = False
if event.key == pygame.K_LEFT:
ship.moving_left = False
7、简单回顾
4个py文件,作别作用
alien_invasion.py
主文件,整个游戏都要用到,包含游戏主题循环,调用check_enets(), ship.update()的while循环
其他文件也是直接或者间接引入
settings.py 包含Settings类,初始化控制游戏外观和飞船速度的属性
game_function.py 包含一系列函数,游戏大部分工作都是他们完成的。
检测事件check_enevts
8、射击
玩家按空格键时发射子弹(小矩形)。子弹将在屏幕中向上穿行,抵达屏幕上边缘后消失。
添加子弹设置
settings.py
# 子弹的设置
self.bullet_speed_factor = 1
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color = 60, 60, 60
创建宽3像素,高15像素的深灰色子弹,子弹的速度比飞船稍低
创建Bullet类
bullet.py
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
"""一个对飞船发射的子弹进行管理的类"""
def __init__(self, ai_settings, screen, ship):
"""在飞船所处的位置创键一个子弹"""
super(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_
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)
Bullet类继承Sprite类,通过使用精灵将游戏元素编组,同时操作编组中的所有元素。调用super().__init()来继承Sprite。
创建的子弹属性rect,并非基于图像,使用pagame.Rect()类从空白开始创建一个矩形。创建实例时,必须提供矩形左上角的x,y坐标和矩形的宽度高度,在(0,0)处创建这个矩形,再将其移到正确的位置
子弹的centerx设置为飞船的rect.centerx。子弹从飞船顶部射出,子弹的rect的top属性为飞船的rect的top属性,这样子弹看起来就像是从飞船中射出来的
方法update()管理子弹的位置。放射出去后,子弹在屏幕中向上移动,y坐标将不断减少,来更新子弹的位置,self.y中减去self.speed_factor的值。子弹发射后,x坐标始终不变,子弹沿直线垂直地往上穿行。
将子弹存储到编组中
创建编组group,用于储存所有有效的子弹,以便于能后管理发射出去的所有字段。
编组在屏幕上绘制子弹,以及更新每颗子弹的位置
from pygame.sprite import Group
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 Invasion")
# 创建一艘飞船
ship = Ship(ai_settings, screen)
# 创建储存子弹的编组
bullets = Group()
# 开始游戏的主循环
while True:
# 监视键盘和鼠标事件
gf.check_event(ai_settings, screen, ship, bullets)
ship.update()
bullets.update()
gf.update_screen(ai_settings, screen, ship, bullets)
开火
修改check_keydown_events() 玩家按空格键时发射一颗子弹,松开空格键时什么都不会发生。
def check_event(ai_settings, screen, ship, bullets):
"""响应按键和鼠标事件"""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
print("键盘事件: " + str(event.key))
check_keydown_event(event, ai_settings, screen, ship, bullets)
elif event.type == pygame.KEYUP:
check_keyup_event(event, ship)
def check_keydown_event(event, ai_settings, screen, ship, bullets):
"""键盘按下"""
if event.key == pygame.K_RIGHT:
# 向右移动飞船
# ship.rect.centex += 1
ship.moving_right = True
if event.key == pygame.K_LEFT:
ship.moving_left = True
if event.key == pygame.K_SPACE:
# 创建一颗子弹, 并将其加入到编组bullets中
new_bullet = Bullet(ai_settings, screen, ship)
bullets.add(new_bullet)
def update_screen(ai_settings, screen, ship, bullets):
"""更新屏幕上的图像,并切换到新屏幕"""
# 每次循环时都重绘制屏幕
screen.fill(ai_settings.bg_color)
# 添加背景
bg_image = pygame.image.load(ai_settings.bg_image).convert()
screen.blit(bg_image, (0, 0))
# 在飞船和外星人后面重新绘制子弹
for bullet in bullets.sprites():
bullet.draw_bullet()
启动报错,查看Bullet类是否有初始化,调用super()继续Sprite
File "C:\Users\shenshuaihu\lib\site-packages\pygame\sprite.py", line 160, in add_internal
self.__g[group] = 0
AttributeError: 'Bullet' object has no attribute '_Sprite__g'
<img src="C:\Users\shenshuaihu\AppData\Roaming\Typora\typora-user-images\image-20220914144536965.png" alt="image-20220914144536965" style="zoom:50%;" />
删除以消失的子弹
当子弹到达屏幕顶端后消失,不仅仅是Pygame不能在屏幕绘制他们,子弹实际依然存在,y坐标为负数,越来越少。他们继续消耗内存
如不删除的话,游戏的无谓工作越来越多,进而变得越来越慢。
子弹的rect的bottom属性为零时,表面以及穿过屏幕顶端了
def run_game():
# 初始化游戏并创建一个屏幕对象
....
# 创建储存子弹的编组
bullets = Group()
# 开始游戏的主循环
while True:
# 监视键盘和鼠标事件
gf.check_event(ai_settings, screen, ship, bullets)
ship.update()
bullets.update()
# 删除已消失的子弹
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
gf.update_screen(ai_settings, screen, ship, bullets)
run_game()
限制子弹数量
对屏幕上子弹数量进行限制,鼓励玩家有目标的射击
settings.py
self.bullets_allowed = 3
def check_keydown_event(event, ai_settings, screen, ship, bullets):
"""键盘按下"""
if event.key == pygame.K_RIGHT:
# 向右移动飞船
# ship.rect.centex += 1
ship.moving_right = True
if event.key == pygame.K_LEFT:
ship.moving_left = True
if event.key == pygame.K_SPACE:
# 创建一颗子弹, 并将其加入到编组bullets中
if len(bullets) < ai_settings.bullets_allowed:
new_bullet = Bullet(ai_settings, screen, ship)
bullets.add(new_bullet)
创建函数
update_bullets()
子弹管理代码独立
game_funcitions.py
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_event(ai_settings, screen, ship, bullets)
ship.update()
gf.update_bullets(bullets)
fire_bullet()
发射子弹的代码移动到一个独立的函数中
外星人
项目第二篇,从屏幕上方边缘添加一个外星人,然后生成一群外星人,外星人向两边和下面移动,并删除被子弹击中的外星人,最后,我们将显示玩家拥有飞船的数量,在玩家的飞船用完之后变结束游戏。
创建一个外星人
在平时上放置类似飞船的,创建Alien类,像Ship那样的。
创建alien类
alien.py
"""
======================
@title: game_functions
@description: 游戏方法
@author: elijah
@date: 2022/9/9 10:10
=====================
"""
import pygame
import sys
from bullet import Bullet
def check_event(ai_settings, screen, ship, bullets):
"""响应按键和鼠标事件"""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
print("键盘事件: " + str(event.key))
check_keydown_event(event, ai_settings, screen, ship, bullets)
elif event.type == pygame.KEYUP:
check_keyup_event(event, ship)
def check_keydown_event(event, ai_settings, screen, ship, bullets):
"""键盘按下"""
if event.key == pygame.K_q:
sys.exit()
if event.key == pygame.K_RIGHT:
# 向右移动飞船
# ship.rect.centex += 1
ship.moving_right = True
if event.key == pygame.K_LEFT:
ship.moving_left = True
if event.key == pygame.K_SPACE:
# 创建一颗子弹, 并将其加入到编组bullets中
fire_bullet(ai_settings, screen, ship, bullets)
if event.key == pygame.K_RETURN:
# 加大活力
fire_full_bullet(ai_settings, screen, ship, bullets)
def check_keyup_event(event, ship):
"""键盘松开"""
if event.key == pygame.K_RIGHT:
ship.moving_right = False
if event.key == pygame.K_LEFT:
ship.moving_left = False
def update_screen(ai_settings, screen, ship, alien, bullets):
"""更新屏幕上的图像,并切换到新屏幕"""
# 每次循环时都重绘制屏幕
screen.fill(ai_settings.bg_color)
# 添加背景
bg_image = pygame.image.load(ai_settings.bg_image).convert()
screen.blit(bg_image, (0, 0))
# 在飞船和外星人后面重新绘制子弹
for bullet in bullets.sprites():
bullet.draw_bullet()
ship.blitme()
alien.blitme()
# 让最近绘制的屏幕可见
pygame.display.flip()
def update_bullets(bullets):
"""更新子弹的位置,并删除已消失的子弹"""
bullets.update()
# 删除已消失的子弹
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
def fire_bullet(ai_settings, screen, ship, bullets):
"""如果没有达到限制,就发射一颗子弹"""
# 创建一颗子弹, 并将其加入到编组bullets中
if len(bullets) < ai_settings.bullets_allowed:
new_bullet = Bullet(ai_settings, screen, ship)
bullets.add(new_bullet)
def fire_full_bullet(ai_settings, screen, ship, bullets):
"""加大火力"""
# 创建一颗子弹, 并将其加入到编组bullets中
i = 0
while i < 80:
# new_ship = ship.copy()
# new_ship.rect.centerx = ship.rect.centerx
ai_settings.ship_full_fire = True
ai_settings.ship_full_fire_num = i * 2 + i
new_bullet = Bullet(ai_settings, screen, ship)
bullets.add(new_bullet)
i += 1
创建Alien实例
在alien_invasion.py 中创建Alien实例
from alien import Alien
def run_game():
# 初始化游戏并创建一个屏幕对象
...
# 创建一艘飞船
ship = Ship(ai_settings, screen)
# 创建储存子弹的编组
bullets = Group()
# 创建一个外星人
alien = Alien(ai_settings, screen)
# 开始游戏的主循环
while True:
# 监视键盘和鼠标事件
gf.check_event(ai_settings, screen, ship, bullets)
ship.update()
gf.update_bullets(bullets)
gf.update_screen(ai_settings, screen, ship, alien, bullets)
run_game()
在外星人出现在屏幕上
game_funcitons.py
def update_screen(ai_settings, screen, ship, alien, bullets):
"""更新屏幕上的图像,并切换到新屏幕"""
# 每次循环时都重绘制屏幕
screen.fill(ai_settings.bg_color)
# 添加背景
bg_image = pygame.image.load(ai_settings.bg_image).convert()
screen.blit(bg_image, (0, 0))
# 在飞船和外星人后面重新绘制子弹
for bullet in bullets.sprites():
bullet.draw_bullet()
ship.blitme()
alien.blitme()
# 让最近绘制的屏幕可见
pygame.display.flip()
启动项目,第一个外星人变出现了
创建一群外星人
简单计算空间
首先需要确定一行可以容纳多少个外星人
屏幕的宽度为ai_setting.screen_width,但屏幕两边需要留空白,两个外边距,放置外星人的水平空间为屏幕宽度减去外星人宽度的两倍
available_space_x = ai_setting.screen_width - (2 * alien_width)
外星人直接也需要流出空白,一个外星人水平需要的宽度是两个外星人的宽度,一行容纳多少外星人可以 可利用空间除以一个外星人的宽度
number_aliens_x = available_space_x / (2 * alien_width)
创建多行外星人
先创建aliens的编组,用于储存外星人,再调用创建函数create_fleet
alien_invasion.py
def run_game():
# 初始化游戏并创建一个屏幕对象
...
# 创建一艘飞船
ship = Ship(ai_settings, screen)
# 创建储存子弹的编组
bullets = Group()
# 一个外星人编组
aliens = Group()
# 创建外星人群
gf.create_fleet(ai_settings, screen, aliens)
# 开始游戏的主循环
while True:
# 监视键盘和鼠标事件
....
gf.update_screen(ai_settings, screen, ship, aliens, bullets)
run_game()
game_funcitons.py的update_screen() 修改参数名称 aliens
创建外星人群
def create_fleet(ai_settings, screen, aliens):
"""创建外星人舰队"""
# 创建一个外星人 并计算一行容纳多少人外星人
# 外星人间距为外星人宽度
alien = Alien(ai_settings, screen)
alien_width = alien.rect.width
available_space_x = ai_settings.screen_width - 2 * alien_width
number_aliens_x = int(available_space_x / (2 * alien_width))
# 创建一行外星人
for alien_number in range(number_aliens_x):
# 创建一个外星人并将其加入当前行
alien = Alien(ai_settings, screen)
alien.x = alien_width + 2 * alien_width * alien_number
alien.rect.x = alien.x
aliens.add(alien)
新增运行游戏即可以看到一行外星人了
重构create_fleet()
创建两个新函数
game_functions.py
def create_fleet(ai_settings, screen, aliens):
"""创建外星人舰队"""
# 创建一个外星人 并计算一行容纳多少人外星人
# 外星人间距为外星人宽度
alien = Alien(ai_settings, screen)
alien_width = alien.rect.width
number_aliens_x = get_number_aliens_x(ai_settings, alien_width)
# 创建一行外星人
for alien_number in range(number_aliens_x):
# 创建一个外星人并将其加入当前行
create_alien(ai_settings, screen, aliens, alien_number)
def get_number_aliens_x(ai_settings, alien_width):
"""计算每行容纳多少外星人"""
available_space_x = ai_settings.screen_width - 2 * alien_width
number_aliens_x = int(available_space_x / (2 * alien_width))
return number_aliens_x
def create_alien(ai_settings, screen, aliens, alien_number):
"""创建一个外星人并将其放在当前行"""
alien = Alien(ai_settings, screen)
alien_width = alien.rect.width
alien.x = alien_width + 2 * alien_width * alien_number
alien.rect.x = alien.x
aliens.add(alien)
添加行
计算可以容纳外形人的有限高度是多少, 垂直空间,屏幕高度减去第一行外星人的上边距,飞船高度和最初外星人高度加上外星人间距
屏幕高度 - 两处留白的位置 - 第一行外星人 - 飞船
available_space_y = ai_settings.screen_height - (3 * alien_height) - ship_height
可容纳多少行
number_rows = available_space_y / (2 * alien_height)
game_functions.py
def create_fleet(ai_settings, screen, ship, aliens):
"""创建外星人舰队"""
# 创建一个外星人 并计算一行容纳多少人外星人
# 外星人间距为外星人宽度
alien = Alien(ai_settings, screen)
alien_width = alien.rect.width
number_aliens_x = get_number_aliens_x(ai_settings, alien_width)
number_rows = get_number_rows(ai_settings, ship.rect.height, alien.rect.height)
# 创建一群外星人
for number_row in range(number_rows):
for alien_number in range(number_aliens_x):
# 创建一个外星人并将其加入当前行
create_alien(ai_settings, screen, aliens, alien_number, number_row)
def get_number_aliens_x(ai_settings, alien_width):
"""计算每行容纳多少外星人"""
available_space_x = ai_settings.screen_width - 2 * alien_width
number_aliens_x = int(available_space_x / (2 * alien_width))
return number_aliens_x
def create_alien(ai_settings, screen, aliens, alien_number, row_number):
"""创建一个外星人并将其放在当前行"""
alien = Alien(ai_settings, screen)
alien_width = alien.rect.width
alien.x = alien_width + 2 * alien_width * alien_number
alien.rect.x = alien.x
alien.rect.y = alien.rect.height + 2 * alien.rect.height * row_number
aliens.add(alien)
def get_number_rows(ai_settings, ship_height, alien_height):
"""计算屏幕可容纳多少行外星人"""
# 屏幕高度 - 两处留白的位置 - 第一行外星人 - 飞船
available_space_y = ai_settings.screen_height - (3 * alien_height) - ship_height
number_rows = int(available_space_y / (2 * alien_height))
return number_rows
创建多行时,需要两个嵌套的循环,内部循环场景一行外星人,外部循环从零数到要创建的行数
调用方法
# 创建外星人群
gf.create_fleet(ai_settings, screen, ship, aliens)
现在运行游戏变可以看到外星人。
也可以使用随机数来创建
# 随机创建
random_number = randint(0, 10)
if alien_number == random_number or number_row == random_number:
continue
让外星人群移动
让外星人在屏幕上向右移动,撞到屏幕边缘后下移一定距离,再沿相反的方向移动。
向右移动
# 开始游戏的主循环
while True:
# 监视键盘和鼠标事件
gf.check_event(ai_settings, screen, ship, bullets)
ship.update()
gf.update_bullets(bullets)
gf.update_aliens(aliens)
gf.update_screen(ai_settings, screen, ship, aliens, bullets)
game_functions.py
def update_aliens(aliens):
"""更新外星人群中的所有外星人位置"""
aliens.update()
alien.py
def update(self):
"""向右移动外星人"""
self.x += self.ai_settings.alien_speed_factor
self.rect.x = self.x
settings.py
# 外星人设置
self.alien_speed_factor = 1
移动方向控制
# 外星人设置
self.alien_speed_factor = 1
self.fleet_drop_speed = 10
# fleet_direction 为1表示向右移 -1表示向左移动
self.fleet_direction = 1
由于是两个方向,使用-1或者1就不会影响两个方向切换
检查是否撞到了屏幕边缘
def check_edges(self):
"""如果外星人位于屏幕边缘 就返回True"""
screen_rect = self.screen.get_rect()
if self.rect.right >= screen_rect.right:
return True
elif self.rect.left <= 0:
return True
def update(self):
"""向右移动外星人"""
self.x += (self.ai_settings.alien_speed_factor * self.ai_settings.fleet.direction)
self.rect.x = self.x
向下移动并改变方向
调用change_fleet_direction后,跳出循环,不然会一直向下
def update_aliens(ai_settings, aliens):
"""
检查是否有外星人位于屏幕边缘
更新外星人群中的所有外星人位置
"""
check_fleet_edges(ai_settings, aliens)
aliens.update()
def check_fleet_edges(ai_settings, aliens):
"""有外星人到达边缘时采取相应的措施"""
for alien in aliens.sprites():
if alien.check_edges():
change_fleet_direction(ai_settings, aliens)
break
def change_fleet_direction(ai_settings, aliens):
"""将外星人下移并改变方向"""
for alien in aliens.sprites():
alien.rect.y += ai_settings.fleet_drop_speed
ai_settings.fleet_direction *= -1
类似的可以做成雨点,下雨天
射杀外星人
sprite.groupcollide() 将每颗子弹的rect同每个外星人rect进行比较,并返回一个字典,包含碰撞的外星人和子弹,相应的值都是被击中的外星人, 两个True是删除响应的元素,如果是高能子弹的话 第一个参数可以为false不用删除。
# 检查是否有子弹击中外星人 如果是这样的 删除相应的子弹和外星人
collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)
可以改变子弹的宽度来提高射杀率
self.bullet_width = 300
生成新的外星人
if len(aliens) == 0:
# 删除现有的子弹并新建一群外星人
bullet.empty()
create_fleet(ai_settings, screen, ship, aliens)
提交子弹的速度,厚礼等
重构update_bullets()
结束游戏
增加趣味性
检测外星人和飞船碰撞
def update_aliens(ai_settings, ship, aliens):
"""
检查是否有外星人位于屏幕边缘
更新外星人群中的所有外星人位置
"""
...
# 检测外形人和飞船之间碰撞
if pygame.sprite.spritecollideany(ship, aliens):
print("--------------- ship hit!!! ------------------")
响应外星人和飞船碰撞
碰撞时,不销毁ship实例并创建一个新的ship,通过追踪游戏统计信息来记录飞船被撞的次数,便于后续记分
新建GameStats 用于跟踪游戏统计信息的类
game_stats.py
class GameStats():
"""跟踪游戏的统计信息"""
def __init__(self, ai_settings):
"""初始化统计信息"""
self.ai_settings = ai_settings
self.reset_stats()
def reset_stats(self):
"""初始化在游戏运行期间可能变化信息统计"""
self.ships_left = self.ai_settings.ship_limit
# 创建一个用于储存游戏统计信息的实例
stats = GameStats(ai_settings)
# 开始游戏的主循环
while True:
# 监视键盘和鼠标事件
gf.update_aliens(ai_settings, ship, aliens, stats)
响应被外星人撞到的飞船
撞击之后清空子弹和外星人, 创建新的外星人,把飞船放在屏幕中间,暂停1s
game_functions.py
def ship_hit(ai_settings, stats, screen, ship, aliens, bullets):
"""响应被外星人撞到的飞船"""
# 将ships_left减1
stats.ships_left -= 1
# 清空外星人列表和子弹
aliens.empty()
bullets.empty()
# 创建一群新的外星人 并将飞船放到屏幕底端中央
create_fleet(ai_settings, screen, ship, aliens)
ship.center_ship()
# 暂停
sleep(1)
def update_aliens(ai_settings, stats, screen, ship, aliens, bullets):
"""
检查是否有外星人位于屏幕边缘
更新外星人群中的所有外星人位置
"""
check_fleet_edges(ai_settings, aliens)
aliens.update()
# 检测外形人和飞船之间碰撞
if pygame.sprite.spritecollideany(ship, aliens):
print("--------------- ship hit!!! ------------------")
ship_hit(ai_settings, stats, screen, ship, aliens, bullets)
ship.py
def center_ship(self):
"""让飞船在屏幕上居中"""
self.center = self.screen_rect.centerx
有外星人到达屏幕底端
也做出与外星人撞击屏幕相同的响应
def check_aliens_bottom(ai_settings, stats, screen, ship, aliens, bullets):
"""检查是否有外星人到达屏幕底端"""
screen_rect = screen.get_rect()
for alien in aliens.sprites():
if alien.rect.bottom >= screen_rect.bottom:
"""像飞船撞击一样处理"""
ship_hit(ai_settings, stats, screen, ship, aliens, bullets)
游戏结束
给定一个游戏状态,飞船用完后结束游戏
game_stats.py
class GameStats():
"""跟踪游戏的统计信息"""
def __init__(self, ai_settings):
"""初始化统计信息"""
# 游戏启动处于活动状态
self.game_active = True
game_function.py
def ship_hit(ai_settings, stats, screen, ship, aliens, bullets):
"""响应被外星人撞到的飞船"""
if stats.ships_left > 0:
# 将ships_left减1
stats.ships_left -= 1
...
# 暂停
sleep(1)
else:
# game over
stats.game_active = False
alien_invasion.py
# 开始游戏的主循环
while True:
# 监视键盘和鼠标事件
gf.check_event(ai_settings, screen, ship, bullets)
if stats.game_active:
ship.update()
gf.update_bullets(ai_settings, screen, ship, bullets, aliens)
gf.update_aliens(ai_settings, stats, screen, ship, aliens, bullets)
gf.update_screen(ai_settings, screen, ship, aliens, bullets)
run_game()
当前游戏主体已经完成了,只是确认积分系统,游戏关卡,交互性比较好的UI。
也是托着连续失眠疲惫身体,坚持着,也盼望着可以看到我们所期待有意义的样子
2022/09/22 chengdu work in home!