当前位置: 首页 > news >正文

外星人入侵-Python-三

武装飞船

开发一个名为《外星人入侵》的游戏吧!为此将使用 Pygame,这是一组功能强大而有趣的模块,可用于管理图形、动画乃至声音, 让你能够更轻松地开发复杂的游戏。通过使用Pygame来处理在屏幕上绘制图像 等任务,可将重点放在程序的高级逻辑上。

你将安装Pygame,再创建一艘能够根据用户输入左右移动和射击的飞船。在接下来的两章,你将创建一群作为射杀目标的外星人,并改进该游戏:限制可供玩家使用的飞船数,并且添加记分牌。

玩家控制一艘最初出现在屏幕底部中央的飞船。玩 家可以使用箭头键左右移动飞船,还可使用空格键射击。游戏开始时,一群外 星人出现在天空中,并向屏幕下方移动。玩家的任务是射杀这些外星人。玩家 将所有外星人都消灭干净后,将出现一群新的外星人,其移动速度更快。只要 有外星人撞到玩家的飞船或到达屏幕底部,玩家就损失一艘飞船。玩家损失三艘飞船后,游戏结束。

一.第三阶段

本章将结束游戏《外星人入侵》的开发。我们会添加一个Play 按钮,用于根据需要启动游戏以及在游戏结束后重启游戏,还会修改这个游 戏,使其随玩家等级提高而加快节奏,并实现一个记分系统。阅读本章后,你 将掌握足够多的知识,能够开始编写随玩家等级提高而加大难度以及显示得分的游戏。

1.1添加Play按钮

添加一个Play按钮,它在游戏开始前出现,并在游戏结束后再次出现,让玩家能够开始新游戏。

 def __init__(self, ai_game): """初始化统计信息。""" self.settings = ai_game.settings self.reset_stats() # 让游戏一开始处于非活动状态。 self.game_active = False
1.1.1创建 Button 类

由于Pygame没有内置创建按钮的方法,编写一个Button 类,用于创建带标 签的实心矩形。

import pygame.font
class Button:def __init__(self,ai_game,msg):                     #1"""初始化按钮的属性。"""self.screen=ai_game.screenself.screen_rect=self.screen.get_rect()# 设置按钮的尺寸和其他属性。self.width,self.height=200,50                   #2self.button_color=(0,255,0)self.text_color=(255,255,255)self.font=pygame.font.SysFont(None,48)          #3# 创建按钮的rect对象,并使其居中。              self.rect=pygame.Rect(0,0,self.width,self.height)self.rect.center=self.screen_rect.center# 按钮的标签只创建一次。self._prep_msg(msg)                              #4

导入模块pygame.font ,让Pygame能够将文本渲染到屏幕上。方法 __init__() 接受参数self 、对象ai_game 和msg ,其中msg是要在按钮中显示的文本(#1)。设置按钮的尺寸(#2),再通过设置button_color ,让按钮的rect 对象为亮绿色,并通过设置text_color 让文本为白色。

(#3)处,指定使用什么字体来渲染文本。实参None 让Pygame使用默认字体,而48 指定了文本的字号。为让按钮在屏幕上居中,创建一个表示按钮的rect 对象(#4),并将其center 属性设置为屏幕的center 属性。

Pygame处理文本的方式是,将要显示的字符串渲染为图像。在(#5)处,调用了 _prep_msg() 来处理这样的渲染。

button.py

        def _prep_msg(self,msg):"""将msg渲染为图像,并使其在按钮上居中。"""self.msg_image=self.font.render(msg,True,self.text_color, #1self.button_color)self.msg_image_rect=self.msg_image.get_rect()             #2self.msg_image_rect.center=self.rect.center

方法_prep_msg() 接受实参self 以及要渲染为图像的文本(msg )。调用 font.render() 将存储在msg 中的文本转换为图像,再将该图像存储在 self.msg_image 中(#1)。方法font.render() 还接受一个布尔实参,该 实参指定开启还是关闭反锯齿功能(反锯齿让文本的边缘更平滑)。余下的两个实 参分别是文本颜色和背景色。我们启用了反锯齿功能,并将文本的背景色设置为按 钮的颜色。(如果没有指定背景色,Pygame渲染文本时将使用透明背景。)

(#2)处,让文本图像在按钮上居中:根据文本图像创建一个rect ,并将其center 属性设置为按钮的center 属性。 最后,创建方法draw_button() ,用于将这个按钮显示到屏幕上:

创建方法draw_button() ,用于将这个按钮显示到屏幕上:

button.py

def draw_button(self): # 绘制一个用颜色填充的按钮,再绘制文本。 self.screen.fill(self.button_color, self.rect) self.screen.blit(self.msg_image, self.msg_image_rect)

调用screen.fill() 来绘制表示按钮的矩形,再调用screen.blit() 并向 它传递一幅图像以及与该图像相关联的rect ,从而在屏幕上绘制文本图像。

1.1.2在屏幕上绘制按钮

更新 import 语句:

alien_invasion.py

--snip-- 
from game_stats import GameStats 
from button import Button

alien_invasion.py

def __init__(self): --snip-- self._create_fleet() #创建Play按钮。 self.play_button = Button(self, "Play") 

alien_invasion.py

  def _update_screen(self): --snip-- self.aliens.draw(self.screen) # 如果游戏处于非活动状态,就绘制Play按钮。 if not self.stats.game_active: self.play_button.draw_button() pygame.display.flip()

为让Play按钮位于其他所有屏幕元素上面,在绘制其他所有游戏元素后再绘制这个 按钮,然后切换到新屏幕。将这些代码放在一个if 代码块中,让按钮仅在游戏出于 非活动状态时才出现。

1.1.3开始游戏

为在玩家单击Play按钮时开始新游戏,在_check_events() 末尾添加如下elif 代码块,以监视与该按钮相关的鼠标事件:

alien_invasion.py

def _check_events(self): """响应按键和鼠标事件。""" for event in pygame.event.get(): if event.type == pygame.QUIT: --snip-- elif event.type == pygame.MOUSEBUTTONDOWN: #1mouse_pos = pygame.mouse.get_pos()     #2self._check_play_button(mouse_pos)     #3

无论玩家单击屏幕的什么地方,Pygame都将检测到一个MOUSEBUTTONDOWN 事件 (#1),但我们只想让这个游戏在玩家用鼠标单击Play按钮时做出响应。为此,使 用了pygame.mouse.get_pos() ,它返回一个元组,其中包含玩家单击时鼠标的 坐标和 坐标(#2)。我们将这些值传递给新方法_check_play_button() (#3)

alien_invasion.py

def _check_play_button(self, mouse_pos): """在玩家单击Play按钮时开始新游戏。""" if self.play_button.rect.collidepoint(mouse_pos): self.stats.game_active = True 

使用了rect 的方法collidepoint() 检查鼠标单击位置是否在Play按钮的 rect 内。如果是,就将game_active 设置为True ,让游戏开始!

1.1.4重置游戏

为在玩家每次单击Play按钮时都重置游戏,需要重置统计信息、删除现有的外星人 和子弹、创建一群新的外星人并让飞船居中

alien_invasion.py

    def _check_play_button(self,mouse_pos):"""在玩家单击Play按钮时开始新游戏。"""if self.play_button.rect.collidepoint(mouse_pos):#重置游戏统计信息。self.stats.reset_stats()self.stats.game_active= True#清空余下的外星人和子弹。self.aliens.empty()self.bullets.empty()#创建一群新的外星人并让飞船居中。self._create_fleet()self.ship.center_ship()

重置游戏统计信息,给玩家提供三艘新飞船。接下来,将game_active 设置为True 。这样,这个方法的代码执行完毕后,游戏就将开始。清空编组 aliens 和bullets ,然后创建一群新的外星人并将飞船居中。

1.1.5将Play按钮切换到非活动状态、

存在一个问题:即便Play按钮不可见,玩家单击其所在的区域时,游戏依然会 做出响应。游戏开始后,如果玩家不小心单击了Play按钮所处的区域,游戏将重新 开始! 为修复这个问题,可让游戏仅在game_active 为False 时才开始:

alien_invasion.py

def _check_play_button(self, mouse_pos): """玩家单击Play按钮时开始新游戏。""" button_clicked = self.play_button.rect.collidepoint(mouse_pos) #1if button_clicked and not self.stats.game_active:              #2#重置游戏统计信息。 self.stats.

标志button_clicked 的值为True 或False (#1)。仅当玩家单击了Play按 钮 且 游戏当前处于非活动状态时,游戏才重新开始(#2)

1.1.6隐藏鼠标光标

alien_invasion.py

 def _check_play_button(self, mouse_pos): """在玩家单击Play按钮时开始新游戏。""" button_clicked = self.play_button.rect.collidepoint(mouse_pos) if button_clicked and not self.stats.game_active: --snip-- #隐藏鼠标光标。 pygame.mouse.set_visible(False) 

通过向set_visible() 传递False ,让Pygame在光标位于游戏窗口内时将其隐藏 起来。 游戏结束后,将重新显示光标,让玩家能够单击Play按钮来开始新游戏。

alien_invasion.py

 def _ship_hit(self): """响应飞船被外星人撞到。""" if self.stats.ships_left > 0: --snip-- else: self.stats.game_active = False pygame.mouse.set_visible(True)

1.2提高等级

当前,将整群外星人消灭干净后,玩家将提高一个等级,但游戏的难度没变。下面 来增加一点趣味性:每当玩家将屏幕上的外星人消灭干净后,都加快游戏的节奏, 让游戏玩起来更难。

1.2.1修改速度设置

首先重新组织Settings 类,将游戏设置划分成静态和动态两组。对于随着游戏进 行而变化的设置,还要确保在开始新游戏时进行重置。settings.py的方法 __init__() 如下: 

settings.py

def __init__(self): """初始化游戏的静态设置。""" #屏幕设置 self.screen_width = 1200 self.screen_height = 800 self.bg_color = (230, 230, 230) #飞船设置 self.ship_limit = 3       #子弹设置 self.bullet_width = 3 self.bullet_height = 15 self.bullet_color = 60, 60, 60 self.bullets_allowed = 3 #外星人设置 self.fleet_drop_speed = 10 # 加快游戏节奏的速度。 self.speedup_scale = 1.1           #1self.initialize_dynamic_settings() #2

(#1)处,添加设置speedup_scale ,用 于控制游戏节奏的加快速度:2表示玩家每提高一个等级,游戏的节奏就翻一倍;1 表示游戏节奏始终不变。将其设置为1.1能够将游戏节奏提高到足够快,让游戏既有 难度又并非不可完成。最后,调用initialize_dynamic_settings() 初始化 随游戏进行而变化的属性(#2)

settings.py

def initialize_dynamic_settings(self): """初始化随游戏进行而变化的设置。""" self.ship_speed = 1.5 self.bullet_speed = 3.0 self.alien_speed = 1.0 # fleet_direction为1表示向右,为-1表示向左。 self.fleet_direction = 1

这个方法设置飞船、子弹和外星人的初始速度。随着游戏的进行,将提高这些速 度。每当玩家开始新游戏时,都将重置这些速度。在这个方法中,还设置了 fleet_direction ,使得游戏刚开始时,外星人总是向右移动。不需要增大 fleet_drop_speed 的值,因为外星人移动的速度越快,到达屏幕底端所需的时 间越短。

为在玩家的等级提高时提高飞船、子弹和外星人的速度,编写一个名为 increase_speed() 的新方法:settings.py

 def increase_speed(self): """提高速度设置""" self.ship_speed *= self.speedup_scale self.bullet_speed *= self.speedup_scale self.alien_speed *= self.speedup_scale

在_check_bullet_alien_collisions() 中,在整群外星人都被消灭后调用 increase_speed() 来加快游戏的节奏

alien_invasion.py

 def _check_bullet_alien_collisions(self): --snip-- if not self.aliens: # 删除现有的子弹并创建一群新的外星人。 self.bullets.empty() self._create_fleet() self.settings.increase_speed()
1.2.2重置速度

每当玩家开始新游戏时,都需要将发生了变化的设置重置为初始值,否则新游戏开 始时,速度设置将为前一次提高后的值:

alien_invasion.py

def _check_play_button(self, mouse_pos): """在玩家单击Play按钮时开始新游戏。""" button_clicked = self.play_button.rect.collidepoint(mouse_pos) if button_clicked and not self.stats.game_active: #重置游戏设置。 self.settings.initialize_dynamic_settings() --snip-- 

1.3记分

实现一个记分系统,以实时跟踪玩家的得分,并显示最高得分、等级和余下的飞船数。

得分是游戏的一项统计信息,因此在GameStats 中添加一个score 属性:

game_stats.py

 class GameStats: --snip-- def reset_stats(self): """初始化随游戏进行可能变化的统计信息。""" self.ships_left = self.ai_settings.ship_limit self.score = 0

为在每次开始游戏时都重置得分,我们在reset_stats() 而不是__init__() 中 初始化score 。

1.3.1显示得分

为在屏幕上显示得分,首先创建一个新类Scoreboard 。

scoreboard.py

import pygame.fontclass Scoreboard:"""显示得分信息的类。"""def __init__(self,ai_game):                        #1"""初始化得分涉及的属性。"""         self.screen=ai_game.screenself.screen.rect=ai_game.screen.get_rect()self.settings=ai_game.settingsself.stats=ai_game.stats# 显示得分信息时使用的字体设置。self.text_color=(30,30,30)                     #2self.font=pygame.font.SysFont(None,48)         #3# 准备初始得分图像。self.prep_score()                              #4

由于Scoreboard 在屏幕上显示文本,首先导入模块pygame.font 。接下来,在 __init__() 中包含形参ai_game ,以便访问报告跟踪的值所需的对象 settings 、screen 和stats (#1)。然后,设置文本颜色(#2)并实例化 一个字体对象(#3)。 为将要显示的文本转换为图像,调用prep_score() (#4),其定义如下:

scoreboard.py

    def prep_score(self):"""将得分转换为渲染的图像。"""score_str=str(self.stats.score)                        #1self.score_image=self.font.render(score_str,True,self.text_color,self.settings.bg_color)#2# 将得分放在屏幕右上角。self.score_rect=self.score_image.get_rect()            #3self.score_rect.right=self.screen_rect.right-20        #4self.score_rect.top=20                                 #5

在prep_score() 中,将数值stats.score 转换为字符串(#1),再将这个字 符串传递给创建图像的render() (#2)。为在屏幕上清晰地显示得分,向 render() 传递屏幕背景色和文本颜色。 将得分放在屏幕右上角,并在得分增大导致数变宽时让其向左延伸。为确保得分始 终锚定在屏幕右边,创建一个名为score_rect 的rect (#3),让其右边缘与 屏幕右边缘相距20像素(#4),并让其上边缘与屏幕上边缘也相距20像素(#5)。 接下来,创建方法show_score() ,用于显示渲染好的得分图像:

scoreboard.py

    def show_score(self): """在屏幕上显示得分。""" self.screen.blit(self.score_image, self.score_rect) 
1.3.2创建记分牌

为显示得分,在AlienInvasion 中创建一个Scoreboard 实例。先来更新 import 语句:

alien_invasion.py

--snip-- 
from game_stats import GameStats 
from scoreboard import Scoreboard 
--snip-- 

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

alien_invasion.py

def __init__(self): --snip-- pygame.display.set_caption("Alien Invasion") #创建存储游戏统计信息的实例, #并创建记分牌。 self.stats = GameStats(self) self.sb = Scoreboard(self) --snip-- 

然后,在_update_screen() 中将记分牌绘制到屏幕上:

 def _update_screen(self): --snip-- self.aliens.draw(self.screen) #显示得分。 self.sb.show_score() #如果游戏处于非活动状态,就显示Play按钮。 --snip--

alien_invasion.py

def _update_screen(self): --snip-- self.aliens.draw(self.screen) #显示得分。 self.sb.show_score() # 如果游戏处于非活动状态,就显示Play按钮。 --snip-- 

在显示Play按钮前调用show_score() 。

1.3.3 在外星人被消灭时更新得分

为在屏幕上实时显示得分,每当有外星人被击中时,都更新stats.score 的值, 再调用prep_score() 更新得分图像。但在此之前,需要指定玩家每击落一个外星 人将得到多少分:

settings.py

 def initialize_dynamic_settings(self): --snip-- # 记分 self.alien_points = 50

随着游戏的进行,将提高每个外星人的分数。为确保每次开始新游戏时这个值都会 被重置,我们在initialize_dynamic_settings() 中设置它。

在_check_bullet_alien_collisions() 中,每当有外星人被击落时,都更新得分:

alien_invasion.py

 def _check_bullet_alien_collisions(self): """响应子弹和外星人发生碰撞。""" #删除彼此碰撞的子弹和外星人。 collisions = pygame.sprite.groupcollide( self.bullets, self.aliens, True, True) if collisions: self.stats.score += self.settings.alien_points self.sb.prep_score() --snip-- 

有子弹击中外星人时,Pygame返回一个字典(collisions )。我们检查这个字典 是否存在,如果存在,就将得分加上一个外星人的分数。接下来,调用 prep_score() 来创建一幅包含最新得分的新图像。

1.3.4重置得分

当前,仅在有外星人被射杀 之后 生成得分。这在大多数情况下可行,但从开始新游 戏到有外星人被射杀之间,显示的是上一次的得分。 为修复这个问题,可在开始新游戏时生成得分:

alien_invasion.py

 def _check_play_button(self, mouse_pos): --snip-- if button_clicked and not self.stats.game_active: --snip-- # 重置游戏统计信息。 self.stats.reset_stats() self.stats.game_active = True self.sb.prep_score() --snip-- 

开始新游戏时,我们重置游戏统计信息再调用prep_score() 。此时生成的记分牌 上显示的得分为零。

1.3.5将消灭的每个外星人都计入得分

当前的代码可能会遗漏一些被消灭的外星人。例如,如果在一次循环中,有两颗子 弹击中了外星人,或者因子弹较宽而同时击中了多个外星人,玩家将只能得到一个 外星人的分数。为修复这种问题,我们来调整检测子弹和外星人碰撞的方式。

在_check_bullet_alien_collisions() 中,与外星人碰撞的子弹都是字典 collisions 中的一个键,而与每颗子弹相关的值都是一个列表,其中包含该子弹 击中的外星人。我们遍历字典collisions ,确保将消灭的每个外星人都计入得分:

alien_invasion.py

def _check_bullet_alien_collisions(self): --snip-- if collisions: for aliens in collisions.values(): self.stats.score += self.settings.alien_points * len(aliens) self.sb.prep_score() 

如果字典collisions 存在,就遍历其中的所有值。别忘了,每个值都是一个列 表,包含被同一颗子弹击中的所有外星人。对于每个列表,都将其包含的外星人数 量乘以一个外星人的分数,并将结果加入当前得分。

1.3.6提高分数

鉴于玩家每提高一个等级,游戏都变得更难,因此处于较高的等级时,外星人的分 数应更高。

settings.py

  class Settings: """存储游戏《外星人入侵》的所有设置的类。""" def __init__(self): --snip-- # 加快游戏节奏的速度。 self.speedup_scale = 1.1 # 外星人分数的提高速度。 self.score_scale = 1.5                                       #1self.initialize_dynamic_settings() def initialize_dynamic_settings(self): --snip-- def increase_speed(self): """提高速度设置和外星人分数。""" self.ship_speed *= self.speedup_scale self.bullet_speed *= self.speedup_scale self.alien_speed *= self.speedup_scale self.alien_points = int(self.alien_points * self.score_scale) #2

定义了分数的提高速度,并称之为score_scale (#1)。较低的节奏加快 速度(1.1)让游戏很快变得极具挑战性,但为了让记分发生显著的变化,需要将分 数的提高速度设置为更大的值(1.5)。现在,在加快游戏节奏的同时,提高了每个 外星人的分数(#2)。为让分数为整数,使用了函数int() 。

1.3.7舍入得分

将得分显示为10的整数倍,下面让记分系统遵循这个原 则。我们还将设置得分的格式,在大数中添加用逗号表示的千位分隔符。在 Scoreboard 中执行这种修改:

scoreboard.py

 def prep_score(self): """将得分转换为渲染的图像。"""  rounded_score = round(self.stats.score, -1)          #1score_str = "{:,}".format(rounded_score)             #2self.score_image = self.font.render(score_str, True, self.text_color, self.settings.bg_color) --snip--

函数round() 通常让小数精确到小数点后某一位,其中小数位数是由第二个实参指 定的。然而,如果将第二个实参指定为负数,round() 将舍入到最近的10的整数 倍,如10、100、1000等。(#1)处的代码让Python将stats.score 的值舍入到最近的 10的整数倍,并将结果存储到rounded_score 中。

1.3.8最高得分

每个玩家都想超过游戏的最高得分纪录。下面来跟踪并显示最高得分,给玩家提供 要超越的目标。我们将最高得分存储在GameStats 中:\

game_stats.py

def __init__(self, ai_game): --snip-- # 任何情况下都不应重置最高得分。 self.high_score = 0 

scoreboard.py

def __init__(self, ai_game): --snip-- # 准备包含最高得分和当前得分的图像。 self.prep_score() self.prep_high_score() 

最高得分将与当前得分分开显示,因此需要编写一个新方法prep_high_score() ,用于准备包含最高得分的图像.

方法prep_high_score() 的代码如下:

scoreboard.py

    def prep_high_score(self):"""将最高得分转换为图像。"""high_score = round(self.stats.high_score, -1)                   #1high_score_str = "{:,}".format(high_score)self.high_score_image = self.font.render(high_score_str, True,  #2self.text_color, self.settings.bg_color)# 将最高得分放在屏幕顶部中央。self.high_score_rect = self.high_score_image.get_rect()self.high_score_rect.centerx = self.screen_rect.centerx         #3self.high_score_rect.top = self.score_rect.top                  #4

将最高得分舍入到最近的10的整数倍,并添加用逗号表示的千分位分隔符(#1)。 然后,根据最高得分生成一幅图像(#2),使其水平居中(#3),并将其top 属性设置为当前得分图像的top 属性(#4)

现在,方法show_score() 需要在屏幕右上角显示当前得分,并在屏幕顶部中央显 示最高得分:

scoreboard.py

 def show_score(self): """在屏幕上显示得分。""" self.screen.blit(self.score_image, self.score_rect) self.screen.blit(self.high_score_image, self.high_score_rect) 

为检查是否诞生了新的最高得分,在Scoreboard 中添加一个新方法 check_high_score() :

scoreboard.py

 def check_high_score(self): """检查是否诞生了新的最高得分。""" if self.stats.score > self.stats.high_score: self.stats.high_score = self.stats.score self.prep_high_score()

方法check_high_score() 比较当前得分和最高得分。如果当前得分更高,就更 新high_score 的值,并调用prep_high_score() 来更新包含最高得分的图像。

在_check_bullet_alien_collisions() 中,每当有外星人被消灭时,都需要 在更新得分后调用check_high_score() :

alien_invasion.py

 def _check_bullet_alien_collisions(self): --snip-- if collisions: for aliens in collisions.values(): self.stats.score += self.settings.alien_points * len(aliens) self.sb.prep_score() self.sb.check_high_score() --snip--

如果字典collisions 存在,就根据消灭了多少外星人更新得分,再调用 check_high_score() 。

1.3.9显示等级

为在游戏中显示玩家的等级,首先需要在GameStats 中添加一个表示当前等级的 属性。为确保每次开始新游戏时都重置等级,在reset_stats() 中初始化它:

game_stats.py

   def reset_stats(self): """初始化随游戏进行可能变化的统计信息。""" self.ships_left = self.settings.ship_limit self.score = 0 self.level = 1 

为了让Scoreboard 显示当前等级,在__init__() 中调用一个新方法 prep_level() :

scoreboard.py

 def __init__(self, ai_game): --snip-- self.prep_high_score() self.prep_level()

prep_level() 的代码如下:

scoreboard.py

 def prep_level(self): """将等级转换为渲染的图像。""" level_str = str(self.stats.level) self.level_image = self.font.render(level_str, True,    #1self.text_color, self.settings.bg_color) # 将等级放在得分下方。 self.level_rect = self.level_image.get_rect() self.level_rect.right = self.score_rect.right           #2self.level_rect.top = self.score_rect.bottom + 10       #3

方法prep_level() 根据存储在stats.level 中的值创建一幅图像(#1),并 将其right 属性设置为得分的right 属性(#2)。然后,将top 属性设置为比 得分图像的bottom 属性大10像素,以便在得分和等级之间留出一定的空间(#3)。 还需要更新show_score() :

scoreboard.py

 def show_score(self): """在屏幕上显示得分和等级。""" self.screen.blit(self.score_image, self.score_rect) self.screen.blit(self.high_score_image, self.high_score_rect) self.screen.blit(self.level_image, self.level_rect)

新增的代码行在屏幕上显示等级图像。 我们在_check_bullet_alien_collisions() 中提高等级并更新等级图像:

alien_invasion.py

 def _check_bullet_alien_collisions(self): --snip-- if not self.aliens: # 删除现有的子弹并新建一群外星人。 self.bullets.empty() self._create_fleet() self.settings.increase_speed() # 提高等级。 self.stats.level += 1 self.sb.prep_level() 

如果整群外星人都被消灭,就将stats.level 的值加1,并调用prep_level() 确保正确地显示了新等级。 为确保在开始新游戏时更新等级图像,还需在玩家单击按钮Play时调用 prep_level() : alien_invasion.py

 def _check_play_button(self, mouse_pos): --snip-- if button_clicked and not self.stats.game_active: --snip-- self.sb.prep_score() self.sb.prep_level() --snip--
1.3.10显示余下的飞船数

首先,需要让Ship 继承Sprite ,以便创建飞船编组:

ship.py

 import pygame from pygame.sprite import Sprite class Ship(Sprite):                            #1"""管理飞船的类。""" def __init__(self, ai_game): """初始化飞船并设置其起始位置。""" super().__init__()                      #2--snip-- 

这里导入了Sprite ,让Ship 继承Sprite (#1),并在__init__() 的开头 调用super() (#2)

接下来,需要修改Scoreboard ,以创建可供显示的飞船编组。下面是其中的 import 语句: scoreboard.py

import pygame.font 
from pygame.sprite import Group 
from ship import Ship 

鉴于需要创建飞船编组,导入Group 和Ship 类。 下面是方法__init__() : scoreboard.py

def __init__(self, ai_game): """初始化记录得分的属性。""" self.ai_game = ai_game self.screen = ai_game.screen --snip-- self.prep_level() self.prep_ships() 

在调用 prep_level() 后调用了prep_ships() 。 prep_ships() 的代码如下:

scoreboard.py

 def prep_ships(self): """显示还余下多少艘飞船。""" self.ships = Group()                               #1        for ship_number in range(self.stats.ships_left):   #2ship = Ship(self.ai_game) ship.rect.x = 10 + ship_number * ship.rect.width #3ship.rect.y = 10                                 #4self.ships.add(ship)                             #5

方法prep_ships() 创建一个空编组self.ships ,用于存储飞船实例(#1)。为填充这个编组,根据玩家还有多少艘飞船以相应的次数运行一个循环(#2)。在这个循环中,创建新飞船并设置其 坐标,让整个飞船编组都位于屏幕左 边,且每艘飞船的左边距都为10像素(#3)。还将 坐标设置为离屏幕上边缘10像 素,让所有飞船都出现在屏幕左上角(#4)。最后,将每艘新飞船都添加到编组 ships 中(#5)

scoreboard.py

 def show_score(self): """在屏幕上绘制得分、等级和余下的飞船数。""" self.screen.blit(self.score_image, self.score_rect) self.screen.blit(self.high_score_image, self.high_score_rect) self.screen.blit(self.level_image, self.level_rect) self.ships.draw(self.screen)

为在屏幕上显示飞船,对编组调用draw() 。Pygame将绘制每艘飞船。 为在游戏开始时让玩家知道自己有多少艘飞船,在开始新游戏时调用 prep_ships() 。这是在AlienInvasion 的_check_play_button() 中进行 的:

alien_invasion.py

   def _check_play_button(self, mouse_pos): --snip-- if button_clicked and not self.stats.game_active: --snip-- self.sb.prep_score() self.sb.prep_level() self.sb.prep_ships() --snip--

还要在飞船被外星人撞到时调用prep_ships() ,从而在玩家损失飞船时更新飞船 图像: alien_invasion.py

def _ship_hit(self): """响应飞船被外星人撞到。""" if self.stats.ships_left > 0: #将ships_left减1并更新记分牌。 self.stats.ships_left -= 1 self.sb.prep_ships() --snip--

这里在将ships_left 的值减1后调用prep_ships() 。这样每次损失飞船后,显 示的飞船数都是正确的。

相关文章:

外星人入侵-Python-三

武装飞船 开发一个名为《外星人入侵》的游戏吧!为此将使用 Pygame,这是一组功能强大而有趣的模块,可用于管理图形、动画乃至声音, 让你能够更轻松地开发复杂的游戏。通过使用Pygame来处理在屏幕上绘制图像 等任务,可将…...

JavaScript相关面试题

以下是150道JavaScript相关面试题及详细答案: JavaScript基础 1.JavaScript是什么? JavaScript是一种直译式脚本语言,主要用于网页开发,也可用于服务器端开发(如Node.js)。它是一种动态类型、弱类型、基于原…...

常见的数学模型

数学模型的基本原理 简单来说,数学模型就是用数学语言来描述现实世界中的现象或规律。它就像一个“翻译器”,把复杂的现实问题转化成我们可以用数学方法解决的问题。 核心思想: 简化现实:现实世界太复杂,模型会抓住最…...

计算机四级 - 数据库原理 - 第3章 「关系数据库系统概述」

3.1 关系数据库系统概述 关系数据模型的三大要素:关系数据结构、关系操作集合(一次一个集合)和关系完整性约束 1. 关系语言的特点是高度非过程化的, DBMS会自动帮用户选择存取路径,用户不需要依靠循环和递归完成数据的重复操作。…...

使用PHP进行自动化测试:工具与策略的全面分析

使用PHP进行自动化测试:工具与策略的全面分析 引言 随着软件开发的复杂性不断增加,自动化测试已成为确保软件质量的关键环节。PHP作为一种广泛使用的服务器端脚本语言,拥有丰富的生态系统和工具支持,使其成为自动化测试的理想选…...

discuz门户文章允许游客评论

discuz开启游客评论 1、进入后台,用户--用户组--系统用户组--游客--编辑 2、论坛相关 设置未允许发表回复 3、门户相关--文章评论字数(设置此用户组发表文章评论字数限制,设置为0将禁止此用户组发表评论) 4、验证游客回复 测试站 http://jinzhu.zhaowo.…...

AtCoder Beginner Contest 003(A - 社の給料、B -トランプ、C -プログラミング講座、D - 社の冬 )题目讲解

前言 又更新AtCoder Beginner Contes 的题目讲解啦!! 希望能给诸位带来帮助。 话不多说,开始讲解: A - 社の給料←题目翻译 为了解决这个问题,我们需要计算青木每月完成正好N个任务时的平均工资。通过分析,我们可以发现这个问题可以通过数学公式直接求解,而不需要复…...

代码随想录二刷|图论11

图论 一、基础知识 1 无向图 (1)度:一个顶点连n条边就度为n (2)权 加权无向图:有边长的无向图 (3)通道:两个顶点之间有一些边和点,并且没有重复的边 路…...

农资出入库登记本,农药化肥库存出入库软件,佳易王农资管理庄稼医院开单管理系统操作教程

一、概述 本实例以佳易王农资管理庄稼医院开单管理系统为例说明,其他版本可参考本实例。试用版软件资源可到文章最后了解,下载的文件为压缩包文件,请使用免费版的解压工具解压即可试用。 软件特点: 1、功能实用,操作简…...

串的KMP算法详解

KMP算法深度解析 一、从暴力匹配到智能跳转: 在文本编辑器的搜索功能中,当我们在百万字的文档中查找特定关键词时,传统暴力匹配算法的时间复杂度高达O(mn)。KMP算法通过独创的部分匹配表(Partial Match Table)&#x…...

软件测试之测试分类

1. 为什么要对软件测试进行分类 软件测试是软件⽣命周期中的⼀个重要环节,具有较⾼的复杂性,对于软件测试,可以从不同的⻆度 加以分类,使开发者在软件开发过程中的不同层次、不同阶段对测试⼯作进⾏更好的执⾏和管理测试 的分类⽅…...

机器学习 : 训练过程

文章目录 概要流程1 . 前向传播2 . 计算损失3 . 后向传播4 . 梯度下降 技术名词解释小结 【全文大纲】 : https://blog.csdn.net/Engineer_LU/article/details/135149485 概要 主要思想拟合数据 流程 1 . 前向传播 y func * (wxb) 2 . 计算损失 y - Y 3 . 后向传播 根据链式法…...

六十天前端强化训练之第二十天React Router 基础详解

欢迎来到编程星辰海的博客讲解 看完可以给一个免费的三连吗,谢谢大佬! 目录 一、核心概念 1.1 核心组件 1.2 路由模式对比 二、核心代码示例 2.1 基础路由配置 2.2 动态路由示例 2.3 嵌套路由实现 2.4 完整示例代码 三、关键功能实现效果 四、…...

如何在AVL树中高效插入并保持平衡:一步步掌握旋转与平衡因子 —— 旋转篇

文章目录 AVL树种旋转的规则右单旋右单旋代码左单旋左单旋代码左右双旋左右单旋的代码右左单旋右左单旋的代码 AVL树种旋转的规则 在AVL树中,旋转是为了保持树的平衡性。AVL树是一种自平衡的二叉搜索树,它要求每个节点的左右子树的高度差不能超过1。当插…...

C++Primer学习(7.1 定义抽象数据类型)

类的基本思想是数据抽象(data abstraction)和封装(encapsulation)。数据抽象是种依赖于接口(interface)和实现(implementation)分离的编程(以及设计)技术。类的接口包括用户所能执行的操作:类的实现则包括类的数据成员、负责接口实现的函数体以及定义类所需的各种私有函数。 封…...

Vue 3 Diff 算法深度解析:与 Vue 2 双端比对对比

文章目录 1. 核心算法概述1.1 Vue 2 双端比对算法1.2 Vue 3 快速 Diff 算法 2. 算法复杂度分析2.1 时间复杂度对比2.2 空间复杂度对比 3. 核心实现解析3.1 Vue 2 双端比对代码3.2 Vue 3 快速 Diff 代码 4. 性能优化分析4.1 性能测试数据4.2 内存使用对比 5. 使用场景分析5.1 Vu…...

启动桌面Docker提示虚拟服务未启动

在启动 Docker Desktop 时,可能会遇到以下提示: Docker Desktop - Virtual Machine Platform not enabled Virtual Machine Platform not enabled该错误通常是由于 Windows 未启用 “Virtual Machine Platform” 功能导致的,这是运行 Docker…...

【SpringBoot】实现登录功能

在上一篇博客中,我们讲解了注册页面的实现。在此基础上会跳转到登录页面,今天给大家带来的是使用 SpringBoot,MyBatis,Html,CSS,JavaScript,前后端交互实现一个登录功能。 目录 一、效果 二、…...

DataWhale 速通AI编程开发:(进阶篇)第3章 提示词(Prompts)配置项

学习网址:Datawhale-学用 AI,从此开始 3.1 Roo Code提示词配置了什么 众所周知,提示词(Prompt)是用户向大语言模型输入的一段文本,用于指导大语言模型生成符合用户要求的输出。在ai编程领域更是如此,提示…...

VUE中VNode(虚拟节点)是个啥?

用 JavaScript 生成 Virtual DOM(VNode) 在 Vue 中,Virtual DOM(虚拟 DOM)是一个用 JavaScript 对象表示真实 DOM 结构的抽象层。通过这种方式,Vue 可以通过比较 Virtual DOM 与真实 DOM 的差异来最小化更…...

力扣:3. 无重复字符的最长子串(滑动窗口)

3. 无重复字符的最长子串 - 力扣(LeetCode)3. 无重复字符的最长子串 - 给定一个字符串 s ,请你找出其中不含有重复字符的 最长 子串 的长度。 示例 1:输入: s "abcabcbb"输出: 3 解释: 因为无重复字符的最长子串是 "abc"…...

注解+AOP实现权限控制

注解与AOP实战:实现权限控制 在现代Java开发中,注解(Annotation)和面向切面编程(AOP)是两种强大的技术,它们能够帮助我们实现代码的解耦,提高代码的可读性和可维护性。本文将通过一…...

2.5 python接口编程

在现代软件开发的复杂生态系统中,不同系统、模块之间的交互协作至关重要。接口编程作为一种关键机制,定义了组件之间的通信规范与交互方式。Python 凭借其卓越的灵活性、丰富的库资源以及简洁易读的语法,在接口编程领域占据了重要地位&#x…...

睡不着运动锻炼贴士

在快节奏的现代生活中,失眠似乎已成为许多人的“夜间伴侣”。夜晚辗转反侧,白天精神不振,这样的恶性循环让许多人苦不堪言。其实,除了调整作息和饮食习惯,适当的运动也是改善睡眠的一剂良药。今天,就让我们…...

【Python入门】一篇掌握Python中的字典(创建、访问、修改、字典方法)【详细版】

🌈 个人主页:十二月的猫-CSDN博客 🔥 系列专栏: 🏀《Python/PyTorch极简课》_十二月的猫的博客-CSDN博客 💪🏻 十二月的寒冬阻挡不了春天的脚步,十二点的黑夜遮蔽不住黎明的曙光 目…...

深入理解 HTML 表单与输入

在网页开发的广袤领域中,HTML 表单如同搭建用户与服务器沟通桥梁的基石。它是收集用户输入信息的关键渠道,承载着交互的重任。今天,就让我们一同深入探索 HTML 表单与输入的奥秘。​ HTML 表单在文档中划定出一片独特的区域,这片…...

宝塔docker切换存储目录

1、 停止 Docker 服务 sudo systemctl stop docker2、迁移 Docker 数据目录 sudo mkdir -p /newpath/docker sudo rsync -avz /var/lib/docker/ /newpath/docker/3、修改 Docker 配置文件 vi /etc/docker/daemon.json 内容 {"data-root": "/newpath/docker&q…...

IPoIB驱动中RSS与TSS技术的深度解析:多队列机制与性能优化

在高速网络通信中,IP over InfiniBand(IPoIB) 是实现低延迟、高吞吐的关键技术之一。为了充分发挥多核处理器的性能潜力,IPoIB驱动通过 接收侧扩展(RSS) 和 发送侧扩展(TSS) 技术,实现了数据包处理的多队列并行化。本文结合源码实现与性能优化策略,深入解析其核心机制…...

目前人工智能的发展,判断10年、20年后的人工智能发展的主要方向,或者带动的主要产业

根据2025年的最新行业研究和技术演进趋势,结合历史发展轨迹,未来10-20年人工智能发展的主要方向及带动的产业将呈现以下六大核心趋势: 一、算力革命与底层架构优化 核心地位:算力将成为类似“新能源电池”的基础设施,…...

DeepSeek-prompt指令-当DeepSeek答非所问,应该如何准确的表达我们的诉求?

当DeepSeek答非所问,应该如何准确的表达我们的诉求?不同使用场景如何向DeepSeek发问?是否有指令公式? 目录 1、 扮演专家型指令2、 知识蒸馏型指令3、 颗粒度调节型指令4、 时间轴推演型指令5、 极端测试型6、 逆向思维型指令7、…...

并发编程面试题二

1、java线程常见的基本状态有哪些,这些状态分别是做什么的 (1)创建(New):new Thread(),生成线程对象。 (2)就绪(Runnable):当调用线程对象的sta…...

【NLP】 8. 处理常见词(Stopwords)的不同策略

处理常见词(Stopwords)的不同策略 在自然语言处理 (NLP) 和信息检索 (IR) 任务中,常见词(Stopwords) 是指在文本中频繁出现但通常对主要任务贡献较小的词,例如 “the”、“is”、“in”、“and” 等。这些…...

【Java基础】java中的lambda表达式

Java Lambda表达式深度解析:语法、简化规则与实战 前言 Java 8的Lambda表达式通过简化匿名内部类和引入函数式编程,极大提升了代码的简洁性和可读性。 一、Lambda表达式的核心语法 Lambda表达式由参数列表、->符号和表达式主体组成,其基…...

【RS】OneRec快手-生成式推荐模型

note 本文提出了一种名为 OneRec 的统一生成式推荐框架,旨在替代传统的多阶段排序策略,通过一个端到端的生成模型直接生成推荐结果。OneRec 的主要贡献包括: 编码器-解码器结构:采用稀疏混合专家(MoE)架构…...

DQN 玩 2048 实战|第一期!搭建游戏环境(附 PyGame 可视化源码)

视频讲解: DQN 玩 2048 实战|第一期!搭建游戏环境(附 PyGame 可视化源码) 代码仓库:GitHub - LitchiCheng/DRL-learning: 深度强化学习 2048游戏介绍,引用维基百科 《2048》在44的网格上进行。…...

练习题:87

目录 Python题目 题目 题目分析 代码实现 代码解释 列表推导式部分: 变量赋值和输出: 运行思路 结束语 Python题目 题目 使用列表推导式生成一个包含 1 到 100 中所有偶数的列表。 题目分析 本题要求使用 Python 的列表推导式生成一个包含 …...

二叉树的层序遍历(102)

102. 二叉树的层序遍历 - 力扣(LeetCode) 解法: /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* …...

NVMe集群:加速数据处理

随着大数据和云计算的快速发展,企业面临着前所未有的数据处理挑战。传统的存储技术和架构已经难以满足现代应用对高性能和低延迟的需求。在这种背景下,NVMe(Non-Volatile Memory Express)集群应运而生,它以其卓越的性能…...

JUC并发编程:共享模型之管程

一、共享带来的问题 (1)Java的体现 两个线程对初始值为 0 的静态变量一个做自增,一个做自减,各做 5000 次,结果是 0 吗? (2)问题分析 以上的结果可能是正数、负数、零。为什么呢…...

Java构造方法详解:从入门到实战

目录 一、什么是构造方法? 二、构造方法的作用 三、构造方法分类与使用 1. 默认构造方法 2. 有参构造方法 3. 构造方法重载 四、注意事项(避坑指南) 五、经典面试题解析 六、实战应用场景 七、总结 一、什么是构造方法? …...

Uniapp 字体加载问题(文件本地存储)

项目场景: 在最近公司开发一款小程序,但是小程序的文字需要用艺术字,就是那种不能用切图绕开的那种! 问题描述 我们在使用uni.loadfontface Api请求数据字体文件的时候总是会报错,就是那种网上也找不到解决方法的那种…...

HTML 新手入门:从零基础到搭建第一个静态页面(一)

开启 HTML 学习之旅 在互联网的广袤世界中,网页是我们与信息交互的主要窗口。而 HTML,作为构建网页的基石,就像是搭建房屋的砖块,是网页开发中不可或缺的基础。无论你是对网页开发充满好奇的小白,还是渴望系统学习前端…...

使用multiprocessing实现进程间共享内存

在 Python 中,可以使用多种方法来实现几个进程之间的通信。 简单消息传递:使用 multiprocessing.Queue 或 multiprocessing.Pipe。 共享简单数据:使用 multiprocessing.Value 或 multiprocessing.Array。 共享复杂数据:使用 multiprocessing.Manager。 进程间信号控制:使用…...

在IDEA中连接达梦数据库:详细配置指南

达梦数据库(DM Database)作为国产关系型数据库的代表,广泛应用于企业级系统开发。本文将详细介绍如何在IntelliJ IDEA中配置并连接达梦数据库,助力开发者高效完成数据库开发工作。 准备工作 1. 下载达梦JDBC驱动 访问达梦官方资…...

docker无法正常拉取镜像问题的解决

目录 1.前言 2.解决方案 1.前言 安装docker后拉取镜像,遇见了如下问题: Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connection (Client.Timeout exceeded whil…...

如何在保持安全/合规的同时更快地构建应用程序:DevOps 指南

随着敏捷思维方式的兴起,开发和 DevOps 团队都面临着持续的压力,他们需要以迭代方式缩短发布周期并加快部署速度,以满足不断增长的客户期望。随着这种对速度的追求越来越强烈,维护安全性和合规性标准的复杂性也随之增加。 当今 D…...

SQL Server查询优化

最常用,最有效的数据库优化方式 查询语句层面 避免全表扫描 使用索引:确保查询条件中的字段有索引。例如,查询语句 SELECT * FROM users WHERE age > 20,若 age 字段有索引,数据库会利用索引快速定位符合条件的记…...

iOS底层原理系列04-并发编程

在移动应用开发中,流畅的用户体验至关重要,而并发编程是实现这一目标的关键技术。本文将深入探讨iOS平台上的并发编程和多线程架构,帮助你构建高性能、响应迅速的应用程序。 1. iOS线程调度机制 1.1 线程本质和iOS线程调度机制 线程是操作…...

企业数字化转型数据治理解决方案(119页PPT)(文末有下载方式)

资料解读:企业数字化转型数据治理解决方案 详细资料请看本解读文章的最后内容。 在当今数字化时代,数据已经成为企业最宝贵的资产之一。然而,随着数据量的激增和数据来源的多样化,如何有效管理和利用这些数据成为了企业面临的一…...

git报错:“fatal:refusing to merge unrelated histories“

新建仓库,克隆本地项目到新仓库,首次同步本地已提交的代码到远程时,报错:"fatal:refusing to merge unrelated histories" 。 报错意思是:致命的:拒绝合并无关的历史。 一、问题背景&#xff…...