1创建第一个外星人
1.1创建Alien类
alien.py
:
import pygame
from pygame.sprite import Sprite
class Alien(Sprite):
"""表示单个外星人并设置其初始位置"""
def __init__(self, ai_settings, screen):
"""初始化外星人并设置其初始位置"""
super().__init__()
self.screen = screen
self.ai_settings = ai_settings
#加入外星人图像,并设置其rect属性
self.image = pygame.image.load('images/alien.bmp')
self.rect = self.image.get_rect()
#每个外星人最初都在屏幕左上角附近
self.rect.x = self.rect.width
self.rect.y = self.rect.height
#存储外星人的准确位置
self.x = float(self.rect.x)
def blitme(self):
"""在指定位置绘制外星人"""
self.screen.blit(self.image, self.rect)
1.2创建Alien实例
在alien_invasion.py
中创建一个Alien实例:
# 创建飞船
ship = Ship(ai_setting, screen)
#创建外星人
alien = Alien(ai_setting, screen)
#开始游戏的主循环
while True:
#监视键盘和鼠标事件
gf.check_events(ai_setting, screen, ship, bullets)
ship.update()
gf.update_bullets(bullets)
#更新屏幕上的图像,并切换到新屏幕
gf.update_screen(ai_setting, screen, ship, bullets, alien)
1.3让外星人出现在屏幕上
game_function()
:
def update_screen(ai_settings, screen, ship, bullets, alien):
"""更新屏幕上的图像,并切换到新屏幕"""
# 每次循环都重绘屏幕
screen.fill(ai_settings.bg_color)
#在飞船和外星人后面重绘所有子弹
for bullet in bullets.sprites():
bullet.draw_bullet()
ship.blitme() # 确保飞船绘制在屏幕上,并出现在背景前面
alien.blitme() # 确保外星人在屏幕上,并出现在背景前面
# 让最近绘制的屏幕可见
pygame.display.flip()
2创建一群外星人
2.1确定一行可以容纳多少外星人
可容纳外星人的水平空间为屏幕宽度减去屏幕两边留下的边距(边距设置为外星人的宽度):
available_space_x = ai_settings.screen.width - alien_width*2
一行可容纳的外星人的个数为可容纳外星人的水平空间除以一个外星人所需的水平空间(即外星人宽度的两倍:一个宽度放外星人,一个宽度为外星人右边的空白区域):
number_aliens_x = int(available_space_x / (2 * alien_width))
2.2 创建多行外星人
alien_invasion.py
:
# 创建飞船
ship = Ship(ai_setting, screen)
#创建一组用于存储子弹的编组
bullets = Group() #创建一个Group实例
# 创建外星人
aliens = Group()
#开始游戏的主循环
while True:
#监视键盘和鼠标事件
gf.check_events(ai_setting, screen, ship, bullets)
ship.update()
gf.update_bullets(bullets)
gf.create_fleet(ai_setting, screen, aliens)
#更新屏幕上的图像,并切换到新屏幕
gf.update_screen(ai_setting, screen, ship, bullets, aliens)
game_function()
修改update_screen()
:
def update_screen(ai_settings, screen, ship, bullets, aliens):
"""更新屏幕上的图像,并切换到新屏幕"""
# 每次循环都重绘屏幕
screen.fill(ai_settings.bg_color)
#在飞船和外星人后面重绘所有子弹
for bullet in bullets.sprites():
bullet.draw_bullet()
ship.blitme() # 确保飞船绘制在屏幕上,并出现在背景前面
aliens.draw(screen) # 1确保外星人在屏幕上,并出现在背景前面
# 让最近绘制的屏幕可见
pygame.display.flip()
1处对编组调用draw()时,Pygame自动绘制编组的每个元素,绘制位置由元素的属性rect决定。aliens.draw(screen)
表示在屏幕上绘制编组中的每一个外星人。
2.3创建外星人群
game_function()
:
def create_fleet(ai_settings, screen, aliens):
"""创建外星人群"""
#创建一个外星人,并计算一行可以容纳多少外星人
# 外星人间距为外星人宽度
alien = Alien(ai_settings, screen)
alien_width = alien.rect.width
available_space_x = ai_settings.screen.width - alien_width*2
number_aliens_x = int(available_space_x / (2 * alien_width))
#创建第一行外星人
for alien_number in number_aliens_x:
#创建一个外星人并加入当前行
alien = Alien(ai_settings, screen)
alien.x = alien_width + 2 * alien_number * alien_number
alien.rect.x = alien.x
aliens.add(alien)
2.4重构create_fleet()
将create_fleet
重构为get_number_aliens_x
和create_alien
,使得添加新行创建整群外星人变得容易。
game_function.py
:
def get_number_aliens_x(ai_settings, alien_width):
"""计算每行可容纳多少外星人"""
# 外星人间距为外星人宽度
available_space_x = ai_settings.screen_width - alien_width * 2
number_aliens_x = int(available_space_x / (2 * alien_width))
return number_aliens_x
def create_alien(ai_settings, screen, alien_number, aliens):
# 创建一个外星人并将其放在当前行
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)
def create_fleet(ai_settings, screen, aliens):
"""创建外星人群"""
#计算一行可以容纳多少外星人
alien = Alien(ai_settings, screen)
number_aliens_x = get_number_aliens_x(ai_settings, alien.rect.width)
#创建第一行外星人
for alien_number in range(number_aliens_x):
create_alien(ai_settings, screen, alien_number, aliens)
2.5添加行
首先计算可用的垂直空间:将屏幕高度减去第一行外星人的上边距(外星人高度)、飞船的高度、最初外星人群与飞船的距离(外星人高度的2倍):
available_space_y = (ai_settings.screen_height - (3 * alien_height) - ship_height)
然后计算可容纳外星人的行数:可用垂直空间/外星人高度的两倍
number_rows = int(available_space_y / (2 * alien_height))
知道可容纳多少行后,重复执行创建一行外星人的代码:
game_function.py
:
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
def create_alien(ai_settings, screen, alien_number, aliens, 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 create_fleet(ai_settings, screen, aliens, ship):
"""创建外星人群"""
#计算一行可以容纳多少外星人
alien = Alien(ai_settings, screen)
number_aliens_x = get_number_aliens_x(ai_settings, alien.rect.width)
number_rows = get_number_rows(ai_settings, ship.rect.height, alien.rect.height)
#创建外星人群
for row_number in range(number_rows):
for alien_number in range(number_aliens_x):
create_alien(ai_settings, screen, alien_number, aliens, row_number)
alien_invasion.py
:
# 创建外星人
aliens = Group()
gf.create_fleet(ai_setting, screen, aliens, ship) #应该在循环外面创建外星人群
在修改代码时需要注意函数实参的使用(究竟是什么)
3让外星人群移动
下面让外星人在屏幕上向右移动,撞到屏幕后下移一定的距离,再沿相反方向移动。
3.1向右移动外星人
settings.py
设置外星人速度
# 外星人速度设置
self.alien_speed_factor = 0.5
在game_function.py
中添加更新外星人函数:
def update_aliens(aliens):
"""更新外星人群所有的外星人位置"""
aliens.update()
最后在alien_invasion.py
调用此函数:
while True:
#监视键盘和鼠标事件
gf.check_events(ai_setting, screen, ship, bullets)
ship.update()
gf.update_bullets(bullets)
gf.update_aliens(aliens)
#aliens.update()
#更新屏幕上的图像,并切换到新屏幕
gf.update_screen(ai_setting, screen, ship, bullets, aliens)
3.2创建表示外星人移动方向的设置
settings.py
:
# 外星人速度设置
self.alien_speed_factor = 0.5
self.fleet_drop_speed = 10 #外星人撞到屏幕边缘时下降速度
#fleet_direction为1表示向右移动,为-1表示向左移动
self.fleet_direction = 1
3.3检查外星人是否撞到了屏幕边缘
alien.py
:
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
3.4向下移动外星人群并改变移动方向
当有外星人到达屏幕边缘时,需要将正群外星人下一,并改变他们的移动方向。
game_functipn.py
:
def check_fleet_edges(aliens, ai_settings, screen):
"""有外星人到达边缘时采取相应行动"""
for alien in aliens.sprites():
if alien.check_edges(): #调用函数需要加小括号
change_fleet_direction(aliens, ai_settings, screen) # 调整外星人下移并改变方向
break
def change_fleet_direction(aliens, ai_settings, screen):
#调增外星人下移,并改变他们的方向
screen_rect = screen.get_rect()
for alien in aliens.sprites():
#if alien.rect.bottom <= screen_rect.bottom: 去掉此if,外星人下落到底端会继续下落
alien.rect.y += ai_settings.fleet_drop_speed
ai_settings.fleet_direction *= -1
def update_aliens(aliens, ai_settings, screen):
"""检查外星人的位置,更新外星人群所有的外星人位置"""
check_fleet_edges(aliens, ai_settings, screen)
aliens.update()
4射杀外星人
这里希望子弹击中外星人时,能够击落外星人,使用sprite.groupcollide()
检测两个编组成员之间的碰撞。
4.1检测子弹与外星人的碰撞
首先进行检测碰撞,game_function.py
:
def update_bullets(bullets, aliens):
bullets.update()
# 更新子弹的位置,并删除已经消失的子弹
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
#检查是否有子弹击中外星人
#如果是这样,就删除相应的子弹和外星人
collisions = pygame.sprite.groupcollide(bullets, aliens, True, True) #1
在1处使用方法sprite.groupcollide()
将每颗子弹的rect同每个外星人的rect进行比较,并返回一个字典,其中包含发生了碰撞的子弹和外星人。在这个字典中,每个键都是一颗子弹,而相应的值都是被击中的外星人。
sprite.groupcollide()
先遍历编组bullets中的每颗子弹,再遍历编组aliens中的每个外星人。每当有子弹和外星人的rect重叠时,groupcollide()就在他返回的字典中添加一个键-值对。两个实参True告诉Pygame删除发生碰撞的子弹和外星人(要模拟能够穿行到屏幕顶端的高能子弹——消灭它击中的每一个外星人,可将第一个布尔实参设置为False,第二个布尔实参设置为True,这样被击中的外星人将会消失,二所有的子弹始终有效,知道地道屏幕顶端后消失。)
修改alien_invasion.py
中调用实参:
gf.update_bullets(bullets, aliens)
现在运行游戏,被击中的外星人会消失。
4.2为测试创建大子弹
在测试代码能否正确处理外星人编组为空的情形时,需要花很长时间将屏幕上的外星人击落。
因此可以增大子弹的尺寸,并使其在击中外星人后依然有效。
类似这样的代码修改可以提高测试效率,还能激发出如何赋予玩家更大威力的思想火花。
self.bullet_width = 300
4.3生成新的外星人群
本游戏一个重要特点为外星人无穷无尽,一个外星人群被消灭后,又会出现一群外星人。
因此需要首先检查编组aliens是否为空。如果为空,就调用create_fleet()。将在update_bullets()中执行这种检查,因为外星人都是在这里被消灭的。
game_function.py
:
def update_bullets(ai_settings, screen, ship, bullets, aliens):
bullets.update()
# 更新子弹的位置,并删除已经消失的子弹
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
#检查是否有子弹击中外星人
#如果是这样,就删除相应的子弹和外星人
collisions = pygame.sprite.groupcollide(bullets, aliens, False, True)
if len(aliens) == 0:
# 删除现有子弹并新建一群外星人
bullets.empty()
create_fleet(ai_settings, screen, aliens, ship)
修改alien_invasion.py
中调用实参:
gf.update_bullets(ai_setting, screen, ship, bullets, aliens)
4.4重构update_bullets()
重构update_bullets()使其不再完成那么多任务。
将处理子弹和外星人碰撞的代码移到独立的函数中。
game_function.py
:
def update_bullets(ai_settings, screen, ship, bullets, aliens):
bullets.update()
# 更新子弹的位置,并删除已经消失的子弹
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
# 检查是否有子弹击中外星人并处理
check_bullet_alien_collisions(bullets, aliens, ai_settings, screen, ship)
def check_bullet_alien_collisions(bullets, aliens, ai_settings, screen, ship):
# 检查是否有子弹击中外星人
# 如果是这样,就删除相应的子弹和外星人
collisions = pygame.sprite.groupcollide(bullets, aliens, False, True)
if len(aliens) == 0:
# 删除现有子弹并新建一群外星人
bullets.empty()
create_fleet(ai_settings, screen, aliens, ship)
创建了一个新函数check_bullet_alien_collisions
以检测子弹和外星人之间的碰撞以及整群外星人都被消灭时采取相应的措施。
5结束游戏
游戏结束条件:
①外星人撞到飞船飞船将摧毁
②足够短时间没有消灭外星人飞船将摧毁
③外星人达到屏幕底端飞船将摧毁
④玩家使用完了所有飞船,游戏结束。
5.1检测外星人和飞船碰撞
game_function.py
:
def update_aliens(aliens, ai_settings, screen,ship):
"""检查外星人的位置,更新外星人群所有的外星人位置"""
check_fleet_edges(aliens, ai_settings, screen)
aliens.update()
#检测外星人和飞船之间的碰撞
if pygame.sprite.spritecollideany(ship, aliens):
print("Ship hit!!!")
方法sprite.spritecollideany
接受两个实参,一个精灵和一个编组。它检查编组是否有成员和精灵发生了碰撞,并在找到与精灵发生额了碰撞的成员后停止遍历编组。在这里它遍历编组aliens,并返回它找到的第一个与飞船发生了碰撞的外星人。
如果没有发生碰撞,spritecollideany()
将分会None,if里面的代码块不会执行,如果找到了飞船发生碰撞的外星人,则返回这个外星人。
碰撞后需要删除余下的外星人和子弹,让飞船重新居中,创建一群新的外星人。
5.2响应外星人和飞船碰撞
第一步:ship被撞时不销毁并创建新的ship,而是最总游戏的统计信息来记录飞船被撞了多少次,创建一个统计类
game_stats.py
:
class GameStats():
"""跟踪游戏的统计信息"""
def __init__(self, ai_settings):
"""初始化统计信息"""
self.ai_settings = ai_settings
self.reset_stats()
def reset_stats(self):
"""初始化在游戏运行期间可能变化的统计信息"""
self.ship_left = self.ai_settings.ship_limit
第二步:设置飞船的个数
settings.py
:\
#飞船设置
self.ship_speed_factor = 1.5 #设置飞船速度为1.5
self.ship_limit = 3 # 飞船的个数限制为3
第三步:在alien_invasion.py
中创建GameStats实例,并更新调用update_aliens()中的实参:
# 创建一个用于存储游戏统计信息的实例
stats = GameStats(ai_setting)
#开始游戏的主循环
while True:
#监视键盘和鼠标事件
gf.check_events(ai_setting, screen, ship, bullets)
ship.update()
gf.update_bullets(ai_setting, screen, ship, bullets, aliens)
gf.update_aliens(aliens, ai_setting, screen, ship, stats, bullets)
#aliens.update()
#更新屏幕上的图像,并切换到新屏幕
gf.update_screen(ai_setting, screen, ship, bullets, aliens)
第四步:响应外星人撞到飞船game_function
:
def update_aliens(aliens, ai_settings, screen, ship, stats, bullets):
"""检查外星人的位置,更新外星人群所有的外星人位置"""
check_fleet_edges(aliens, ai_settings, screen)
aliens.update()
#检测外星人和飞船之间的碰撞
if pygame.sprite.spritecollideany(ship, aliens):
ship_hit(stats, aliens, bullets, ai_settings, screen, ship)
def ship_hit(stats, aliens, bullets, ai_settings, screen, ship):
"""响应被外星人撞到的飞船"""
# 将ship_left-1
stats.ship_left -= 1
# 清空子弹和外星人列表
aliens.empty()
bullets.empty()
# 创建一群新的外星人,并将飞船放到屏幕底端中央
create_fleet(ai_settings, screen, aliens, ship)
ship.center_ship()
# 暂停
sleep(0.5)
其中,center_ship()
方法为ship.py
中的新方法:
def center_ship(self):
"""让飞船在屏幕上居中"""
self.center = self.screen_rect.centerx
5.3有外星人达到屏幕底端
game_function.py
def update_aliens(aliens, ai_settings, screen, ship, stats, bullets):
"""检查外星人的位置,更新外星人群所有的外星人位置"""
check_fleet_edges(aliens, ai_settings, screen)
aliens.update()
#检测外星人和飞船之间的碰撞
if pygame.sprite.spritecollideany(ship, aliens):
ship_hit(stats, aliens, bullets, ai_settings, screen, ship)
check_aliens_bottom(screen, aliens, stats, bullets, ai_settings, ship)
def check_aliens_bottom(screen, aliens, stats, bullets, ai_settings, ship):
"""检查是否有外星人到达屏幕底端"""
screen_rect = screen.get_rect()
for alien in aliens.sprites():
if alien.rect.bottom <= screen_rect.bottom:
ship_hit(stats, aliens, bullets, ai_settings, screen, ship)
5.4游戏结束
刚启动游戏处于活跃状态:
game_stats
:
def __init__(self, ai_settings):
"""初始化统计信息"""
self.ai_settings = ai_settings
self.reset_stats()
self.game_active = True #游戏刚启动处于活跃状态
当飞船数为0时,使得游戏非活跃状态:
game_function.py
:
def ship_hit(stats, aliens, bullets, ai_settings, screen, ship):
"""响应被外星人撞到的飞船"""
if stats.ship_left > 0:
# 将ship_left-1
stats.ship_left -= 1
# 清空子弹和外星人列表
aliens.empty()
bullets.empty()
# 创建一群新的外星人,并将飞船放到屏幕底端中央
create_fleet(ai_settings, screen, aliens, ship)
ship.center_ship()
# 暂停
sleep(0.5)
else:
stats.game_active = False
6确定应运行游戏的哪些部分
最后确认游戏哪些部分在任何情况都运行,哪些部分仅在游戏处于活动状态才运行:
while True:
#监视键盘和鼠标事件
gf.check_events(ai_setting, screen, ship, bullets)
if stats.game_active:
ship.update()
gf.update_bullets(ai_setting, screen, ship, bullets, aliens)
gf.update_aliens(aliens, ai_setting, screen, ship, stats, bullets)
#aliens.update()
#更新屏幕上的图像,并切换到新屏幕
gf.update_screen(ai_setting, screen, ship, bullets, aliens)