编程语言
首页 > 编程语言> > Python项目实践之二:外星人(2)

Python项目实践之二:外星人(2)

作者:互联网

Python项目实践之二:外星人

接着上篇文章继续写。。。

一、射杀外星人

我们创建了飞船和外星人群,但子弹击中外星人时,将穿过外星人,因为我们还没有检查碰撞。在游戏编程中,碰撞指的是游戏元素重叠在一起。要让子弹能够击落外星人,我们将使用sprite.groupcollide() 检测两个编组的成员之间的碰撞。

1、检测子弹与外星人的碰撞

sprite.groupcollide() 函数:检测两个编组成员之间的碰撞,因此,我们只需要检测子弹编组和外星人群编组是否发生碰撞即可。
修改game_functions.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)

解释:
1、此方法是用于实时更新子弹位置,因此把检测子弹和外星人群碰撞的方法放到这里在合适不过了,当然,你也可以把检测碰撞的方法放到实时更新外星人群的方法update_aliens()中去,效果是一样的。
2、sprite.groupcollide() 函数:新增的这行代码遍历编组 bullets 中的每颗子弹,再遍历编组 aliens 中的每个外星人。每当有子弹和外星人的 rect 重叠时, groupcollide() 就在它返回的字典中添加一个键值对。两个实参True 告诉Pygame删除发生碰撞的子弹和外星人。(第一个布尔值设置为false,子弹将不会被删除,直接飞出屏幕)。

我们给update_bullets()方法添加了一个新的参数,所以相应的的主函数alien_invasion.py调用到此方法的地方也要修改一下:

#调用管理子弹的函数
gf.update_bullets(aliens, bullets)

2、生成新的外星人群

这个游戏的一个重要特点是外星人无穷无尽,一个外星人群被消灭后,又会出现一群外星人。

要在外星人群被消灭后又显示一群外星人,首先需要检查编组 aliens 是否为空。如果为空,就调用 create_fleet() 。我们将在 update_bullets() 中执行这种检查,因为外星人都是在这里被消灭的,修改game_functions.py中的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, True, True)
    if len(aliens) == 0:
        # 删除现有的子弹并新建一群外星人
        bullets.empty()
        create_fleet(ai_settings, screen, ship, aliens)

我们给update_bullets()方法添加了新的参数,所以相应的的主函数alien_invasion.py调用到此方法的地方也要修改一下:

		#调用管理子弹的函数
        gf.update_bullets(ai_settings, screen, ship, aliens, bullets)

3、为测试添加创建大子弹

目前,已经能实现外星人被全部击落后又重新创建新的外星人群,为了测试这个功能是否正常,我们需要运行游戏后把所有外星人都击落,然后观察是否会创建新的外星人群,但是这着实也太繁琐了,浪费了很多不必要的时间。

为此,我们就要考虑通过什么方式来测试程序能高效而简洁,测试有些功能时,可以修改游戏的某些设置,以便专注于游戏的特定方面。例如,可以缩小屏幕以减少需要击落的外星人数量,也可以提高子弹的速度,以便能够在单位时间内发射大量子弹。测试这个游戏时,可以做的一项修改是增大子弹的尺寸,使其在击中外星人后依然有效。

4、提高子弹的速度

如果你现在尝试在这个游戏中射杀外星人,可能发现子弹的速度比以前慢,这是因为在每次循 环 中 , Pygame 需 要 做 的 工 作 更 多 了 。 为 提 高 子 弹 的 速 度 , 可 调 整 settings.py 中bullet_speed_factor 的值。
settings.py中关于子弹设置的代码:

# 子弹设置
self.bullet_speed_factor = 3

5、重构 update_bullets()

重构 update_bullets() ,使其不再完成那么多任务。我们将把处理子弹和外星人碰撞的代码移到一个独立的函数中。
在game_functions.py添加新的函数check_bullet_alien_collisions(),用于处理子弹和外星人碰撞检测:

def check_bullet_alien_collisions(ai_settings, screen, ship, aliens, bullets):
    """响应子弹和外星人的碰撞"""
    # 删除发生碰撞的子弹和外星人
    collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)
    if len(aliens) == 0:
        # 删除现有的所有子弹,并创建一个新的外星人群
        bullets.empty()
        create_fleet(ai_settings, screen, ship, aliens)

然后,在game_functions.py中的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)
    check_bullet_alien_collisions(ai_settings, screen, ship, aliens, bullets)

二、结束游戏

接收游戏的设定:如果玩家没能在足够短的时间内将整群外星人都消灭干净,且有外星人撞到了飞船,飞船将被摧毁。与此同时,我们还限制了可供玩家使用的飞船数,而有外星人抵达屏幕底端时,飞船也将被摧毁。玩家用光了飞船后,游戏便结束。

1、检测外星人和飞船碰撞

需要检测外星人和飞船是否发生碰撞,我们需要在更新外星人群或者更新飞船的函数下添加碰撞检测,这里选择在更新外星人群的函数下添加碰撞检测。
在game_functions.py的update_aliens()下添加碰撞检测:

def update_aliens(ai_settings, ship, aliens):
    """检查是否有外星人位于屏幕边缘,并更新整群外星人的位置"""
    check_fleet_edges(ai_settings, aliens)
    aliens.update()
    # 检测外星人和飞船之间的碰撞
    if pygame.sprite.spritecollideany(ship, aliens):
        print("Ship hit!!!")

解释:spritecollideany()函数: 接受两个实参:一个精灵和一个编组。它检查编组是否有成员与精灵发生了碰撞,并在找到与精灵发生了碰撞的成员后就停止遍历编组。在这里,它遍历编组aliens ,并返回它找到的第一个与飞船发生了碰撞的外星人。

我们给update_aliens()方法添加了新的参数,所以相应的的主函数alien_invasion.py调用到此方法的地方也要修改一下:

		#调用外星人群的函数
        gf.update_aliens(ai_settings, ship, aliens)

2、响应外星人和飞船碰撞

现在需要确定外星人与飞船发生碰撞时,该做些什么。我们不销毁 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

解释:
1、在这个游戏运行期间,我们只创建一个 GameStats 实例,但每当玩家开始新游戏时,需要重置一些统计信息。为此,我们在方法 reset_stats() 中初始化大部分统计信息,而不是在 init()中直接初始化它们。我们在 init() 中调用这个方法,这样创建 GameStats 实例时将妥善地设置这些统计信息,同时在玩家开始新游戏时也能调用 reset_stats() 。
2、ship_limit:当前游戏所剩飞船数量。

首先,需要把ship_limit属性添加到settings.py中的飞船设置中:
		# 飞船的设置
        self.ship_speed_factor = 1.5
        self.ship_limit = 3   #初始化飞船数量为3
其次,修改主程序alien_invasion.py,实例化一个GameStats对象,用于实时储存游戏统计信息,然后再调用update_aliens() 时,添加实参 stats 、 screen 和 ship ,以便于通过这些实参来跟踪玩家还有多少艘飞船,以及创建一群新的外星人。

主程序alien_invasion.py中实例化GameStats对象:

	 # 创建一个用于存储游戏统计信息的实例
    stats = GameStats(ai_settings)

主循环while中调度update_aliens() 时,添加实参 stats 、 screen 和 ship :

		#调用外星人群的函数
        gf.update_aliens(ai_settings, stats, screen, ship, aliens, bullets)
接下来,我们需要实现外星人碰撞到飞船后,我们将余下的飞船数减1,创建一群新的外星人,并将飞船重新放置到屏幕底端中央(我们还将让游戏暂停一段时间,让玩家在新外星人群出现前注意到发生了碰撞,并将重新创建外星人群)。

显而易见,这是一个功能,需要对应的函数来完成操作,有关于游戏功能实现的函数我们都放在game_functions.py中,因此我们在其中添加一个新函数 ship_hit()来完成此项功能:

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)

解释:外星人群和飞船发生碰撞后触发的程序响应:
1、飞船数量减少1
2、子弹组和外星人群组被清空(初始化的时候再重新创建)
3、重新创建外外星人群,并且重新创建飞船置于屏幕中央
4、提供1秒钟时间的游戏间隔,使玩家做好准备

替换game_functions.py中的update_aliens()的碰撞检测内容:

def update_aliens(ai_settings, stats, screen, ship, aliens, bullets):
    """检查是否有外星人位于屏幕边缘,并更新整群外星人的位置"""
    check_fleet_edges(ai_settings, aliens)
    aliens.update()
    # 检测外星人和飞船之间的碰撞
    if pygame.sprite.spritecollideany(ship, aliens):
        ship_hit(ai_settings, stats, screen, ship, aliens, bullets)
最后,我们还需要在Ship中定义用于重置飞船出现在屏幕中央的ship.center_ship()方法:
	def center_ship(self):
        """让飞船在屏幕上居中"""
        self.center = self.screen_rect.centerx

3、有外星人到达屏幕底端

外星人触碰到屏幕底端所触发的程序响应与其碰撞到飞船所触发的程序响应是一样的,所以,我们只需要判断其是否到达屏幕底端,然后重新调用用于判断外星人碰撞到飞船所触发的程序响应函数ship_hit()即可。

在game_functions.py中添加一个用于处理外星人到达屏幕底端所触发程序响应的函数check_aliens_bottom():

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

在game_functions.py中的update_aliens()中添加check_aliens_bottom()即可:

def update_aliens(ai_settings, stats, screen, ship, aliens, bullets):
    """检查是否有外星人位于屏幕边缘,并更新整群外星人的位置"""
    check_fleet_edges(ai_settings, aliens)
    aliens.update()
    # 检测外星人和飞船之间的碰撞
    if pygame.sprite.spritecollideany(ship, aliens):
        ship_hit(ai_settings, stats, screen, ship, aliens, bullets)
    # 检查是否有外星人到达屏幕底端
    check_aliens_bottom(ai_settings, stats, screen, ship, aliens, bullets)

4、游戏结束

现在这个游戏看起来更完整了,但它永远都不会结束,只是 ships_left 不断变成更小的负数。

下面在 GameStats 中添加一个作为标志的属性 game_active ,以便在玩家的飞船用完后结束游戏:
game_stats.py中的__init__():

    def __init__(self, ai_settings):
        """初始化统计信息"""
        self.ai_settings = ai_settings
        self.reset_stats()
        # 游戏刚启动时处于活动状态
        self.game_active = True

解释: game_active作为一个判断标志,只有它为True的时候才会创建新的飞船继续游戏,否则就不创建。

在game_functions.py中的 ship_hit() 中添加代码,在玩家的飞船都用完后将 game_active 设置为 False :

def ship_hit(ai_settings, stats, screen, ship, aliens, bullets):
    """响应被外星人撞到的飞船"""
    if stats.ships_left > 0:
        # 将ships_left减1
        stats.ships_left -= 1
        # 清空外星人列表和子弹列表
        aliens.empty()
        bullets.empty()
        # 创建一群新的外星人,并将飞船放到屏幕底端中央
        create_fleet(ai_settings, screen, ship, aliens)
        ship.center_ship()
        # 暂停
        sleep(1)
    else:
        stats.stats.game_active = False

5、确定应运行游戏的哪些部分

在alien_invasion.py中,我们需要确定游戏的哪些部分在任何情况下都应运行,哪些部分仅在游戏处于活动状态时才运行(在主循环中,在任何情况下都需要调用 check_events() ,即便游戏处于非活动状态时亦如此)。
目前主程序alien_invasion.py:

import pygame
from pygame.sprite import Group
from settings import Settings
from ship import Ship
from alien import Alien
import game_functions as gf
from game_stats import GameStats

def run_game():
    #初始化游戏并创建一个窗口对象
    pygame.init()
    #初始化设置对象
    ai_settings = Settings()
    #设置窗口大小为1200*800像素
    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()

    #创建一个外星人
    # alien = Alien(ai_settings, screen) #替换为创建外星人群
    # 创建外星人群
    gf.create_fleet(ai_settings, screen, ship, aliens)

    # 创建一个用于存储游戏统计信息的实例
    stats = GameStats(ai_settings)

    #开始游戏主循环
    while True:
        #监听键盘和鼠标事件
        gf.check_events(ai_settings, screen, ship, bullets)

        if stats.game_active:
            #调用飞船目前位置
            ship.update()
            #调用管理子弹的函数
            gf.update_bullets(ai_settings, screen, ship, aliens, bullets)
            #调用外星人群的函数
            gf.update_aliens(ai_settings, stats, screen, ship, aliens, bullets)

        #每次循环重绘屏幕
        gf.update_screen(ai_settings, screen, ship, aliens, bullets)

run_game()

ok,现在《外星人入侵》项目的武装飞船和外星人都已经完成了,下面还需要添加一些人性化的设置,比如计分、退出游戏等功能。

这是一篇读书笔记,如有不妥之处,请各位指正!!!

标签:settings,Python,aliens,外星人,之二,ai,bullets,ship
来源: https://blog.csdn.net/qq_35092730/article/details/112216154