Python入门项目——飞机大战(二)

简介: Python入门项目——飞机大战

重构 check_events()

随着开发工作的进行,函数 check_events() 将越来越长,所以可以考虑将其部分代码放在两个函数中:


  • 一个处理 KEYDOWN 事件;
  • 一个处理 KEYUP 事件;

如下:


def check_events(settings, 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.moving_right = True
                #ship.rect.centerx += 50
                #if ship.rect.centerx > settings.screen_width:
                #    ship.rect.centerx -= 50
            if event.key == pygame.K_LEFT:
                ship.moving_left = True
                #ship.rect.centerx -= 50
                #if ship.rect.centerx < 0:
                    #ship.rect.centerx += 50
        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


修改后,将其中按键按下处理和按键松开处理封装成对应函数,如下:


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)


射击

下面就开始添加射击功能。将编写玩家按下空格键时发射子弹(小矩形)的响应处理,子弹将在屏幕中向上穿行,抵达屏幕上边缘后消失。


添加子弹设置

首先,更新 settings.py ,在其方法 __init__() 末尾存储新类 Bullet 所需的值:


class Settings():
    '''存储《外星人大战》所有设置的类'''
    def __init__(self):
        # 初始化游戏的设置
        self.screen_width = 1200
        self.screen_height = 800
        self.bg_color = (230, 0, 0)
        # 飞船的设置
        self.ship_speed_factor = 1.5
        # 子弹设置
        self.bullet_speed_factor = 1
        self.bullet_width = 3
        self.bullet_height = 15
        self.bullet_color = 60, 60, 60


创建 Bullet 类

下面创建 bullet 模块,编写子弹运动的 Bullet 类,具体如下:


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_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)


  • Bullet 类继承了从模块 pygame.sprite 模块中导入的 Sprite 类,通过使用精灵,可以将游戏中相关的元素编组,进而同时操作编组中的所有元素。为创建子弹实例,需要想 __init__() 传递 ai_settings 、 screen 、 ship 实例。
  • 方法 update() 管理子弹的位置,放射子弹后,子弹在屏幕中向上移动,这意味着 y 的值不断减小,因此更新子弹的位置就可以看到子弹在移动。
  • 方法 draw_bullet() 绘制子弹,使用存储在 self.color 中的颜色填充子弹的 rect 占据屏幕部分。

将子弹存储到编组

定义 Bullet 类后,就可以编写代码。首先,将 alien_invasion.py 中创建一个编组(group),用于存储所有高效的子弹,以便能够管理发射出去的所有子弹。


Group 类其实类似于一个列表,方便存储精灵。具体修改如下(alien_invasion.py):


import sys
import pygame
from settings import Settings
from ship import Ship
import game_func as gf
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_events(ai_settings, screen, ship, bullets)
        ship.update()
        bullets.update()
        # 调用 update_screen() 更新屏幕
        gf.update_screen(ai_settings, screen, ship, bullets)
run_game()


开火

在 game_func.py 中,需要修改 check_keydown_events() 代码,添加对空格键的响应操作,并且还需要修改 update_screen() ,以确保在调用 flip() 前在屏幕上已经完成了子弹重绘。具体修改如下:


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 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()


20200703001604368.gif

执行效果如下图所示:

20200703001604368.gif

删除已消失的子弹

上面的操作完成后,子弹到达顶端后看上去是消失了,实际上是因为超出边界没有显示,其实子弹依然存在,只不过它们的 y 坐标值为负数,这样积攒过多对程序消耗和处理都是负担。所以需要将这些已经消失的子弹进行删除,其实就是检测子弹坐标条件,在超出上边界后将其删除即可。具体代码如下( alien_invasion.py ):


import sys
import pygame
from settings import Settings
from ship import Ship
import game_func as gf
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_events(ai_settings, screen, ship, bullets)
        ship.update()
        bullets.update()
        # 删除已消失的子弹
        for bullet in bullets.copy():
            if bullet.rect.bottom <= 0:
                bullets.remove(bullet)
                print(len(bullets))
        # 调用 update_screen() 更新屏幕
        gf.update_screen(ai_settings, screen, ship, bullets)
run_game()


20200703073228280.png

执行结果如下,从图中可以看出在子弹到达上边界后,列表中的数量就减少了。

20200703073228280.png

限制子弹的数量

很多射击游戏都对可同时出现在屏幕上的子弹数量进行了限制,以鼓励玩家有目标地设计,这里也添加上这部分的限制。


首先,在 settings.py 中存储子弹最大限制数量参数,如下:


class Settings():
    '''存储《外星人大战》所有设置的类'''
    def __init__(self):
        # 初始化游戏的设置
        self.screen_width = 1200
        self.screen_height = 800
        self.bg_color = (230, 0, 0)
        # 飞船的设置
        self.ship_speed_factor = 1.5
        # 子弹设置
        self.bullet_speed_factor = 1
        self.bullet_width = 3
        self.bullet_height = 15
        self.bullet_color = 60, 60, 60
        self.bullets_allowed = 5


这里将同时存在的子弹数量设置为 5 ,如何在 game_func.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.bullets_allowed:
            new_bullet = Bullet(ai_settings, screen, ship)
            bullets.add(new_bullet)


创建函数 update_bullets()

编写并检查子弹管理代码后,可以将这部分代码封装到 game_func模块中,使得主程序尽可能简单,这里创建一个名为 update_bullets() 的函数,添加到 game_func模块中,具体如下:


def update_bullets(bullets)
    # 更新子弹的位置
    bullets.update()
    # 删除已消失的子弹
    for bullet in bullets.copy():
        if bullet.rect.bottom <= 0:
            bullets.remove(bullet)


如何修改 alien_invasion.py 模块中的 while 循环,如下:


import sys
import pygame
from settings import Settings
from ship import Ship
import game_func as gf
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_events(ai_settings, screen, ship, bullets)
        ship.update()
        # 调用 update_bullets() 更新子弹
        gf.update_bullets(bullets)
        # 调用 update_screen() 更新屏幕
        gf.update_screen(ai_settings, screen, ship, bullets)
run_game()


创建函数 fire_bullet()

下面将发射子弹的代码转移到单独的函数中,这样在 check_keydown_events() 中只需要使用一行函数调用即可完成发射子弹操作。


具体如下( game_func.py ):


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 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(ai_settings, screen, ship, bullets)


添加程序退出快捷键响应

在 game_func.py 模块中添加对 Q 按键的退出响应,具体如下:


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(ai_settings, screen, ship, bullets)
    elif event.key == pygame.K_q:
        # 退出程序
        sys.exit()


创建第一个外星人

在屏幕上放置敌方外星人和放置飞船类似,每个外星人的行为都由 Alien 类控制,我们将像创建 Ship 类那样创建这个类,简化考虑,使用位图表示外星人,下载外星人的图片文件,下载链接:外星人图片下载


将下载好的图片放置到 images 目录下,命名为 alien.png 如下图所示:

20200703200243303.png

创建 alien.py 模块,具体代码如下:


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
    def blitme(self):
        # 在指定位置绘制外星人
        self.screen.blit(self.image, self.rect)


创建 Alien 实例

下面在 alien_invasion.py 中创建一个 Alien 实例:


import sys
import pygame
from settings import Settings
from ship import Ship
from alien import Alien
import game_func as gf
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()
    # 创建一个外星人
    alien = Alien(ai_settings, screen)
    # 开始游戏的主循环
    while True:
        # 监视键盘和鼠标事件
        gf.check_events(ai_settings, screen, ship, bullets)
        ship.update()
        # 调用 update_bullets() 更新子弹
        gf.update_bullets(bullets)
        # 调用 update_screen() 更新屏幕
        gf.update_screen(ai_settings, screen, ship, bullets)
run_game()


并且修改其中调用函数 updaget_screen(),如下:


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()


运行之后,如下图:

20200703213103149.png

创建一群外星人

要绘制一群外星人,需要确定一行能容纳多少个外星人以及要绘制多少行外星人。所以需要先计算外星人之间的水平间距,并创建一行外星人,再确定可以的垂直空间,并创建整群外星人。


确定一行可容纳多少个外星人

为了确定一行可容纳多少个外星人,了解可用的水平空间大小。屏幕宽度存储在 ai_settings.screen_width 中,但是两边还是需要留下一定的空袭,因此可以用放置外星人的水平控件定为屏幕宽度减去外星人宽度的两倍。


# 可用空间宽度
available_space_x = ai_settings.screen_width - (2 * alien_width)
# 单行可容纳外星人个数
number_aliens_x = available_space_x / (2 * alien_width)


创建多行外星人

为创建一行外星人,首先在 alien_ivasion.py 中创建一个名为 aliens 的空编组,用于存储全部外星人。再调用 game_func.py 模块中创建外星人群的函数。


game_func.py 模块中创建外星人群函数如下:



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 = 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)
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()


修改 alien_invasion.py 中的调用,具体如下:


import sys
import pygame
from settings import Settings
from ship import Ship
from alien import Alien
import game_func as gf
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()
    # 创建外星人群
    aliens = Group()
    gf.create_fleet(ai_settings, screen, aliens)
    # 开始游戏的主循环
    while True:
        # 监视键盘和鼠标事件
        gf.check_events(ai_settings, screen, ship, bullets)
        ship.update()
        # 调用 update_bullets() 更新子弹
        gf.update_bullets(bullets)
        # 调用 update_screen() 更新屏幕
        gf.update_screen(ai_settings, screen, ship, aliens, bullets)
run_game()


执行如下图所示:

20200703221600719.png

重构 create_fleet()

这时候能看出外星人都在屏幕上靠左边,这样就可以向右移动,触及屏幕边缘后下移再向左移动,依次类推。


下面是 create_fleet() 和新创建的两个函数 aliens_x() 、 create_alien() :


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
    alien.add(alien)
def create_fleet(ai_settings, screen, aliens):
    # 创建外星人群
    # 创建一个外星人,并计算一行可容纳多少个外星人
    # 外星人间距为外星人宽度
    alien = Alien(ai_settings, screen)
    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)


添加行

要创建外星人群,首先就需要计算屏幕可容纳多少行,并对创建一行外星人的循环重复相应的次数。为计算可容纳的行数,需要计算当前屏幕可用垂直空间:


将屏幕高度减去第一行外星人的高度,飞船的高度以及最初外星人群与飞船的距离(外星人高度的两倍)

available_space_y = ai_settings.screen_height - 3 * alien_height - ship_height


这样计算将会在飞船上方留出一定的空白区域,给玩家留出射杀外星人的时间。所以行数计算如下:


number_rows = available_height_y / (2 * alien_height)


知道可容纳多少行后,便可以重复执行创建一行外星人的代码( game_func.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, 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 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, alien_number, row_number)


执行后可以看到添加了一群外星人,如下图所示:

20200704230836677.png

让外星人移动

接下来就是让外星人群在屏幕上向右移动,在接触到屏幕边缘后下移一定距离,再向相反方向移动,不断移动所有的外星人,知道都被消灭;有外星人撞到飞船,或者有外星人抵达屏幕最低端则游戏结束。


向右移动外星人

为移动外星人,可以调用 alien.py 中的 update() 方法,需要先在 settings.py 模块中添加外星人移动速度参数,如下:


class Settings():
    '''存储《外星人大战》所有设置的类'''
    def __init__(self):
        # 初始化游戏的设置
        self.screen_width = 1200
        self.screen_height = 800
        self.bg_color = (230, 0, 0)
        # 飞船的设置
        self.ship_speed_factor = 1.5
        # 子弹设置
        self.bullet_speed_factor = 1
        self.bullet_width = 3
        self.bullet_height = 15
        self.bullet_color = 60, 60, 60
        self.bullets_allowed = 5
        # 外星人设置
        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_func.py 模块中添加 update_aliens() 方法用于更新外星人组的位置,如下:



def update_aliens(aliens):
    # 更新外星人群中所有的外星人的位置
    # 对编组调用方法update(),会自动对每个组内成员调用update()
    aliens.update()


在主循环中添加使用更新外星人位置的方法,具体如下:


import sys
import pygame
from settings import Settings
from ship import Ship
from alien import Alien
import game_func as gf
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()
    # 创建外星人群
    aliens = Group()
    gf.create_fleet(ai_settings, screen, ship, aliens)
    # 开始游戏的主循环
    while True:
        # 监视键盘和鼠标事件
        gf.check_events(ai_settings, screen, ship, bullets)
        ship.update()
        # 调用 update_bullets() 更新子弹
        gf.update_bullets(bullets)
        # 调用 update_aliens() 更新外星人位置
        gf.update_aliens(aliens)
        # 调用 update_screen() 更新屏幕
        gf.update_screen(ai_settings, screen, ship, aliens, bullets)
run_game()


此时还没有添加移动到右端限制,所以外星人会始终右移,即使离开屏幕后还是会不断增加 x 的大小。


创建表示外星人移动方向的设置

下面创建使外星人在接触右边缘后向下移动、再向左移动的设置。在 settings.py 模块中添加参数:


class Settings():
    '''存储《外星人大战》所有设置的类'''
    def __init__(self):
        # 初始化游戏的设置
        self.screen_width = 1200
        self.screen_height = 800
        self.bg_color = (230, 0, 0)
        # 飞船的设置
        self.ship_speed_factor = 1.5
        # 子弹设置
        self.bullet_speed_factor = 1
        self.bullet_width = 3
        self.bullet_height = 15
        self.bullet_color = 60, 60, 60
        self.bullets_allowed = 5
        # 外星人设置
        self.alien_speed_factor = 1
        # 遇到边沿后向下移动的速度
        self.fleet_drop_speed = 10
        # fleet_direction :1-表示向右移动,-1 表示向左移动
        self.fleet_direction = 1


添加外星人是否撞到边缘的判断

现在向 alien.py 中添加检测边缘的代码,如下:


def update(self):
        # 向右移动外星人
        self.x += (self.ai_settings.alien_speed_factor * self.ai_settings.fleet_direction)
        self.rect.x = self.x
    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
        else
            return False


实现向下移动并改变方向

有外星人到达屏幕边沿时,需要将其下移并改变移动方向。为此,可以向 game_func() 模块中添加判断边缘函数 check_fleet_edges() 、 change_fleet_direction() 。具体如下:


def check_fleet_edges(ai_settings, aliens):
    # 有外星人到达边缘时采取相应措施
    for alien in aliens:
        if alien.check_edges():
            change_fleet_direction(ai_settings, aliens)
            break
def change_fleet_direction(ai_settings, aliens):
    # 整群外星人下移,并改变移动方向
    for alien in aliens:
        alien.rect.y += ai_settings.fleet_drop_speed
    ai_settings.fleet_direction *= -1
def update_aliens(ai_settings, aliens):
    # 更新外星人群中所有的外星人的位置
    check_fleet_edges(ai_settings, aliens)
    aliens.update()


修改 ai_invasion.py 中的相关调用,之前调用 gf.update_aliens() 的传递参数部分,gf.update_aliens(ai_settings, aliens) 。修改完成后执行效果如下:

20200706073356721.gif

射杀外星人

在创建完成飞船及操作响应和外星人移动后,就需要添加当子弹击中外星人后的响应操作。在游戏编程中,子弹碰撞到外星人其实就是两个游戏元素产生了重叠,可以采用 sprite.groupcollide() 方法来检测两个编组成员之间的碰撞。


检测子弹与外星人的碰撞

首先添加坚持子弹与外星人直接的碰撞,如果发生碰撞立刻使被碰撞的外星人消失,为此,可以在更新子弹位置后立刻检测碰撞并做出响应。


方法 sprite.groupcollide() 将每颗子弹的 rect 同每个外星人的 rect 进行比较,并返回一个字典,其中包含了碰撞的子弹和外星人。在这个字典中,每个键都是一颗子弹,而对应的值就是发生碰撞的外星人。


具体在 game_func.py 模块中的 update_bullets() 中添加如下代码:


def update_bullets(aliens, bullets):
    # 更新子弹的位置
    bullets.update()
    # 删除已消失的子弹
    for bullet in bullets.copy():
        if bullet.rect.bottom <= 0:
            bullets.remove(bullet)
    # 检查是否有子弹与外星人发送碰撞
    #如果发生碰撞,删除发送碰撞的子弹和外星人
    collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)


其中,collisions = pygame.sprite.groupcollide(bullets, aliens, True, True) ,groupcollide 会返回字典,后面的两个实参 True 表示当检测到发生碰撞时会自动移除组内发送碰撞的元素。修改相应的参数调用并执行,如下:

20200706074811249.gif

为测试方便添加大招子弹

只需要通过运行这个游戏就可以测试其中的很多功能,但是有些时候需要花很长的时间才能击落外星人进行测试,这很浪费时间,为了方便起见,可以将子弹的宽度设置很大,这样就能一次操作后将所有的外星人都击落了。


在 settings.py 模块中将子弹宽度设置与屏幕同宽,如下:


 

# 子弹设置
        self.bullet_speed_factor = 1
        self.bullet_width = 1200
        self.bullet_height = 15
        self.bullet_color = 60, 60, 60
        self.bullets_allowed = 5


在 game_func.py 中的碰撞检测的调用修改为 collisions = pygame.sprite.groupcollide(bullets, aliens, False, True) ,这样子弹在击落外星人后会继续存在,效果如下:

20200707070450308.gif

生成新的外星人群

当一个外星人群被消灭之后,又会出现新的一群外星人。所以需要在外星人群被消灭后再次添加一群外星人,这就需要检查编组 aliens 是否为空,如果为空则再次调用 create_fleet() ,这一步操作添加到 update_bullets() 中进行,因为外星人是在这里被消灭的,如下:


def update_bullets(ai_settings, screen, ship, aliens, bullets):
    # 更新子弹的位置
    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, ship, aliens)


20200707073801503.gif

改动后,效果如下:

20200707073801503.gif

提高子弹速度

当外星人数量多了之后,可能会发现子弹速度比之前更慢了,这是因为在每次循环后,pygame 需要做更多工作了,可以修改 settings.py 模块中的子弹速度参数来提高子弹的速度。

   

# 子弹设置
        self.bullet_speed_factor = 1
        self.bullet_width = 1200
        self.bullet_height = 15
        self.bullet_color = 60, 60, 60
        self.bullets_allowed = 5


 

     


相关文章
|
5天前
|
Linux 数据库管理 Python
CentOS7编译安装Python3.10(含OpenSSL1.1.1安装),创建虚拟环境,运行Django项目(含sqlite版本报错)
CentOS7编译安装Python3.10(含OpenSSL1.1.1安装),创建虚拟环境,运行Django项目(含sqlite版本报错)
59 4
|
3天前
|
分布式计算 大数据 数据处理
Python入门与大数据处理环境配置指南
**Python入门与大数据处理环境配置** Python作为高级编程语言,因其简洁语法和丰富库资源,成为数据处理、AI和大数据分析首选。本文旨在介绍Python基础和环境配置,特别是针对大数据处理的环境搭建。首先,讲解Python语言基础,包括语言概述、基本语法(变量、数据类型、控制流语句、函数和模块)。接着,讨论如何安装Python环境,以及安装NumPy、Pandas等大数据处理库。对于大数据处理,可以选择本地环境或搭建分布式环境,如Hadoop和Spark,并提供相关API示例。最后,列出环境配置中可能遇到的问题及解决方案,如版本不兼容、库安装失败等,并提供参考资料以供深入学习。
16 3
|
3天前
|
Java Serverless 应用服务中间件
Serverless 应用引擎操作报错合集之部署python项目时,构建过程报错,怎么解决
Serverless 应用引擎(SAE)是阿里云提供的Serverless PaaS平台,支持Spring Cloud、Dubbo、HSF等主流微服务框架,简化应用的部署、运维和弹性伸缩。在使用SAE过程中,可能会遇到各种操作报错。以下是一些常见的报错情况及其可能的原因和解决方法。
|
6天前
|
人工智能 IDE 开发工具
【Python学习篇】Python基础入门学习——你好Python(一)
【Python学习篇】Python基础入门学习——你好Python(一)
14 1
|
2天前
|
机器学习/深度学习 算法 数据挖掘
机器学习新手也能飞:Python+Scikit-learn让你轻松入门!
【6月更文挑战第12天】Python和Scikit-learn降低了机器学习的门槛,让初学者也能轻松涉足。Python以其易用性及丰富的库支持成为机器学习首选语言,而Scikit-learn作为开源机器学习库,提供多种算法和工具。通过简单示例展示了如何使用两者处理鸢尾花数据集进行分类,体现其在实践中的高效便捷。掌握这两者,能助你在机器学习领域不断探索和创新。
|
2天前
|
开发者 Python
入门实战丨Python小游戏经典案例
入门实战丨Python小游戏经典案例
10 4
|
5天前
|
机器学习/深度学习 数据可视化 API
Python适合做哪些类型的项目?
【6月更文挑战第9天】Python适合做哪些类型的项目?
9 2
|
6天前
|
存储 Python
【Python学习篇】Python基础入门学习——Python基础语法(二)
【Python学习篇】Python基础入门学习——Python基础语法(二)
18 1
|
6天前
|
数据采集 存储 Web App开发
Python爬虫实战:从入门到精通
Python是开发网络爬虫的首选语言,因其简洁语法和丰富库如requests, BeautifulSoup, Scrapy。爬虫涉及HTTP交互、HTML解析及法律道德问题。以下是爬取豆瓣电影Top250的步骤:确定目标,分析网站,安装必要库(requests, BeautifulSoup),编写代码抓取电影名称、评分和简介,处理异常并优化,如设置请求间隔、使用代理IP和遵循Robots协议。
|
安全 Java Python
sonarqube扫描Python项目代码
sonarqube扫描Python项目代码
sonarqube扫描Python项目代码