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

news2024/11/15 21:46:12

外星人入侵(五)

  • 1.项目回顾
  • 2.创建第一个外星人
    • 2.1 创建Alien类
    • 2.2 创建Alien实例
  • 3.创建一群外星人
    • 3.1 确定一行可容纳多少个外星人
    • 3.2 创建一行外星人
    • 3.3 重构_create_fleet()
    • 3.4 添加行

在游戏《外星人入侵》中添加外星人。我们将首先在屏幕上边缘附近添加一个外星人,再生成一群外星人。然后让这群外星人向两边和下面移动,并删除被子弹击中的外星人。最后,显示玩家拥有的飞船数量,并在玩家的飞船用完后结束游戏。

1.项目回顾

开发大型项目时,要在进入每个开发阶段之前回顾一下开发计划,搞清楚接下来要通过编写代码来完成哪些任务。本章涉及以下内容。

▲ 研究既有代码,确定实现新功能前是否要重构。
▲ 在屏幕左上角添加一个外星人,并指定合适的边距。
▲ 根据第一个外星人的边距和屏幕尺寸计算屏幕上可容纳多少个外星人。编写一个循环来创建一系列外星人,使其填满屏幕的上半部分。

▲ 让外星人群向两边和下方移动,直到外星人被全部击落、有外星人撞到飞船或有外星人抵达屏幕底端。如果整群外星人都被击落,将再创建一群外星人。如果有外星人撞到了飞船或抵达屏幕底端,将销毁飞船并再创建一群外星人。
▲ 限制玩家可用的飞船数量。当配给的飞船用完之后,游戏将结束。

我们将在实现功能的同时完善这个计划,但就目前而言,该计划已足够详尽。

在项目中添加新功能前,还应审核既有代码。每进入一个新阶段,项目通常会更复杂,因此最好对混乱或低效的代码进行清理。我们一直在不断重构,因此当前没有需要重构的代码。

2.创建第一个外星人

在屏幕上放置外星人与放置飞船类似。每个外星人的行为都由Alien类控制,我们将像创建Ship类那样创建这个类。出于简化考虑,也将使用位图来表示外星人。你可以自己寻找表示外星人的图像。这幅图像的背景为灰色,与屏幕背景色一致。请务必将选择的图像文件保存到文件夹images中。

在这里插入图片描述

2.1 创建Alien类

下面来编写Alien类并将其保存为文件alien.py:

alien.py

  import pygame
  from pygame.sprite import Sprite

  class Alien(Sprite):
      """表示单个外星人的类。"""

      def __init__(self, ai_game):
          """初始化外星人并设置其起始位置。"""
          super().__init__()
          self.screen = ai_game.screen

          # 加载外星人图像并设置其rect属性
          self.image = pygame.image.load('images/alien.bmp')
          self.rect = self.image.get_rect()

          # 每个外星人最初都在屏幕左上角附近。
❶         self.rect.x = self.rect.width
          self.rect.y = self.rect.height

          # 存储外星人的精确水平位置。
❷         self.x = float(self.rect.x)

除位置不同外,这个类的大部分代码与Ship类相似。每个外星人最初都位于屏幕左上角附近。将每个外星人的左边距都设置为外星人的宽度,并将上边距设置为外星人的高度(见❶),这样更容易看清。我们主要关心的是外星人的水平速度,因此精确地记录了每个外星人的水平位置(见❷)。

Alien类不需要一个在屏幕上绘制外星人的方法,因为我们将使用一个Pygame编组方法,自动在屏幕上绘制编组中的所有元素。

2.2 创建Alien实例

要让第一个外星人在屏幕上现身,需要创建一个Alien实例。这属于设置工作,因此将把这些代码放在AlienInvasion类的方法__init__()末尾。我们最终会创建一群外星人,涉及的工作量不少,因此将新建一个名为_create_fleet()的辅助方法。

在类中,方法的排列顺序无关紧要,只要按统一的标准排列就行。我们将把_create_fleet()放在_update_screen()前面,不过放在AlienInvasion类的任何地方其实都可行。首先,需要导入Alien类。

下面是alien_invasion.py中修改后的import语句:

alien_invasion.py

--snip--
from bullet import Bullet
from alien import Alien

下面是修改后的方法__init__():

alien_invasion.py

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

        self._create_fleet()

创建了一个用于存储外星人群的编组,还调用了接下来将编写的方法_create_fleet()。

下面是新编写的方法_create_fleet():

alien_invasion.py

    def _create_fleet(self):
        """创建外星人群。"""
        # 创建一个外星人。
        alien = Alien(self)
        self.aliens.add(alien)

在这个方法中,创建了一个Alien实例,再将其添加到用于存储外星人群的编组中。外星人默认放在屏幕左上角附近,对第一个外星人来说,这样的位置非常合适。

要让外星人现身,需要在_update_screen()中对外星人编组调用方法draw():

alien_invasion.py

    def _update_screen(self):
        --snip--
        for bullet in self.bullets.sprites():
            bullet.draw_bullet()
        self.aliens.draw(self.screen)

        pygame.display.flip()

对编组调用draw()时,Pygame将把编组中的每个元素绘制到属性rect指定的位置。方法draw()接受一个参数,这个参数指定了要将编组中的元素绘制到哪个surface上。图显示了在屏幕上现身的第一个外星人。

在这里插入图片描述

第一个外星人正确地现身了,下面来编写绘制一群外星人的代码。

3.创建一群外星人

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

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

为确定一行可容纳多少个外星人,来看看可用的水平空间有多大。屏幕宽度存储在settings.screen_width中,但需要在屏幕两边都留下一定的边距(将其设置为外星人的宽度)。因为有两个边距,所以可用于放置外星人的水平空间为屏幕宽度减去外星人宽度的两倍:

available_space_x = settings.screen_width – (2 * alien_width)

还需要在外星人之间留出一定的空间,不妨将其定为外星人的宽度。因此,显示一个外星人所需的水平空间为外星人宽度的两倍:一个宽度用于放置外星人,另一个宽度为外星人右边的空白区域。为确定一行可容纳多少个外星人,将可用空间除以外星人宽度的两倍。我们使用整除(floor division)运算符//,它将两个数相除并丢弃余数,让我们得到一个表示外星人个数的整数。

number_aliens_x = available_space_x // (2 * alien_width)

我们将在创建外星人群时使用这些公式。

注意 令人欣慰的是,在程序中执行计算时,无须在一开始确定公式是正确的,而是可以尝试运行程序,看看结果是否符合预期。即便是在最坏的情况下,也只是屏幕上显示的外星人太多或太少。随后可根据在屏幕上看到的情况调整计算公式。

3.2 创建一行外星人

现在可以创建整行外星人了。由于创建单个外星人的代码管用,我们重写_create_fleet()使其创建一行外星人:

alien_invasion.py

      def _create_fleet(self):
          """创建外星人群。"""
          # 创建一个外星人并计算一行可容纳多少个外星人。
          # 外星人的间距为外星人宽度。
❶         alien = Alien(self)
❷         alien_width = alien.rect.width
❸         available_space_x = self.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(self)
❺             alien.x = alien_width + 2 * alien_width * alien_number
              alien.rect.x = alien.x
              self.aliens.add(alien)

这些代码大多在前面详细介绍过。为放置外星人,需要知道外星人的宽度和高度,因此在执行计算前,创建一个外星人(见❶)。这个外星人不是外星人群的成员,因此没有将其加入编组aliens中。在❷处,从外星人的rect属性中获取外星人宽度,并将这个值存储到alien_width中,以免反复访问属性rect。在❸处,计算可用于放置外星人的水平空间以及其中可容纳多少个外星人。

接下来,编写一个循环,从零数到要创建的外星人数(见❹)。在这个循环中,创建一个新的外星人,并通过设置[插图]坐标将其加入当前行(见❺)。将每个外星人都往右推一个外星人宽度。接下来,将外星人宽度乘以2,得到每个外星人占据的空间(其中包括右边的空白区域),再据此计算当前外星人在当前行的位置。我们使用外星人的属性x来设置其rect的位置。最后,将每个新创建的外星人都添加到编组aliens中。

如果现在运行这个游戏,将看到第一行外星人,如图所示。

在这里插入图片描述

这行外星人在屏幕上稍微偏向了左边、这实际上是有好处的,因为后面将让外星人群往右移,触及屏幕边缘后稍微往下移,再往左移,依此类推。就像经典游戏《太空入侵者》,相比于只往下移,这种移动方式更为有趣。我们将让外星人群不断这样移动,直到所有外星人都被击落,或者有外星人撞上飞船或抵达屏幕底端。

3.3 重构_create_fleet()

倘若只需使用前面的代码就能创建外星人群,也许应该让_create_fleet()保持原样,但鉴于创建外星人群的工作还未完成,我们稍微整理一下这个方法。为此,添加辅助方法_create_alien(),并在_create_fleet()中调用它:

alien_invasion.py

    def _create_fleet(self):
        --snip--
        # 创建第一行外星人。
        for alien_number in range(number_aliens_x):
            self._create_alien(alien_number)

    def _create_alien(self, alien_number):
        """创建一个外星人并将其放在当前行。"""
        alien = Alien(self)
        alien_width = alien.rect.width
        alien.x = alien_width + 2 * alien_width * alien_number
        alien.rect.x = alien.x
        self.aliens.add(alien)

除self外,方法_create_alien()还接受另一个参数,即要创建的外星人的编号。该方法的代码与_create_fleet()相同,但在内部获取外星人宽度,而不是将其作为参数传入。这样重构后,添加新行进而创建整群外星人将更容易。

3.4 添加行

要创建外星人群,需要计算屏幕可容纳多少行,并将创建一行外星人的循环重复执行相应的次数。为计算可容纳的行数,要先计算可用的垂直空间:用屏幕高度减去第一行外星人的上边距(外星人高度)、飞船的高度以及外星人群最初与飞船之间的距离(外星人高度的两倍):

available_space_y = settings.screen_height – (3 * alien_height) – ship_height

这将在飞船上方留出一定的空白区域,给玩家留出射杀外星人的时间。

每行下方都要留出一定的空白区域,不妨将其设置为外星人的高度。为计算可容纳的行数,将可用的垂直空间除以外星人高度的两倍。我们使用整除,因为行数只能是整数。(同样,如果这样的计算不对,我们马上就能发现,继而将间距调整为合理的值。)

number_rows = available_space_y // (2 * alien_height)

知道可容纳多少行之后,便可重复执行创建一行外星人的代码了:

alien_invasion.py

      def _create_fleet(self):
          --snip--
          alien = Alien(self)
❶         alien_width, alien_height = alien.rect.size
          available_space_x = self.settings.screen_width - (2 * alien_width)
          number_aliens_x = available_space_x // (2 * alien_width)

          #计算屏幕可容纳多少行外星人。
          ship_height = self.ship.rect.height
❷         available_space_y = (self.settings.screen_height -
                                   (3 * alien_height) - ship_height)
          number_rows = available_space_y // (2 * alien_height)

          # 创建外星人群。for row_number in range(number_rows):
              for alien_number in range(number_aliens_x):
                  self._create_alien(alien_number, row_number)

      def _create_alien(self, alien_number, row_number):
          """创建一个外星人,并将其放在当前行。"""
          alien = Alien(self)
          alien_width, alien_height = alien.rect.size
          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
          self.aliens.add(alien)

需要知道外星人的宽度和高度,因此在❶处使用了属性size。该属性是一个元组,包含rect对象的宽度和高度。为计算屏幕可容纳多少行外星人,在计算available_space_x的代码后面添加了计算available_space_y的代码(见❷)。此处将计算公式用圆括号括起来,以便将代码分成两行,遵循每行不超过79字符的建议。

为创建多行外星人,使用了两个嵌套在一起的循环:一个外部循环和一个内部循环(见❸)。内部循环创建一行外星人,而外部循环从零数到要创建的外星人行数:Python将重复执行创建单行外星人的代码,重复次数为number_rows。

为嵌套循环,编写了一个新的for循环,并缩进了要重复执行的代码。(在大多数文本编辑器中,缩进代码块和取消缩进都很容易,详情请参阅附录B)。现在调用_create_alien()时,传递了一个表示行号的实参,将每行都沿屏幕依次向下放置。

在_create_alien()的定义中,需要一个用于存储行号的形参。在_create_alien()中,修改外星人的[插图]坐标(见❹)并在第一行外星人上方留出与外星人等高的空白区域。相邻外星人行的[插图]坐标相差外星人高度的两倍,因此将外星人高度乘以2,再乘以行号。第一行的行号为0,因此第一行的垂直位置不变,而其他行都沿屏幕依次向下放置。

如果现在运行这个游戏,将看到一群外星人,如图:

在这里插入图片描述

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

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

相关文章

RHCE中级项目

一、项目需求 1、在 bbs.example.com 主机上创建 Discuz 论坛,数据库服务器使用 db.example.com 主机的 bbs 数据库实例,该实例由 MySQL数据库软件提供服务。 2、在 ntp.example.com 主机上创建 NTP 服务,该服务由 Chronyd软件提供服务&…

GoLang网络编程:HTTP服务端之底层原理与源码分析——http.HandleFunc()、http.ListenAndServe()

一、启动 http 服务 import ("net/http" ) func main() {http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {w.Write([]byte("ping...ping..."))})http.ListenAndServe(":8999", nil) }在 Golang只需要几行代…

第三章 SSD存储介质:闪存 3.4

3.4 闪存数据完整性 可采用以下数据完整性的技术确保用户数据不丢失: (1)ECC纠错; (2)RAID数据恢复; (3)重读(Read Retry); &#xff…

C/C++指针从0到99(详解)

目录 一,指针的基础理解 二,指针的基本使用 三,为什么要用指针 四,指针与数组的联系 五,指针的拓展使用 1)指针数组 2)数组指针 3)函数指针 结构:返回类型 (*p)…

我国新能源汽车存量已突破1620万辆,登记数量创历史新高

根据公安部发布的最新数据,截至2023年6月底,全国的机动车数量达到4.26亿辆,其中汽车数量为3.28亿辆,新能源汽车数量为1620万辆。与此同时,机动车驾驶人口达到5.13亿人,其中汽车驾驶人口为4.75亿人。在2023年…

Zabbix监控软件 Linux外多平台监控【Windows JAVA SNMP】

在之前的博客中,已经介绍了zabbix的安装,配置,以及如何用zabbix监控Linux服务器。这篇博客则介绍zabbix监控的其他几种方式(Windows服务器 Java应用 SNMP)。 -------------------- Zabbix 监控 Windows 系统 ---------…

传统图像处理之图像美化——图像金字塔

代码实战:图像融合 我将肯巴.沃克和约翰.沃尔融合在了一起。 具体见注释。 import numpy as np import cv2 import scipy.ndimage as ndimageimg cv2.imread("1.png")img1 cv2.imread("2.png")imgcv2.resize(img,(192,192)) img1cv2.resiz…

JVM调优的相关命令

在进行JVM调试时,熟练使用Linux和Java命令是非常重要的,有利于我们快速发现问题,并进行解决。平时常用的命令有:Linux的Top、Top -Hp等;Java的Jps、Jinfo、Jstack、Jstat、Jmap、Jhat等。下面,我将逐一介绍…

【技能实训】Day01

文章目录 任务1 项目准备一、开发环境二、系统简介三、项目创建 任务2【任务2.1】菜单项设计及其测试【任务2.2】使用数组存储采集的数据【任务2.3】控制显示采集的数据 任务1 项目准备 一、开发环境 1.JDK8下载及其环境变量配置(JDK8以上版本) 2.IDE :Eclipse 或…

多边形连接器PolygonConnector

PolygonConnector将多个多边形连接在一起 PolygonConnectorTest用于测试PolygonConnector Polygon test_square; Polygon test_square_around

关于IIS安全设置http能访问https不能访问的解决方案

最近折腾IIS,发现网站的http能访问但是https不能访问。 我确认所有关于HTTPS的配置我都配置正确了,结果还是不能访问,一番折腾发现,服务器本身的防火墙和阿里云服务器的安全组规则不是一回事。改完防火墙也没有用,重要…

【前端】网页开发精讲与实战 HTML Day 1

🚀Write In Front🚀 📝个人主页:令夏二十三 🎁欢迎各位→点赞👍 收藏⭐️ 留言📝 📣系列专栏:前端 💬总结:希望你看完之后,能对你有…

OpenCV使用ellipse()函数来绘制一个椭圆

/*** void ellipse(* InputOutputArray img, // 图像* Point center, // 椭圆原心* Size axes, // 椭圆x轴长度的一半,y轴长度的一半* double angle, // 椭圆旋转角度* double startAngle, // 起始角度* double endAngle, // 终止角度* const Scalar& color, // 椭圆颜色*…

整数序列(山东大学考研机试题)

整数序列 题目链接:3717. 整数序列 - AcWing题库 /* 纯暴力 刚开始以为挺难的结果是个暴力题 */ #include<iostream> using namespace std; int main() {int n;cin>>n;int f0;for(int i1;i<n/2;i){int sum 0;for(int ji;j<n/21;j){sumj;if(sumn){f1;for(int…

手机怎么设置四小时后提醒我休息,防止疲劳驾驶?

很多开车的人都知道&#xff0c;连续驾驶4个小时后需要至少休息20分钟&#xff0c;否则很容易出现疲劳驾驶的情况。但在开车的时候需要专注&#xff0c;很容易忘记按时休息&#xff0c;手机怎么设置四小时后提醒我休息&#xff0c;防止疲劳驾驶呢&#xff1f; 其实使用一款支持…

开放式耳机是什么意思?开放式耳机和封闭式耳机哪个好?

开放式耳机相比传统入耳式耳机&#xff0c;开放式耳机不会堵塞耳道&#xff0c;使用时可以开放双耳&#xff0c;不影响与他人的正常交流。还有很多朋友问开放式耳机和封闭式耳机哪个好&#xff1f;开放式耳机有哪些推荐好&#xff1f;等问题。 开放式耳机作为一种新兴的蓝牙耳…

String的hashCode为什么选择31作为乘子?

目录 hashCode()源码val[i] 是中文怎么办&#xff1f;如果是中文char的整数值是多少&#xff1f;注释中的计算方法为什么不用更大的数&#xff0c;比如101作为乘数&#xff1f;为啥用31了&#xff1f; hashCode()源码 先看下String类的源码&#xff0c;31出现在hashCode()方法…

Window操作系统---屏蔽蓝牙绝对音量

绝对音量定义&#xff1a; 关闭绝对音量方法&#xff1a;在系统注册表里修改 在运行输入框中输入&#xff1a;regedit 设置方法&#xff1a; 把DisableAbsoluteVolume的值改为&#xff1a;1

解释齐次坐标和投影几何

以前的翻译文章&#xff0c;存档。翻译自&#xff1a;https://www.tomdalling.com/blog/modern-opengl/explaining-homogenous-coordinates-and-projective-geometry/&#xff0c; 有改动 在本文中&#xff0c;我将尽可能简单地解释齐次坐标(即4D坐标)。在之前的文章中&#xf…

【MySQL练习及单表查询】

一、MySQL练习 一.创建表&#xff1a; 创建员工表employee&#xff0c;字段如下&#xff1a; id&#xff08;员工编号&#xff09; name&#xff08;员工名字&#xff09; gender&#xff08;员工性别&#xff09; salary&#xff08;员工薪资&#xff09; 二.插入数据 1&…