在本文中,我们将在游戏《外星人入侵》中添加外星人。首先,我们在屏幕上边缘附近添加一个外星人,然后生成一群外星人。我们让这群外星人向两边和下面移动,并删除被子弹击中的外星人。最后,我们将显示玩家拥有的飞船数量,并在玩家的飞船用完后结束游戏。
开发较大的项目时,进入每个开发阶段前回顾一下开发计划,搞清楚接下来要通过编写代码来完成哪些任务都是不错的主意。
主文件alien_invasion.py创建一系列整个游戏都要用到的对象:
1、ai_settings:设置游戏相关的内容(游戏界面尺寸、飞船速度、子弹尺寸及速度等等)
2、screen:显示游戏相关的内容(游戏界面、飞船、子弹等)
3、ship:实例化了一个飞船
4、bullets:实例化了一个子弹的编组
5、while循环:提供了一个游戏运行的主循环,用于动态更新游戏状态
文件settings.py包含 Settings 类,这个类只包含方法 init() ,它初始化控制游戏外观和飞船速度的属性。
文件game_functions.py包含一系列函数:
1、check_keydown_events:监听键盘某个键按下时需要提供的操作
2、check_keyup_events:监听键盘某个按下的键松开后需要提供的操作
3、check_events:监听全部键盘和鼠标的行为操作
4、update_screen:实时重绘游戏界面
5、update_bullets:对子弹数量进行控制
6、fire_bullet:根据实际情况,创建并发射子弹
文件ship.py包含 Ship 类,这个类包含方法 init() 、管理飞船位置的方法 update() 以及在屏幕上绘制飞船的方法 blitme() 。
文件bullet.py包含 Bullet 类,这个类包含方法 init() 、管理子弹位置的方法 update() 以及在屏幕上绘制飞船的方法 draw_bullet() 。
创建一个外星人的方式与创建一个飞船的方式基本是一样的,我们需要先找到一个外星人的图片,图片最好是无背景或者背景颜色与游戏界面颜色一致的,这样可以大大提升游戏的美观和体验,把它放入到images文件夹中,如下图就是一个背景颜色与游戏界面一致的图片:
import pygame
from pygame.sprite import Sprite
class Alien(Sprite):
"""表示单个外星人的类"""
def __init__(self, ai_settings, screen):
super(Alien, self).__init__()
self.screen = screen
self.ai_settings = ai_settings
# 加载外星人图像,并设置其rect属性
self.image = pygame.image.load('images/alien.png')
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)
解释:Alien类与Ship类基本设置都是一样的,只是位置设置不同而已,需要注意的是Alien类继承了Sprite(精灵,不是雪碧哦),和子弹一样,外星人也是多个不是一个,而Ship 只有一个。
1、在主循环外实例化Alien:
#创建一个外星人
alien = Alien(ai_settings, screen)
2、在主循环内显示外星人:
#每次循环重绘屏幕
gf.update_screen(ai_settings, screen, ship, alien, bullets)
为让外星人出现在屏幕上,我们在 game_functions.py的update_screen() 中调用其方法 blitme() :
def update_screen(ai_settings, screen, ship, alien, bullets):
"""更新屏幕上的图像,并切换到新屏幕"""
# 每次循环时都重绘屏幕
screen.fill(ai_settings.bg_color)
# 在飞船和外星人后面重绘所有子弹
for bullet in bullets.sprites():
bullet.draw_bullet()
ship.blitme()
alien.blitme()
# 让最近绘制的屏幕可见
pygame.display.flip()
要绘制一群外星人,需要确定一行能容纳多少个外星人以及要绘制多少行外星人。我们将首先计算外星人之间的水平间距,并创建一行外星人,再确定可用的垂直空间,并创建整群外星人。
容纳外星人宽度 = 游戏界面宽度 - 2倍的外星人宽度(两边游戏界面的空出来的宽度)
available_space_x = ai_settings.screen_width – (2 * alien_width)
容纳外星人的个数 = 容纳外星人的宽度 / 2倍的外星人宽度(一个宽度用于放置外星人,另一个宽度为外星人右边的空白区域)
number_aliens_x = available_space_x / (2 * alien_width)
为创建一行外星人,首先在alien_invasion.py中创建一个名为 aliens 的空编组(类似于创建子弹的编组),用于存储全部外星人,再调用game_functions.py中创建外星人群的函数:
alien_invasion.py的run_game()函数中,主循环while之外添加:
#创建一个外星人编组
aliens = Group()
#创建一个外星人
# alien = Alien(ai_settings, screen) #替换为创建外星人群
# 创建外星人群
gf.create_fleet(ai_settings, screen, aliens)
game_functions.py中用于更新游戏图像的update_screen()函数:
def update_screen(ai_settings, screen, ship, aliens, bullets):
"""更新屏幕上的图像,并切换到新屏幕"""
# 每次循环时都重绘屏幕
screen.fill(ai_settings.bg_color)
# 在飞船和外星人后面重绘所有子弹
for bullet in bullets.sprites():
bullet.draw_bullet()
ship.blitme()
aliens.draw(screen)
# 让最近绘制的屏幕可见
pygame.display.flip()
主程序中调用了game_functions.py中的创建外星人群的新函数 create_fleet(),但是我们还未定义此,函数,现在来定义此函数:
game_functions.py中添加新函数 create_fleet():
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)
解释:
1、首先,别忘记把Alien类导入到game_functions.py中,要不然你再怎么调用alien的属性都会报错。
2、计算游戏界面每一行能放置几个外星人,由于外星人的图片素材是外部提供的,Alien类中并没有它的长宽属性,所以必须实例化一个外星人,通过rect对其确定大小,然后再计算个数。
3、通过for循环将允许创建的外星人的个数添加到外星人组中,当然,这里要明确外星人组中每个外星人在游戏界面的定位alien.x = alien_width + 2 * alien_width * alien_number(每个外星人都往右
推一个外星人的宽度)。
分析create_fleet()函数,我们发现函数内容结构复杂,而且不便于以后的使用,它在内部处理了一系列的问题而得到了很多结论性的结果,但是假设后面我们要使用它内部的结果,就不能直接调用了,这样非常的不方便,因此,我们要对其进行重构,方便于后面的开发。
添加新函数 get_number_aliens_x() :用于计算每行可容纳多少个外星人
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
添加新函数 create_alien() :用于创建一个外星人,并设置其位置后放入外星人组
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)
最后:重构 create_fleet()
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, aliens, alien_number)
这样做就舒服了,以后要是别的程序还用到每行可容纳多少个外星人,直接来这里调用就可以了
目前,已经完成了添加一行外星人的目的,但是游戏设定需要添加多行外星人。接下来,我们就来计算一下,游戏界面能添加多少行外星人。
容纳外星人的高度 = 游戏界面高度 - 外星人高度(外星人与游戏界面顶部的区域)- 最初外星人群与飞船的距离(外星人高度的两倍)- 飞船的高度
available_space_y = ai_settings.screen_height – 3 * alien_height – ship_height
容纳外星人的行数 = 容纳外星人的高度 / 2倍外星人的高度(每个外星人间距1个外星人高度的距离)
number_rows = available_height_y / (2 * alien_height)
接下来,我们在game_functions.py添加新函数get_number_rows()用于获取一共可以添加多少行外星人:
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
我们修改外星人的y坐标,并在第一行外星人上方留出与外星人等高的空白区域。相邻外星人行的y坐标相差外星人高度的两倍,因此我们将外星人高度乘以2,再乘以行号。第一行的行号为0,因此第一行的垂直位置不变,而其他行都沿屏幕依次向下放置。
修改game_functions.py中的create_alien(),将创建:
def create_alien(ai_settings, screen, aliens, row_number, 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
alien.rect.y = alien.rect.height + 2 * alien.rect.height * row_number
aliens.add(alien)
好了,现在再把game_functions.py中的create_fleet()进行修改,把外星人群添加到游戏界面中去:
def create_fleet(ai_settings, screen, ship, aliens):
"""创建外星人群"""
# 创建一个外星人,并计算一行可容纳多少个外星人
# 外星人间距为外星人宽度
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, aliens, row_number, alien_number)
解释:
1、number_aliens_x:每行最多可以放多少个外星人
2、number_rows:游戏界面一共可以放多少行外星人
3、通过双层for循环实现外星人群体布局
最后,修改alien_invasion.py主程序中“# 创建外星人群”的代码(需要添加一个ship参数,因为计算行数的时候用到了飞船的高度):
gf.create_fleet(ai_settings, screen, ship, aliens)
需求:外星人撞到屏幕边缘后下移一定的距离,再沿相反的方向移动。
首先,在settings.py中的__init__方法中添加一个控制外星人速度的设置,绑定到对象本身:
# 外星人设置
self.alien_speed_factor = 1
然后,使用这个设置来创建 alien.py 中的 update()方法,表示向右移动外星人 :
def update(self):
"""向右移动外星人"""
self.x += self.ai_settings.alien_speed_factor
self.rect.x = self.x
接下来,在game_functions.py末尾添加新函数 update_aliens(),表示更新外星人的位置 :
def update_aliens(aliens):
"""更新外星人群中所有外星人的位置"""
aliens.update()
最后,在主程序的主循环中添加game_functions.py的调用即可:
#调用外星人群移动显示的函数
gf.update_aliens(aliens)
目前,已经实现外星人群整体向右移动,直至全部移出游戏界面,接下来,我们需要实现外星人群移动到游戏界面边界后向下移动,然后再反向移动的功能。
在settings.py中的外星人设置添加向下移动的速度属性和移动方向的判断标准:
# 外星人设置
self.alien_speed_factor = 1
self.fleet_drop_speed = 10 #表示向下移动的速度
# fleet_direction为1表示向右移,为-1表示向左移
self.fleet_direction = 1
现在需要编写一个方法来检查是否有外星人撞到了屏幕边缘,还需修改 alien.py 中的 update() ,以让每个外星人都沿正确的方向移动。
首先,在alien.py中添加一个判断外星人是否撞到了屏幕边缘的函数check_edges():
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
其次,修改修改 alien.py 中的 update() :
def update(self):
"""向左或向右移动外星人"""
self.x += (self.ai_settings.alien_speed_factor * self.ai_settings.fleet_direction)
self.rect.x = self.x
解释:self.x(当前外星人的位置) = self.x + self.ai_settings.alien_speed_factor(移动速度)* self.ai_settings.fleet_directio(移动方向)
有外星人到达屏幕边缘时,需要将整群外星人下移,并改变它们的移动方向。下面我们就对game_functions.py做修改,因为我们要在这里检查是否有外星人到达了左边缘或右边缘,game_functions.py是为游戏提供逻辑处理的函数库。
首先,要对外星人碰撞到屏幕边界实现转向向下的功能,所以在game_functions.py中添加一个change_fleet_direction函数:
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
其次,要对外星人碰撞到屏幕边缘采取相应措施,因此,在game_functions.py中添加一个check_fleet_edges()函数:
def check_fleet_edges(ai_settings, aliens):
"""有外星人到达边缘时采取相应的措施"""
for alien in aliens.sprites():
if alien.check_edges():
change_fleet_direction(ai_settings, aliens)
break
最后,就是要修改game_functions.py中用于更新外星人群位置的update_aliens()函数,使外星人群能正确的显示自己在屏幕的位置:
def update_aliens(ai_settings, aliens):
"""检查是否有外星人位于屏幕边缘,并更新整群外星人的位置"""
check_fleet_edges(ai_settings, aliens)
aliens.update()
我们修改了函数 update_aliens() ,在其中通过调用 check_fleet_edges() 来确定是否有外星人位于屏幕边缘。现在,函数 update_aliens() 包含形参 ai_settings ,因此我们在主程序alien_invasion.py调用它时也要指定与ai_settings 对应的实参:
#调用外星人群的函数
# gf.update_aliens(aliens)
gf.update_aliens(ai_settings, aliens)