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

news2024/11/15 8:57:42

外星人入侵(四)

  • 1.前文总结回顾
    • 1.1 alien_invasion.py
    • 1.2 settings.py
    • 1.3 ship.py
  • 2.射击
    • 2.1 添加子弹设置
    • 2.2 创建Bullet类
    • 2.3 将子弹存储到编组中
    • 2.4 开火
    • 2.5 删除消失的子弹
    • 2.6 限制子弹数量
    • 2.7 创建方法_update_bullets()

1.前文总结回顾

1.1 alien_invasion.py

主文件alien_invasion.py包含AlienInvasion类。这个类创建一系列贯穿整个游戏都要用到的属性:赋给self.settings的设置,赋给screen中的主显示surface,以及一个飞船实例。这个模块还包含游戏的主循环,即一个调用_check_events()、ship.update()和_update_screen()的while循环。

方法_check_events()检测相关的事件(如按下和松开键盘),并通过调用方法_check_keydown_events()和_check_keyup_events()处理这些事件。当前,这些方法负责管理飞船的移动。AlienInvasion类还包含方法_update_screen(),该方法在每次主循环中重绘屏幕。

要玩游戏《外星人入侵》,只需运行文件alien_invasion.py,其他文件(settings.py和ship.py)包含的代码会被导入这个文件中。

1.2 settings.py

文件settings.py包含Settings类,这个类只包含方法__init__(),用于初始化控制游戏外观和飞船速度的属性。

1.3 ship.py

文件ship.py包含Ship类,这个类包含方法__init__()、管理飞船位置的方法update()和在屏幕上绘制飞船的方法blitme()。表示飞船的图像存储在文件夹images下的文件ship.bmp中。

2.射击

下面来添加射击功能。我们将编写在玩家按空格键时发射子弹(用小矩形表示)的代码。子弹将在屏幕中向上飞行,抵达屏幕上边缘后消失。

2.1 添加子弹设置

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

    def __init__(self):
        --snip--
        # 子弹设置
        self.bullet_speed = 1.0
        self.bullet_width = 3
        self.bullet_height = 15
        self.bullet_color = (60, 60, 60)

这些设置创建宽3像素、高15像素的深灰色子弹。子弹的速度比飞船稍低。

2.2 创建Bullet类

下面来创建存储Bullet类的文件bullet.py,其前半部分如下:

bullet.py

  import pygame
  from pygame.sprite import Sprite

  class Bullet(Sprite):
      """管理飞船所发射子弹的类"""

      def __init__(self, ai_game):
          """在飞船当前位置创建一个子弹对象。"""
          super().__init__()
          self.screen = ai_game.screen
          self.settings = ai_game.settings
          self.color = self.settings.bullet_color

          #在(0,0)处创建一个表示子弹的矩形,再设置正确的位置。
❶         self.rect = pygame.Rect(0, 0, self.settings.bullet_width,
              self.settings.bullet_height)
❷         self.rect.midtop = ai_game.ship.rect.midtop

          # 存储用小数表示的子弹位置。
❸         self.y = float(self.rect.y)

Bullet类继承了从模块pygame.sprite导入的Sprite类。通过使用精灵(sprite),可将游戏中相关的元素编组,进而同时操作编组中的所有元素。为创建子弹实例,init()需要当前的AlienInvasion实例,我们还调用了super()来继承Sprite。另外,我们还定义了用于存储屏幕以及设置对象和子弹颜色的属性。

在❶处,创建子弹的属性rect。子弹并非基于图像,因此必须使用pygame.Rect()类从头开始创建一个矩形。创建这个类的实例时,必须提供矩形左上角的[插图]坐标和[插图]坐标,以及矩形的宽度和高度。我们在(0, 0)处创建这个矩形,但下一行代码将其移到了正确的位置,因为子弹的初始位置取决于飞船当前的位置。子弹的宽度和高度是从self.settings中获取的。

在❷处,将子弹的rect.midtop设置为飞船的rect.midtop。这样子弹将从飞船顶部出发,看起来像是从飞船中射出的。我们将子弹的[插图]坐标存储为小数值,以便能够微调子弹的速度(见❸)。

下面是bullet.py的第二部分,包括方法update()和draw_bullet():

      def update(self):
          """向上移动子弹。"""
          #更新表示子弹位置的小数值。
❶         self.y -= self.settings.bullet_speed
          # 更新表示子弹的rect的位置。
❷         self.rect.y = self.y

      def draw_bullet(self):
          """在屏幕上绘制子弹。"""
❸         pygame.draw.rect(self.screen, self.color, self.rect)

方法update()管理子弹的位置。发射出去后,子弹向上移动,意味着其[插图]坐标将不断减小。为更新子弹的位置,从self.y中减去settings .bullet_speed的值(见❶)。接下来,将self.rect.y设置为self.y的值(见❷)。

属性bullet_speed让我们能够随着游戏的进行或根据需要提高子弹的速度,以调整游戏的行为。子弹发射后,其[插图]坐标始终不变,因此子弹将沿直线垂直向上飞行。

需要绘制子弹时,我们调用draw_bullet()。draw.rect()函数使用存储在self.color中的颜色填充表示子弹的rect占据的屏幕部分(见❸)。

2.3 将子弹存储到编组中

定义Bullet类和必要的设置后,便可编写代码在玩家每次按空格键时都射出一发子弹了。我们将在AlienInvasion中创建一个编组(group),用于存储所有有效的子弹,以便管理发射出去的所有子弹。这个编组是pygame.sprite.Group类的一个实例。pygame.sprite.Group类似于列表,但提供了有助于开发游戏的额外功能。在主循环中,将使用这个编组在屏幕上绘制子弹以及更新每颗子弹的位置。

首先,在__init__()中创建用于存储子弹的编组:

alien_invasion.py

    def __init__(self):
        --snip--
        self.ship = Ship(self)
        self.bullets = pygame.sprite.Group()

然后在while循环中更新子弹的位置:

      def run_game(self):
          """开始游戏主循环。"""
          while True:
              self._check_events()
              self.ship.update()
❶             self.bullets.update()
              self._update_screen()

对编组调用update()时(见❶),编组自动对其中的每个精灵调用update()。因此代码行bullets.update()将为编组bullets中的每颗子弹调用bullet.update()。

2.4 开火

在AlienInvasion中,需要修改_check_keydown_events(),以便在玩家按空格键时发射一颗子弹。无须修改_check_keyup_events(),因为玩家松开空格键时什么都不会发生。还需要修改_update_screen(),确保在调用flip()前在屏幕上重绘每颗子弹。

为发射子弹,需要做的工作不少,因此编写一个新方法_fire_bullet()来完成这项任务:

alien_invasion.py

  --snip--
  from ship import Ship
❶ from bullet import Bullet

  class AlienInvasion:
      --snip--
      def _check_keydown_events(self, event):
          --snip--
          elif event.key == pygame.K_q:
              sys.exit()elif event.key == pygame.K_SPACE:
              self._fire_bullet()

      def _check_keyup_events(self, event):
          --snip--

      def _fire_bullet(self):
          """创建一颗子弹,并将其加入编组bullets中。"""
❸         new_bullet = Bullet(self)
❹         self.bullets.add(new_bullet)

      def _update_screen(self):
          """更新屏幕上的图像,并切换到新屏幕。"""
          self.screen.fill(self.settings.bg_color)
          self.ship.blitme()for bullet in self.bullets.sprites():
              bullet.draw_bullet()
          pygame.display.flip()
  --snip--

首先导入Bullet类(见❶),再在玩家按空格键时调用_fire_bullet()(见❷)。在_fire_bullet()中,创建一个Bullet实例并将其赋给new_bullet(见❸),再使用方法add()将其加入编组bullets中(见❹)。方法add()类似于append(),不过是专门为Pygame编组编写的。

方法bullets.sprites()返回一个列表,其中包含编组bullets中的所有精灵。为在屏幕上绘制发射的所有子弹,遍历编组bullets中的精灵,并对每个精灵调用draw_bullet()(见❺)。

如果此时运行alien_invasion.py,将能够左右移动飞船,并发射任意数量的子弹。子弹在屏幕上向上飞行,抵达屏幕顶部后消失得无影无踪,如图12-3所示。你可在settings.py中修改子弹的尺寸、颜色和速度。

在这里插入图片描述

2.5 删除消失的子弹

当前,子弹在抵达屏幕顶端后消失,但这仅仅是因为Pygame无法在屏幕外面绘制它们。这些子弹实际上依然存在,其[插图]坐标为负数且越来越小。这是个问题,因为它们将继续消耗内存和处理能力。

需要将这些消失的子弹删除,否则游戏所做的无谓工作将越来越多,进而变得越来越慢。为此,需要检测表示子弹的rect的bottom属性是否为零。如果是,则表明子弹已飞过屏幕顶端:

alien_invasion.py

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

              # 删除消失的子弹。for bullet in self.bullets.copy():if bullet.rect.bottom <= 0:
❸                     self.bullets.remove(bullet)print(len(self.bullets))

              self._update_screen()

使用for循环遍历列表(或Pygame编组)时,Python要求该列表的长度在整个循环中保持不变。因为不能从for循环遍历的列表或编组中删除元素,所以必须遍历编组的副本。我们使用方法copy()来设置for循环(见❶),从而能够在循环中修改bullets。我们检查每颗子弹,看看它是否从屏幕顶端消失(见❷)。如果是,就将其从bullets中删除(见❸)。在❹处,使用函数调用print()显示当前还有多少颗子弹,以核实确实删除了消失的子弹。

如果这些代码没有问题,我们发射子弹后查看终端窗口时,将发现随着子弹一颗颗地在屏幕顶端消失,子弹数将逐渐降为零。运行该游戏并确认子弹被正确删除后,请将这个函数调用print()删除。如果不删除,游戏的速度将大大降低,因为将输出写入终端花费的时间比将图形绘制到游戏窗口花费的时间还要多。

2.6 限制子弹数量

很多射击游戏对可同时出现在屏幕上的子弹数量进行了限制,以鼓励玩家有目标地射击。下面在游戏《外星人入侵》中做这样的限制。

首先,在settings.py中存储最大子弹数:

        # 子弹设置
        --snip--
        self.bullet_color = (60, 60, 60)
        self.bullets_allowed = 3

这将未消失的子弹数限制为三颗。在AlienInvasion的_fire_bullet()中,在创建新子弹前检查未消失的子弹数是否小于该设置:

alien_invasion.py

    def _fire_bullet(self):
        """创建新子弹并将其加入编组bullets中。"""
        if len(self.bullets) < self.settings.bullets_allowed:
            new_bullet = Bullet(self)
            self.bullets.add(new_bullet)

玩家按空格键时,我们检查bullets的长度。如果len(bullets)小于3,就创建一颗新子弹;但如果有三颗未消失的子弹,则玩家按空格键时什么都不会发生。如果现在运行这个游戏,屏幕上最多只能有三颗子弹。

2.7 创建方法_update_bullets()

编写并检查子弹管理代码后,可将其移到一个独立的方法中,确保AlienInvasion类组织有序。为此,创建一个名为_update_bullets()的新方法,并将其放在_update_screen()前面:

alien_invasion.py

    def _update_bullets(self):
        """更新子弹的位置并删除消失的子弹。"""
        # 更新子弹的位置。
        self.bullets.update()

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

_update_bullets()的代码是从run_game()剪切并粘贴而来的,这里只是让注释更清晰了。
run_game()中的while循环又变得简单了:

alien_invasion.py

      while True:
          self._check_events()
          self.ship.update()
          self._update_bullets()
          self._update_screen()

我们让主循环包含尽可能少的代码,这样只要看方法名就能迅速知道游戏中发生的情况。主循环检查玩家的输入,并更新飞船的位置和所有未消失子弹的位置。然后,使用更新后的位置来绘制新屏幕。

请再次运行alien_invasion.py,确认发射子弹时没有错误。

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

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

相关文章

java项目之汽车在线销售系统(ssm+mysql)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于ssm的汽车在线销售系统。 开发环境&#xff1a; 后端&#xff1a; 开发语言&#xff1a;Java 框架&#xff1a;ssm&#xff0c;mybatis JDK版…

电脑提示找不到应用程序怎么办?

无论个人电脑还是办公电脑&#xff0c;无一例外的都会安装一些应用程序来帮助我们使用。而在电脑的使用过程中总会出现一些大大小小的问题&#xff0c;其中双击桌面快捷方式显示找不到应用程序是一个比较常见的故障。那么&#xff0c;电脑找不到应用程序怎么解决呢? 电脑找不到…

RabbitMQ学习笔记8 高级特性 消息可靠性+消费

我们在测试类中去写一个方法去实现它&#xff1a; 运行&#xff1a; 我们去掉之前的消息鉴定&#xff0c;我们这里不用消息鉴定。 我们注释掉这个。再去运行&#xff1a; 我们现在模拟一下发送失败的情况&#xff1a; 之前我们设置了我们交换机的名称。 我们在测试类发送消息时…

如何提高力扣(Leetcode)的解题能力?

如何提高力扣&#xff08;Leetcode&#xff09;的解题能力&#xff1f; 力扣&#xff08;Leetcode&#xff09;是一个在线编程平台&#xff0c;提供了各种算法和数据结构的题目&#xff0c;让程序员可以练习和提高自己的编程技能。很多人都把力扣&#xff08;Leetcode&#xf…

【脚本语言】Shell Script - 终端打印指令

目录 终端打印 基础语法 有趣的拓展 在echo中使用转义序列 输出彩色文本 终端打印 终端是交互式工具&#xff0c;在终端中打印文本是绝大多数脚本中的基本任务。 基础语法 终端打印有两种方法输出文本&#xff0c;分别是 echo 和 printf (就是C/C中的那个)。 在默认情况下…

Git---标签管理

文章目录 前言一、理解标签二、创建标签三、操作标签总结 前言 正文开始!!! 一、理解标签 标签tag,可以简单的理解为是对某次commit的一个标识,相当于起了一个别名.例如,在项目发布某个版本的时候,针对最后一次commit起一个v1.0这样的标签来标识里程碑的意义. 这有什么用呢?…

信息系统项目管理师(第四版)教材精读思维导图-第一章信息化发展

请参阅我的另一篇文章&#xff0c;综合介绍软考高项&#xff1a; 信息系统项目管理师&#xff08;软考高项&#xff09;备考总结_计算机技术与软件专业技术_铭记北宸的博客-CSDN博客 思维导图源文件下载&#xff1a; https://download.csdn.net/download/hanjingjava/8801629…

如何在工作中保持稳定的情绪

如何在工作中保持稳定的情绪 在高压的工作环境中&#xff0c;保持稳定的情绪对于程序员来说至关重要。作为一个程序员&#xff0c;我们常常面临着各种技术挑战、项目期限和团队合作的压力。在这样的环境下&#xff0c;情绪的波动可能会影响我们的工作效率和质量。因此&#xff…

使用OpenCV显示图像的RGB颜色直方图

#include <opencv2/opencv.hpp> #include <opencv2/imgproc/imgproc.hpp> using namespace cv;int main( ) {Mat srcImage;srcImage=<

数据特征降维 | 主成分分析PCA数据特征降维

文章目录 效果一览文章概述部分源码部分源码参考资料效果一览 文章概述 数据特征降维 | 主成分分析PCA,(主成分分析经常用于减少数据集的维数,同时保持数据集中的对方差贡献最大的特征) 部分源码 部分源码 %% 清空环境变量 warning off % 关闭报警信息 close a…

linux:ubuntu命令行扩容

参考&#xff1a; ubuntu 20.04 逻辑卷 ext4 文件系统扩容_mob604757006a49的技术博客_51CTO博客 总结&#xff1a; 准备步骤&#xff08;链接里没有说明&#xff0c;我参考了其他的帖子增加的一步&#xff09;&#xff1a; 1.关闭虚拟机&#xff0c;右键虚拟机&#xff0c;选择…

【枚举+容斥】牛客练习赛113 F

牛客练习赛113_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ (nowcoder.com) 题意&#xff1a; 思路&#xff1a; 这道题根本不是子序列DP&#xff0c;因为子序列内部是无序的&#xff0c;这点没意识到 这是问题的easy版本&#xff0c;这种easy版本的题一般都是去…

Windows下PaddleOCR用NCNN部署

1.所用到的ncnn格式的模型文件 要问这些模型哪里来的&#xff0c;请看下面提示信息: 2.查看字典函数读取方法 char* readKeysFromAssets() {std::ifstream ifs("./model/paddleocr_keys.txt");if (!ifs.is_open()){return 0;}ifs.seekg(0, std::ios_base::end);int …

[桌面运维]PC常用的视频接口,显示器VGA、DVI、HDMI、DP、USB-C接口的认识和应用

⬜⬜⬜ &#x1f430;&#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea;(*^▽^*)欢迎光临 &#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea;&#x1f430;⬜⬜⬜ ✏️write in front✏️ &#x1f4dd;个人主页&#xff1a;陈丹宇jmu &am…

keepalived

文章目录 一、suse系统安装keepalived1.1、准备环境1.2、修改主机名1.3、关闭防火墙1.4、配置网络1.5、配置 yum 源1.6、安装 mysql1.7、安装 keepalived报错信息&#xff1a;使用 wget 下载keepalived 报错解决 使用 wget 下载 keepalived的报错 一、suse系统安装keepalived …

详解c++---位图模拟实现

目录标题 为什么会有位图一道题了解位图模拟实现位图setresettest构造函数 代码测试关于位图的几道题第一题第二题 第三题 为什么会有位图 通过前面的学习想必大家应该已经了解了哈希表的原理&#xff0c;我们使用两种不同的方法来模拟实现哈希表&#xff0c;第一种方法就是闭…

加拿大访问学者签证如何提升通过率?

加拿大访问学者是许多学者向往的机会&#xff0c;而提高签证通过率则需要注意一些关键因素。下面是知识人网小编整理的一些建议&#xff0c;希望能对您有所帮助。 首先&#xff0c;准备充分的材料是至关重要的。确保您提供了完整、准确的申请材料&#xff0c;包括学历证明、工作…

vue confirm弹窗去掉取消和右上角关闭按钮

this.confirm弹窗去除取消, 右上角x按钮 1.弹窗是显示 使用的是this.confirm弹窗, 去掉右上角的x和取消按钮, 也不能点击空白处关闭 2.修改代码 this.$confirm(this.$t(login.udpatePwdSuccess), this.$t(common.tip), {confirmButtonText: this.$t(button.confirm),showCance…

量化风控算法详解之CatBoost

CatBoost是俄罗斯的搜索巨头Yandex在2017年开源的机器学习库&#xff0c;与XGBoost、LightGBM并称为GBDT三大主流神器库。LightGBM和XGBoost已经在各领域得到了广泛的应用&#xff0c;而Yandex的CatBoost作为后起之秀则是号称比XGBoost和LightGBM在算法准确率等方面表现更为优秀…

【Hello mysql】 mysql数据类型

Mysql专栏&#xff1a;Mysql 本篇博客简介&#xff1a;介绍mysql的数据类型 mysql数据类型 数据类型分类数值类型tinyint类型越界测试bit类型小数类型float类型decimal类型 字符串类型charvarchar 时间和日期类型enum和setenunmset 总结 数据类型分类 为什么要存在数据类型呢&a…