Python应用实例(一)外星人入侵(七)

news2024/12/30 3:30:12

外星人入侵(七)

  • 1.射杀外星人
    • 1.1 检测子弹与外星人的碰撞
    • 1.2 为测试创建大子弹
    • 1.3 生成新的外星人群
    • 1.4 提高子弹的速度
    • 1.5 重构_update_bullets()
  • 2.结束游戏
    • 2.1 检测外星人和飞船碰撞
    • 2.2 响应外星人和飞船碰撞
    • 2.3 有外星人到达屏幕底端
    • 2.4 游戏结束
  • 3.确定应运行游戏的哪些部分

1.射杀外星人

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

1.1 检测子弹与外星人的碰撞

子弹击中外星人时,我们需要马上知道,以便碰撞发生后让子弹立即消失。为此,我们将在更新子弹的位置后立即检测碰撞。

函数sprite.groupcollide()将一个编组中每个元素的rect同另一个编组中每个元素的rect进行比较。在这里,是将每颗子弹的rect同每个外星人的rect进行比较,并返回一个字典,其中包含发生了碰撞的子弹和外星人。在这个字典中,每个键都是一颗子弹,而关联的值是被该子弹击中的外星人。

在方法_update_bullets()末尾,添加如下检查子弹和外星人碰撞的代码:alien_invasion.py

    def _update_bullets(self):
        """更新子弹的位置,并删除消失的子弹。"""
        --snip--

        # 检查是否有子弹击中了外星人。
        #  如果是,就删除相应的子弹和外星人。
        collisions = pygame.sprite.groupcollide(
                self.bullets, self.aliens, True, True)

这些新增的代码将self.bullets中所有的子弹都与self.aliens中所有的外星人进行比较,看它们是否重叠在一起。每当有子弹和外星人的rect重叠时,groupcollide()就在它返回的字典中添加一个键值对。两个实参True让Pygame删除发生碰撞的子弹和外星人。(要模拟能够飞行到屏幕顶端、消灭击中的每个外星人的高能子弹,可将第一个布尔实参设置为False,并保留第二个布尔参数为True。这样被击中的外星人将消失,但所有的子弹都始终有效,直到抵达屏幕顶端后消失。)

如果此时运行这个游戏,被击中的外星人将消失。如图13-5所示,有些外星人被射杀了。

在这里插入图片描述

1.2 为测试创建大子弹

只需运行这个游戏就可测试很多功能,但有些功能在正常情况下测试起来比较烦琐。例如,要测试代码能否正确处理外星人编组为空的情形,需要花很长时间将屏幕上的外星人全部射杀。

测试有些功能时,可以修改游戏的某些设置,以便能够专注于游戏的特定方面。例如,可以缩小屏幕以减少需要射杀的外星人数量,也可以提高子弹的速度,以便能够在单位时间内发射大量子弹。

测试这个游戏时,我喜欢做的一项修改是,增大子弹的尺寸并使其在击中外星人后依然有效,如图13-6所示。请尝试将bullet_width设置为300乃至3000,看看将所有外星人全部射杀有多快!

在这里插入图片描述

这样的修改可提高测试效率,还可能激发出如何赋予玩家更大威力的思想火花。(完成测试后,别忘了将设置恢复正常。)

1.3 生成新的外星人群

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

要在一群外星人被消灭后再显示一群外星人,首先需要检查编组aliens是否为空。如果是,就调用_create_fleet()。我们将在_update_bullets()末尾执行这项任务,因为外星人都是在这里被消灭的:

alien_invasion.py

      def _update_bullets(self):
          --snip--
❶         if not self.aliens:
              # 删除现有的子弹并新建一群外星人。
❷             self.bullets.empty()
              self._create_fleet()

在❶处,检查编组aliens是否为空。空编组相当于False,因此这是一种检查编组是否为空的简单方式。如果编组aliens为空,就使用方法empty()删除编组中余下的所有精灵,从而删除现有的所有子弹(见❷)。我们还调用了_create_fleet(),在屏幕上重新显示一群外星人。

现在,当前这群外星人被消灭干净后,将立刻出现一群新的外星人。

1.4 提高子弹的速度

如果现在尝试在游戏中射杀外星人,可能会发现子弹的速度不太合适(有点快或有点慢),游戏感不好。当前,可通过修改设置让这款游戏更有意思、更好玩。

要修改子弹的速度,可调整settings.py中bullet_speed的值。在我的系统中,我把bullet_speed的值调整到1.5,让子弹的速度快些:

         # 子弹设置
         self.bullet_speed = 1.5
         self.bullet_width = 3
         --snip--

这项设置的最佳值取决于你使用的系统的速度,请找出适合自己的值。你也可以调整其他设置。

1.5 重构_update_bullets()

下面来重构_update_bullets(),使其不再执行那么多任务。为此,将处理子弹和外星人碰撞的代码移到一个独立的方法中:

alien_invasion.py

    def _update_bullets(self):
        --snip--
        # 删除消失的子弹。
        for bullet in self.bullets.copy():
            if bullet.rect.bottom <= 0:
                self.bullets.remove(bullet)

        self._check_bullet_alien_collisions()

    def _check_bullet_alien_collisions(self):
        """响应子弹和外星人碰撞。"""
        # 删除发生碰撞的子弹和外星人。

        collisions = pygame.sprite.groupcollide(
                self.bullets, self.aliens, True, True)

        if not self.aliens:
            # 删除现有的所有子弹,并创建一群新的外星人。
            self.bullets.empty()
            self._create_fleet()

我们创建了一个新方法_check_bullet_alien_collisions(),用于检测子弹和外星人之间的碰撞,并在整群外星人被消灭干净时采取相应的措施。这能避免_update_bullets()过长,简化了后续开发工作。

2.结束游戏

如果玩家根本不会输,游戏还有什么趣味和挑战性可言?如果玩家没能在足够短的时间内将整群外星人消灭干净,导致有外星人撞到了飞船或抵达屏幕底端,飞船将被摧毁。与此同时,限制玩家可使用的飞船数,在玩家用光所有的飞船后,游戏将结束。

2.1 检测外星人和飞船碰撞

首先检查外星人和飞船之间的碰撞,以便在外星人撞上飞船时做出合适的响应。为此,在AlienInvasion中更新每个外星人的位置后,立即检测外星人和飞船之间的碰撞:

alien_invasion.py

      def _update_aliens(self):
          --snip--
           self.aliens.update()

           # 检测外星人和飞船之间的碰撞。if pygame.sprite.spritecollideany(self.ship, self.aliens):
❷             print("Ship hit!!!")

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

如果没有发生碰撞,spritecollideany()将返回None,因此❶处的if代码块不会执行。如果找到了与飞船发生碰撞的外星人,它就返回这个外星人,因此if代码块将执行:打印“Ship hit!!!”(见❷)。有外星人撞到飞船时,需要执行很多任务:删除余下的外星人和子弹,让飞船重新居中,以及创建一群新的外星人。编写完成这些任务的代码之前,需要确定检测外星人和飞船碰撞的方法是否可行。为此,最简单的方式就是调用函数print()。

现在如果运行这个游戏,则每当有外星人撞到飞船时,终端窗口都将显示“Ship hit!!!”。测试这项功能时,请将alien_drop_speed设置为较大的值,如50或100,这样外星人将更快地撞到飞船。

2.2 响应外星人和飞船碰撞

现在需要确定当外星人与飞船发生碰撞时该做些什么。我们不销毁Ship实例并创建新的,而是通过跟踪游戏的统计信息来记录飞船被撞了多少次(跟踪统计信息还有助于记分)。

下面来编写一个用于跟踪游戏统计信息的新类GameStats,并将其保存为文件game_stats.py:

class GameStats:
    """跟踪游戏的统计信息。"""

    def __init__(self, ai_game):
        """初始化统计信息。"""
        self.settings = ai_game.settings
        self.reset_stats()

    def reset_stats(self):
        """初始化在游戏运行期间可能变化的统计信息。"""
        self.ships_left = self.settings.ship_limit

在游戏运行期间,只创建一个GameStats实例,但每当玩家开始新游戏时,需要重置一些统计信息。为此,在方法reset_stats()中初始化大部分统计信息,而不是在__init__()中直接初始化。我们在__init__()中调用这个方法,这样创建GameStats实例时将妥善地设置这些统计信息,在玩家开始新游戏时也能调用reset_stats()。

当前,只有一项统计信息ships_left,其值在游戏运行期间不断变化。一开始玩家拥有的飞船数存储在settings.py的ship_limit中:

settings.py

        # 飞船设置
        self.ship_speed = 1.5
        self.ship_limit = 3

还需对alien_invasion.py做些修改,以创建一个GameStats实例。首先,更新这个文件开头的import语句:

alien_invasion.py

import sys
from time import sleep

import pygame

from settings import Settings
from game_stats import GameStats
from ship import Ship
--snip--

从Python标准库的模块time中导入函数sleep(),以便在飞船被外星人撞到后让游戏暂停片刻。我们还导入了GameStats。

接下来,在__init__()中创建一个GameStats实例:

alien_invasion.py

    def __init__(self):
        --snip--
        self.screen = pygame.display.set_mode(
            (self.settings.screen_width, self.settings.screen_height))
        pygame.display.set_caption("Alien Invasion")

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

        self.ship = Ship(self)
        --snip--

在创建游戏窗口后、定义诸如飞船等其他游戏元素前,创建一个GameStats实例。

有外星人撞到飞船时,将余下的飞船数减1,创建一群新的外星人,并将飞船重新放到屏幕底端的中央。另外,让游戏暂停片刻,让玩家在新外星人群出现前注意到发生了碰撞并将重新创建外星人群。

下面将实现这些功能的大部分代码放到新方法_ship_hit()中。在_update_aliens()中,将在有外星人撞到飞船时调用这个方法:

alien_invasion.py

      def _ship_hit(self):
          """响应飞船被外星人撞到。"""

          # 将ships_left减1。
❶         self.stats.ships_left -= 1

          # 清空余下的外星人和子弹。
❷         self.aliens.empty()
          self.bullets.empty()

          # 创建一群新的外星人,并将飞船放到屏幕底端的中央。
❸         self._create_fleet()
          self.ship.center_ship()

          # 暂停。
❹         sleep(0.5)

新方法_ship_hit()在飞船被外星人撞到时做出响应。在这个方法中,将余下的飞船数减1(见❶),再清空编组aliens和bullets(见❷)。

接下来,创建一群新的外星人,并将飞船居中(见❸)。(稍后将在Ship类中添加方法center_ship()。)最后,在更新所有元素后(但在将修改显示到屏幕前)暂停,让玩家知道飞船被撞到了(见❹)。这里的函数调用sleep()让游戏暂停半秒钟,让玩家能够看到外星人撞到了飞船。函数sleep()执行完毕后,将接着执行方法_update_screen(),将新的外星人群绘制到屏幕上。

在_update_aliens()中,当有外星人撞到飞船时,不调用函数print(),而调用_ship_hit():

ship.py

    def center_ship(self):
        """让飞船在屏幕底端居中。"""
        self.rect.midbottom = self.screen_rect.midbottom
        self.x = float(self.rect.x)

这里像__init__()中那样让飞船在屏幕底端居中。让飞船在屏幕底端居中后,重置用于跟踪飞船确切位置的属性self.x。

注意 我们根本没有创建多艘飞船。在整个游戏运行期间,只创建了一个飞船实例,并在该飞船被撞到时将其居中。统计信息ships_left指出玩家是否用完了所有的飞船。

请运行这个游戏,射杀几个外星人,并让一个外星人撞到飞船。游戏暂停片刻后,将出现一群新的外星人,而飞船将在屏幕底端居中。

2.3 有外星人到达屏幕底端

如果有外星人到达屏幕底端,我们将像有外星人撞到飞船那样做出响应。为检测这种情况,在alien_invasion.py中添加一个新方法:a

lien_invasion.py

      def _check_aliens_bottom(self):
          """检查是否有外星人到达了屏幕底端。"""
          screen_rect = self.screen.get_rect()
          for alien in self.aliens.sprites():if alien.rect.bottom >= screen_rect.bottom:
                  # 像飞船被撞到一样处理。
                  self._ship_hit()
                  break

方法_check_aliens_bottom()检查是否有外星人到达了屏幕底端。到达屏幕底端后,外星人的属性rect.bottom大于或等于屏幕的属性rect.bottom(见❶)。如果有外星人到达屏幕底端,就调用_ship_hit()。只要检测到一个外星人到达屏幕底端,就无须检查其他外星人了,因此在调用_ship_hit()后退出循环。

我们在_update_aliens()中调用_check_aliens_bottom():

alien_invasion.py

    def _update_aliens(self):
        --snip--
        # 检查是否有外星人撞到飞船。
        if pygame.sprite.spritecollideany(self.ship, self.aliens):
            self._ship_hit()

        # 检查是否有外星人到达了屏幕底端。
        self._check_aliens_bottom()

在更新所有外星人的位置并检测是否有外星人和飞船发生碰撞后调用_check_aliens_bottom()。现在,每当有外星人撞到飞船或抵达屏幕底端时,都将出现一群新的外星人。

2.4 游戏结束

现在这个游戏看起来更完整了,但它永远都不会结束,只是ships_left不断变成越来越小的负数。下面在GameStats中添加一个作为标志的属性game_active,以便在玩家的飞船用完后结束游戏。首先,在GameStats类的方法__init__()末尾设置这个标志:

game_stats.py

    def __init__(self, ai_game):
        --snip--
        # 游戏刚启动时处于活动状态。
        self.game_active = True

接下来在_ship_hit()中添加代码,在玩家的飞船用完后将game_active设置为False:

alien_invasion.py

    def _ship_hit(self):
        """响应飞船被外星人撞到。"""
        if self.stats.ships_left > 0:
            # 将ships_left减1。
            self.stats.ships_left -= 1
          --snip--
            # 暂停。
            sleep(0.5)
        else:
            self.stats.game_active = False

_ship_hit()的大部分代码没有变。我们将原来的代码都移到了一个if语句块中,它检查玩家是否至少还有一艘飞船。如果是,就创建一群新的外星人,暂停片刻,再接着往下执行。如果玩家没有了飞船,就将game_active设置为False。

3.确定应运行游戏的哪些部分

我们需要确定游戏的哪些部分在任何情况下都应运行,哪些部分仅在游戏处于活动状态时才运行:

alien_invasion.py

    def run_game(self):
        """开始游戏主循环。"""
        while True:
            self._check_events()

            if self.stats.game_active:
                self.ship.update()
                self._update_bullets()
                self._update_aliens()

            self._update_screen()

在主循环中,在任何情况下都需要调用_check_events(),即便游戏处于非活动状态。例如,我们需要知道玩家是否按了Q键以退出游戏,或者是否单击了关闭窗口的按钮。我们还需要不断更新屏幕,以便在等待玩家是否选择开始新游戏时修改屏幕。其他的函数仅在游戏处于活动状态时才需要调用,因为游戏处于非活动状态时,不用更新游戏元素的位置。

现在运行这个游戏,它将在飞船用完后停止不动。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/736798.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

毫米波雷达(三):实操

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、福相一致性校准二、速度解模糊其他 一、福相一致性校准 多波束天线通道幅相一致性校正及实现 https://www.docin.com/p-2640129630.html 二、速度解模糊 一种…

SpringBoot的@ConfigurationProperties、@Autowired、@Conditional注解

目录 1. ConfigurationProperties EnableConfigurationProperties Autowired注解1.1 configuration自定义配置参数自动补全功能 2. Conditional注解 1. ConfigurationProperties EnableConfigurationProperties Autowired注解 在resources/application.properties文件中&a…

map、foreach 和 for ,多角度对比

for 1.访问元素和操作方式------通过索引访问数组元素&#xff0c;并可以执行任意操作。 const numbers [1, 2, 3, 4, 5];// for循环示例 for (let i 0; i < numbers.length; i) {console.log("Number (for loop): " numbers[i]); } 2.索引控制--------通过…

非线性系统的混沌特性分析方法--相图/分岔图分析法

非线性系统的混沌特性分析方法–相图/分岔图分析法 ​ 混沌映射被用于生成混沌序列&#xff0c;这是一种由简单的确定性系统产生的随机性序列。一般混沌序列具有以下主要特征&#xff1a; 非线性&#xff1b;对初值的敏感依赖性&#xff1b;遍历性&#xff1b;随机性&#xf…

【Linux初阶】基础IO - 磁盘 文件系统

&#x1f31f;hello&#xff0c;各位读者大大们你们好呀&#x1f31f; &#x1f36d;&#x1f36d;系列专栏&#xff1a;【Linux初阶】 ✒️✒️本篇内容&#xff1a;认识磁盘&#xff08;物理结构、储存结构、逻辑结构、读取单位&#xff09;&#xff0c;理解文件系统&#xf…

会声会影2023最新免费版零基础上手视频剪辑工具

比如会声会影视频编辑软件&#xff0c;既加入光影、动态特效的滤镜效果&#xff0c;也提供了与色彩调整相关的LUT配置文件滤镜&#xff0c;可选择性大&#xff0c;运用起来更显灵活。会声会影在用户的陪伴下走过20余载&#xff0c;经过上百个版本的优化迭代&#xff0c;已将操作…

【消息队列RabbitMQ】一、RabbitMQ认识

这里写目录标题 RabbitMQSpringboot整合RabbitMQRabbitMQ的常见开发模式如何保证消息的可靠生产和可靠投递什么是死值队列和延迟队列 RabbitMQ 消息队列有&#xff1a; 1、ActiveMQ 2、RabbitMQ 3、ZeroMQ 4、Kafka 什么是RabbitMQ&#xff1f; RabbitMQ是一个开源的消息队列服…

第23章:范式

一、范式 1.什么是范式 关于数据表设计的基本原则&#xff0c;规则就是范式NF。 2.范式都包括哪些&#xff1f; 第一范式&#xff08;1NF&#xff09;、第二范式&#xff08;2NF&#xff09;、第三范式&#xff08;3NF&#xff09;、巴斯-科德范式&#xff08;BCNF - Boyce…

两台电脑如何共享文件?6步快速完成!

电脑之间共享文件已经成为日常工作中不可或缺的一部分。无论是在家庭环境中与家人共享照片和视频&#xff0c;还是在办公室中与同事共享文档和数据&#xff0c;了解两台电脑如何共享文件是非常重要的。 但在实际操作中&#xff0c;如何更好的使两台电脑共享文件&#xff0c;也…

多层感知机(MLP)算法原理和代码实现

文章目录 多层感知机入门算法优化原理sklearn代码实现核心优缺点分析 多层感知机入门 神经网络在最近几年&#xff0c;是个很火的名词了。常听到的卷积神经网络(CNN)或者循环神经网络(RNN&#xff09;&#xff0c;都可以看做是神经网络在特定场景下的具体应用方式。 本文我们…

干货 | 智能网联汽车大数据基础平台构建研究

以下内容整理自大数据能力提升项目必修课《大数据系统基础》同学们的期末答辩汇报。 各位老师大家上午好&#xff0c;我们组的题目是智能网联汽车大数据基础平台的构建。我们的指导企业是西部智联。我们的汇报将从这五个方面进行展开&#xff0c;第一个方面是项目背景与需求分析…

uni-app基础知识

发展 DCloud于2012年开始研发小程序技术&#xff0c;优化webview的功能和性能&#xff0c;并加入W3C和HTML5中国产业联盟。 2015年&#xff0c;DCloud正式商用了自己的小程序&#xff0c;产品名为“流应用”&#xff0c;它不是B/S模式的轻应用&#xff0c;而是能接近原生功能…

Debezium系列之:基于debezium将mysql数据库数据更改流式传输到 Elasticsearch和PostgreSQL数据库

Debezium系列之&#xff1a;基于debezium将mysql数据库数据更改流式传输到 Elasticsearch和PostgreSQL数据库 一、背景二、技术路线三、配置四、从mysql同步数据到Elasticsearch和PostgreSQL数据库五、总结 一、背景 基于 Debezium 的端到端数据流用例&#xff0c;将数据流式传…

I/O 多路复用小结

Socket 模型 Socket 编程是一种使用 Socket 模型进行网络通信的编程技术。它是一种基于网络套接字的编程模型&#xff0c;用于实现不同计算机之间的数据传输。 事实上&#xff0c;在进行网络通信前&#xff0c;通信双方都要创建一个 Socket&#xff0c;双方的数据读写都要依赖于…

【Python】执行SQL报错

可以再数据库查询界面执行的SQL&#xff0c;一直报错 unsupported format character Y (0x59) at index 61 SQL如下&#xff1a; datapd.read_sql_query(sql"""selectdate_format(create_time,%Y-%m) as mon,count(distinct order_id) as ord_cntfrom prod.o…

HTTP与HTTPS

HTTP与HTTPS介绍 超文本传输协议HTTP协议被用于在Web浏览器和网站服务器之间传递信息&#xff0c;HTTP协议以明文方式发送内容&#xff0c;不提供任何方式的数据加密&#xff0c;如果攻击者截取了Web浏览器和网站服务器之间的传输报文&#xff0c;就可以直接读懂其中的信息&…

qt源码--事件系统

qt的事件传播主要依赖于QCoreApplication、QAbstractEventDispatcher&#xff08;会根据不同的平台生成各自的处理对象&#xff09;、QEvent&#xff08;各种事件类型&#xff09;等。 首先看下QCoreApplication的实现&#xff1a; 2、了解QCoreApplication的构造函数 其构造函…

在最新ICP备案域名的基础上,结合其他网络营销手段,打造强大的品牌推广效果

API接口是一种软件系统之间进行交互的方式&#xff0c;通过API接口&#xff0c;可以在不同的系统之间传递数据、命令等信息。在网络营销中&#xff0c;API接口可以帮助我们更加高效地进行品牌推广。本文将以在最新ICP备案域名的基础上&#xff0c;结合其他网络营销手段&#xf…

JVM回收算法(标记-清除算法, 复制算法, 标记-整理算法)

1.标记-清除算法 最基础的算法&#xff0c;分为两个阶段&#xff0c;“标记”和“清除” 原理&#xff1a; - 标记阶段&#xff1a;collector从mutator根对象开始进行遍历&#xff0c;对从mutator根对象可以访问到的对象都打上一个标识&#xff0c;一般是在对象的header中&am…

vue-router 4.0 动态路由会跳转到 404 页面的问题

引子 开发过前端单页面应用的小伙伴们&#xff0c;应该对前端路由都不陌生吧。 无论是用 vue 或者 react&#xff0c;都有官方提供的 router 方案。 但是有些场景下&#xff0c;处于安全性和友好性考虑&#xff0c;我们需要用到动态路由。 如果你不知道什么叫动态路由&…