Python项目外星人入侵(终)记录分数

编程入门 行业动态 更新时间:2024-10-23 03:17:59

Python项目<a href=https://www.elefans.com/category/jswz/34/1758306.html style=外星人入侵(终)记录分数"/>

Python项目外星人入侵(终)记录分数

文章目录

  • Python项目外星人入侵(终)记录分数
    • 添加Play按键
      • 使游戏进入非活跃状态
      • 创建Button类
      • 在屏幕上绘制按钮
    • 使用按钮开始游戏
      • 重置游戏
      • 将Play按钮切换至非活跃状态
      • 隐藏游戏光标
    • 随着游戏进度不断地提高等级
      • 修改速度设置
      • 在游戏结束时重置速度
    • 分数记录
      • 显示得分
      • 创建记分牌
      • 在外星人被消灭时获得分数
      • 将消灭的每个外星人的点数都计入得分
      • 提高点数
      • 将得分圆整
      • 最高得分
      • 显示等级
      • 显示余下的飞船数
    • 总结
    • 全部代码
      • alien_invasion.py
      • ship.py
      • game_function.py
      • alien.py
      • settings.py
      • game_stats.py
      • bullet.py
      • button.py
      • scoreboard.py
    • 结语

Python项目外星人入侵(终)记录分数

在本章中,我们将结束整个游戏项目的开发,我们会添加一个play按钮,用来使玩家通过点击启动游戏或者在游戏结束后重启游戏。同时我们还将修改这个游戏,使其在玩家等级提高时加快游戏节奏,并实现一个记分系统。代码块中的--snip--表示代码不变的部分。

在本节进行讲解之前,我们首先做了如下修改,使游戏布局更加合理:

# settings.pyself.ship_speed = 1.5  # 设置飞船速度的初始值self.bullet_speed = 3  # 子弹的速度self.alien_speed = 0.5  # 外星人的移动速度为0.5

修改了飞船,子弹,外星人的速度。

# game_function.pydef get_number_rows(ai_settings, ship_height, alien_height):"""计算屏幕可以容纳多少行外星人"""# 计算屏幕有多少剩余空间available_space_y = ai_settings.screen_height - 4 * alien_height - ship_height# 计算屏幕有多少行number_rows = int(available_space_y / (2 * alien_height))return number_rows

修改了计算屏幕有多少剩余空间的公式,将3个外星人高度修改为4个。

添加Play按键

在本章节中,我们将添加一个play按键,它将在游戏开始前出现,并在游戏结束后再次出现,使玩家能重新开始游戏。

使游戏进入非活跃状态

当前,游戏在运行alien_invasion.py就开始了,接下来我们要使游戏处在非活跃状态,只有当我们点击Play按键后才进入活跃状态,能够开始游戏,为此我们需要修改game_stats.py的参数:

class GameStats:"""跟踪游戏的统计信息"""def __init__(self, ai_settings):self.ai_settings = ai_settingsself.reset_stats()self.game_active = False  # 游戏刚启动处于非活动状态

创建Button类

由于Pygame没有内置创建按钮的方法,因此,我们需要创建哟个Button类,用来创建带标签的实心矩形,我们可以用这些代码来创建任何按钮,首先我们创建一个Python文件,将其命名为button.py

# button.py
import pygame.ftfont    # 该模块能将文本渲染到屏幕上class Button:# message是我们要在按钮中显示的文本def __init__(self, ai_settings, screen, message):"""初始化按钮的属性"""self.screen = screenself.screen_rect = screen.get_rect()# 设置按钮的尺寸和其他属性self.width, self.height = 200, 50self.button_color = (0, 255, 0) # 按钮设置为绿色self.text_color = (255, 255, 255)   # 文本内容设置为白色self.font = pygame.font.SysFont(None, 48)   # None表示使用默认字体,48表示字号# 创建按钮的rect对象,并使其居中self.rect = pygame.Rect(0, 0, self.width, self.height)self.rect.center = self.screen_rect.center# 创建按钮的标签self.prep_msg(message)	# 将message渲染为图像,并使其在按钮中居中

新出现的代码的相关解读见注释,其中prep_msg()的代码如下,我们使用该函数将文本渲染为图像:

# button.pydef prep_msg(self, message):"""将message渲染为图像,并且使其在按钮中居中"""self.msg_image = self.font.render(message, True, self.text_color, self.button_color)	# 文本转换为图像self.msg_image_rect = self.msg_image.get_rect()self.msg_image_rect.center = self.rect.center

方法prep_msg(),接收实参self,以及要渲染成图像的文本message。调用方法font.render()将存储在message的文本转换为图像,这个方法接收一个布尔实参,该实参指定开启还是关闭反锯齿(反锯齿让文本边缘更加光滑)功能。

最后,我们创建方法draw_button()方法,通过调用它可以将按钮显示在屏幕上。

# button.pydef draw_button(self):"""先绘制一个按钮,再绘制文本"""self.screen.fill(self.button_color, self.rect)  # 绘制按钮self.screen.blit(self.msg_image, self.msg_image_rect)   # 绘制文本

fill()方法是在指定的位置,用指定的颜色填充块

blit()方法是在指定的地方绘制图像。

在屏幕上绘制按钮

我们使用Button类来创建一个Play按钮,由于我们只需要一个Play按钮,因此我们直接在alien_invasion.py中创建它:

# alien_invasion.py
from button import Button
--snip--play_button = Button(ai_settings, screen, "Play")	# 创建一个Button类的实例
--snip--gf.update_screen(ai_settings, screen, ship, aliens, bullets, play_button)run_geme()

在这里我们创建了一个Button类的实例,之后将实例传递给update_screen(),以便于在屏幕更新时显示按钮,接下来我们修改update_screen()函数:

# game_function.pydef update_screen(ai_settings, screen, stats, ship, aliens, bullets, play_button):"""更新屏幕上的图像,并且切换新屏幕"""screen.fill(ai_settings.bg_color)  # 用指定的颜色填充屏幕for bullet in bullets:bullet.draw_bullet()ship.blitme()  # 将飞船显示在屏幕中aliens.draw(screen)  # 让外星人显示在屏幕中if not stats.game_active:   # 如果游戏处于不活跃状态就打印开始按钮play_button.draw_button()pygame.display.flip()  # 将最近绘制的屏幕显示出来

为了使Play按钮显示在所有其他屏幕元素上边,我们在绘制其他所有元素后再绘制该按钮,现在我们运行主函数后会在屏幕中央看到一个Play按钮。

使用按钮开始游戏

接下来我们要修改check_evernts()的代码,已经创建新的函数check_play_button()

# game_function.pydef check_events(ai_settings, screen, stats, play_button, ship, bullets):"""响应键盘和鼠标事件"""for event in pygame.event.get():if event.type == pygame.QUIT:  # 如果事件的类型是退出,相当于鼠标点击×sys.exit()elif event.type == pygame.KEYDOWN:  # 判断是否有按键按下check_keydown_events(event, ai_settings, screen, ship, bullets)elif event.type == pygame.KEYUP:  # 判断是否有按键松开check_keyup_events(event, ship)elif event.type == pygame.MOUSEBUTTONDOWN:  # 判断是否有鼠标按下mouse_x, mouse_y = pygame.mouse.get_pos()  # 返回按下的点的坐标check_play_button(stats, play_button, mouse_x, mouse_y)def check_play_button(stats, play_button, mouse_x, mouse_y):"""在玩家单击Play后开始游戏"""if play_button.rect.collidepoint(mouse_x, mouse_y):  # 判断鼠标单击的位置是否在按键内stats.game_active = True

无论玩家单击屏幕什么地方,Pygame都会检测一个MOUSEBUTTONDOWN(鼠标按下)事件,但是我们只希望这个游戏在玩家单击Play按键时做出响应,为此我们使用pygame.mouse.get_pos()函数,他返回包含点击位置的坐标的元组,之后我们将值传递给函数check_play_button()判断点击的位置是否是按钮内,如果是就开始游戏,否则什么也不会发生。

接下来我们在主函数中修改check_events()函数,并传递新的实参。

# alien_invasion.pygf.check_events(ai_settings, screen, stats, play_button, ship, bullets)

重置游戏

前边编写的代码只处理了玩家第一次单击Play按钮的情况,而没有处理游戏结束时候的情况,因为没有重置导致游戏结束的条件。接下来,我们通过设置使玩家每次单击Play按键时重置游戏,包括统计信息,删除现有的外星人,子弹并且创建一群新的,使飞船居中。

# game_function.pydef check_play_button(ai_settings, screen, stats, play_button, ship, aliens, bullets, mouse_x, mouse_y):"""在玩家单击Play后开始游戏"""if play_button.rect.collidepoint(mouse_x, mouse_y):  # 判断鼠标单击的位置是否在按键内# 重置游戏统计信息stats.rest_stats()stats.game_active = True# 清空外星人和子弹列表aliens.empty()bullets.empty()# 创建一群新的外星人并使其居中create_fleet(ai_settings, screen, ship, aliens)ship.center_ship()

在这里我们更新了check_play_button()函数,在这里我们传入了新的参数,在点击后给玩家提供三条新的飞船,删除现有的外星人和子弹群并且创建一群新的,最后将飞船居中。

接下来我们需要修改check_events()部分的代码,并使其能正确的传入参数。

# game_function.pydef check_events(ai_settings, screen, stats, play_button, ship, aliens, bullets):"""响应键盘和鼠标事件"""for event in pygame.event.get():if event.type == pygame.QUIT:  # 如果事件的类型是退出,相当于鼠标点击×sys.exit()elif event.type == pygame.KEYDOWN:  # 判断是否有按键按下check_keydown_events(event, ai_settings, screen, ship, bullets)elif event.type == pygame.KEYUP:  # 判断是否有按键松开check_keyup_events(event, ship)elif event.type == pygame.MOUSEBUTTONDOWN:  # 判断是否有鼠标按下mouse_x, mouse_y = pygame.mouse.get_pos()  # 返回按下的点的坐标check_play_button(ai_settings, screen, stats, play_button, ship, aliens, bullets, mouse_x, mouse_y)

同时,alien_invasion.py部分的代码也需要修改:

# alien_invasion.pygf.check_events(ai_settings, screen, stats, play_button, ship, aliens, bullets)

现在当我们点击Play按键时,游戏就会被正确的重置,我们想玩几次就玩几次。

将Play按钮切换至非活跃状态

当前使用Play按钮存在如下问题,那就是即便Play按钮不可见,玩家单击原来所在的区域时,游戏会重新开始,接下来我们通过设置成游戏处于不活跃状态时点击Play按键才有效来解决这个问题

def check_play_button(ai_settings, screen, stats, play_button, ship, aliens, bullets, mouse_x, mouse_y):"""在玩家单击Play后开始游戏"""# 判断鼠标单击的位置是否在按键内, 以及此时游戏状态是否活跃if play_button.rect.collidepoint(mouse_x, mouse_y) and not stats.game_active:# 重置游戏统计信息stats.reset_stats()stats.game_active = True# 清空外星人和子弹列表aliens.empty()bullets.empty()# 创建一群新的外星人并使其居中create_fleet(ai_settings, screen, ship, aliens)ship.center_ship()

隐藏游戏光标

为了能让玩家开始游戏,我们需要使光标可见,但是接下来我们希望当游戏处在非活动状态时光标不可见:

# game_function.pydef check_play_button(ai_settings, screen, stats, play_button, ship, aliens, bullets, mouse_x, mouse_y):"""在玩家单击Play后开始游戏"""# 判断鼠标单击的位置是否在按键内, 以及此时游戏状态是否活跃if play_button.rect.collidepoint(mouse_x, mouse_y) and not stats.game_active:# 隐藏光标pygame.mouse.set_visible(False)# 重置游戏统计信息stats.reset_stats()stats.game_active = True# 清空外星人和子弹列表aliens.empty()bullets.empty()# 创建一群新的外星人并使其居中create_fleet(ai_settings, screen, ship, aliens)ship.center_ship()

通过set_visible()函数里的参数为False,我们可以在游戏开始时隐藏鼠标,接下来,我们需要在游戏结束后重新显示光标。

# game_function.pydef ship_hit(ai_settings, stats, screen, ship, aliens, bullets):"""响应被外星人撞到的飞船"""if stats.ships_left > 0:# 将飞船数目减一stats.ships_left -= 1# 清空子弹和外星人列表aliens.empty()bullets.empty()# 创建一群新的外星人,并把飞船放到屏幕中央create_fleet(ai_settings, screen, ship, aliens)  # 创建新的外星人ship.center_ship()  # 将飞船移动到屏幕中央# 暂停sleep(0.5)else:stats.game_active = Falsepygame.mouse.set_visible(True)

在上述最后一行,我们在set_visible()函数中传入参数True,以便于当游戏进入非活跃状态后能够使鼠标再次出现,以便与玩家进行操作。

随着游戏进度不断地提高等级

当前将整群外星人都消灭干净后,游戏的难度并没有变化,接下来我们将给游戏增加难度:每当玩家将屏幕上的外资那个人都消灭干净后,加快游戏的节奏,使游戏更难。

修改速度设置

首先,我们重新组织Settings类,将其中的游戏设置分为静态和动态两个部分,其中静态设置是指不会随着游戏的进度而变化的设置。

# settings.py
class Settings:"""存储外星人入侵的有关的所有的类"""def __init__(self):"""初始化游戏静态设置"""# 屏幕设置self.screen_width = 1200  # 设置窗口宽度self.screen_height = 700  # 设置窗口高度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  # 将未消失的子弹限制为3颗# 外星人设置self.fleet_drop_speed = 10  # 外星人向下移动的速度# 以什么样的速度加快游戏节奏self.speed_up = 1.1self.initialize_dynamic_speed()def initialize_dynamic_speed(self):"""随着游戏的进行动态变化的量"""self.ship_speed = 1.5  # 设置速度的初始值self.bullet_speed = 3  # 子弹的速度self.alien_speed = 1  # 外星人的移动速度为0.5# fleet_direction为1时表示右移,为-1时表示左移self.fleet_direction = 1

我们在__init__()中初始化静态设置,同时增加了设置speed_up用来控制游戏节奏的加快速度。同时我们将一些随着游戏进行会变化的量放在函数initialize_dynamic_speed()中,每当游戏重新开始时我们都会重置这些变量。

接下来,我们在其中定义increase_speed()函数,用来提高速度:

# settings.pydef increase_speed(self):"""提高速度"""self.ship_speed *= self.speed_upself.bullet_speed *= self.speed_upself.alien_speed *= self.speed_up

在这个函数中,我们每次让飞船,子弹,外星人的速度提高为上一次的speed_up的倍数,当我们消灭完一群外星人后,我们会先调用该函数来加快游戏进度,再创建一群新的外星人:

def update_bullets(ai_settings, screen, ship, aliens, bullets):"""更新子弹的位置,并且删除已经消失的子弹"""bullets.update()# 在for循环中不应该修改列表或者编组的数目,这样会导致遍历缺失for bullet in bullets.copy():if bullet.rect.bottom <= 0:bullets.remove(bullet)check_bullet_alien_collisions(ai_settings, screen, ship, aliens, bullets)

在游戏结束时重置速度

每当玩家开始新的游戏时,我们都需要将变化了的设置还原成初始值。

# game_function.pydef check_play_button(ai_settings, screen, stats, play_button, ship, aliens, bullets, mouse_x, mouse_y):"""在玩家单击Play后开始新游戏"""# 判断鼠标单击的位置是否在按键内, 以及此时游戏状态是否活跃if play_button.rect.collidepoint(mouse_x, mouse_y) and not stats.game_active:# 重置游戏速度设置ai_settings.initialize_dynamic_speed()# 隐藏光标pygame.mouse.set_visible(False)# 重置游戏统计信息stats.reset_stats()stats.game_active = True# 清空外星人和子弹列表aliens.empty()bullets.empty()# 创建一群新的外星人并使其居中create_fleet(ai_settings, screen, ship, aliens)ship.center_ship()

现在游戏的游戏性功能基本开发完成,我们目前能通过飞船对外星人进行设计,并且能逐步增加游戏难度,接下来我们将开发计分系统。

分数记录

记下来我们将实现计分系统,以便于实时追踪玩家的得分,并显示最高分,当前等级,还有余下的飞船数目。

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

 # gamestats.pydef reset_stats(self):"""初始化游戏运行期间可能变化的信息"""# 统计游戏中剩余的飞船数目self.ships_left = self.ai_settings.ship_limitself.score = 0  # 统计游戏得分

为了每次在游戏开始时重置得分,我们在reset_stats()函数中重置得分信息。

显示得分

为了在屏幕上显示得分,我们首先创建一个新类ScoreBoard。就目前而言,这个类只显示得分,但是之后我们也用它来显示最高分,等级,还有余下的飞船数。

# scoreboard.pyimport pygame.ftfontclass Scoreboard:"""显示得分信息的类"""def __init__(self, ai_settings, screen, stats):"""初始化显示得分涉及的属性"""self.screen = screenself.screen_rect = screen.get_rect()self.ai_settings = ai_settingsself.stats = stats# 显示得分信息的字体设置self.text_color = (30, 30, 30)self.font = pygame.font.SysFont(None, 48)# 准备初始得分图像self.prep_score()

由于需要在屏幕上显示文本,因此我们需要先导入pygame.ftfont,接下来我们在方法prep_score()中将文本转换为图像:

# scoreboard.pydef prep_score(self):"""将得分渲染为图像"""score_str = str(self.stats.score)self.score_image = self.font.render(score_str, True, self.text_color, self.ai_settings.bg_color)# 将得分显示在右上角self.score_rect = self.score_image.get_rect()self.score_rect.rigtht = self.screen_rect.right - 20self.score_rect.top = 20

在上述方法中,我们先将数字值转换为字符串,再将字符串传递给render()函数渲染为图像,为了更加清楚的显示得分,我们向render()传递了屏幕背景色和文本颜色。

在这里我们将得分窗口放在屏幕的右上角,并在得分增大导致数字变宽时,让它向左延申。

接下来,我们创建方法show_score(),用来更好的渲染图象:

# scoreboard.pydef show_score(self):"""显示得分"""self.screen.blit(self.score_image, self.score_rect)

这个方法将得分图像显示到指定的位置。

创建记分牌

为了显示得分,我们在alien_invasion.py中创建一个ScoreBoard的实例:

# alien_invasion.py--snip--from scoreboard import Scoreboard--snip--stats = GameStats(ai_settings)sb = Scoreboard(ai_settings, screen, stats)--snip--gf.update_screen(ai_settings, screen, stats, sb, ship, aliens, bullets, play_button)run_game()

接下来我们在屏幕更新函数中调用打印计分板的函数,并传递相应的实参:

# game_funcrion.pydef update_screen(ai_settings, screen, stats, sb, ship, aliens, bullets, play_button):"""更新屏幕上的图像,并且切换新屏幕"""screen.fill(ai_settings.bg_color)  # 用指定的颜色填充屏幕for bullet in bullets:bullet.draw_bullet()ship.blitme()  # 将飞船显示在屏幕中aliens.draw(screen)  # 让外星人显示在屏幕中sb.show_score() # 打印计分信息if not stats.game_active:  # 如果游戏处于不活跃状态就打印开始按钮play_button.draw_button()pygame.display.flip()  # 将最近绘制的屏幕显示出来

在外星人被消灭时获得分数

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

# settings.pydef initialize_dynamic_speed(self):"""随着游戏的进行动态变化的量"""self.ship_speed = 1.5  # 设置飞船速度的初始值self.bullet_speed = 3  # 子弹的速度self.alien_speed = 0.5  # 外星人的移动速度为0.5self.alien_point = 50 # 每击败一个外星人获得的点数# fleet_direction为1时表示右移,为-1时表示左移self.fleet_direction = 1

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

接下来在check_bullet_alien_collisions()中,每当有外星人 被击落,都会更新得分

# game_function.pydef check_bullet_alien_collisions(ai_settings, screen, stats, sb, ship, aliens, bullets):"""响应子弹和外星人的碰撞,并在所有外星人全部死亡后生成新的外星人群"""# 检测是否有子弹击中了外星人,如果有,就删除子弹和外星人collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)if collisions:  # 击中外星人时增加得分stats.score += ai_settings.alien_pointsb.prep_score()if len(aliens) == 0:# 如果外星人被全部消灭了,就删除现有子弹,重新生成外星人bullets.empty()ai_settings.increase_speed()create_fleet(ai_settings, screen, ship, aliens)

在这里我们更新了check_bullet_alien_collisions()的定义,使其中包括了形参stats和sb,在子弹撞到外星人时,Pygame会返回一个字典,我们检查这个字典是否存在,如果就增加一个外星人的得分,然后我们用prep_score()来创建一幅显示最新得分的图像。

接下来我们修改update_bullets(),确保在函数间传递正确的实参

# game_function.py
def update_bullets(ai_settings, screen, stats, sb, ship, aliens, bullets):"""更新子弹的位置,并且删除已经消失的子弹"""bullets.update()# 在for循环中不应该修改列表或者编组的数目,这样会导致遍历缺失for bullet in bullets.copy():if bullet.rect.bottom <= 0:bullets.remove(bullet)check_bullet_alien_collisions(ai_settings, screen, stats, sb, ship, aliens, bullets)

同时我们还需要修改主循环中调用上述函数部分的代码:

 while True:gf.check_events(ai_settings, screen, stats, play_button, ship, aliens, bullets)if stats.game_active:ship.update()gf.update_bullets(ai_settings, screen, stats, sb, ship, aliens, bullets)gf.update_aliens(ai_settings, stats, screen, ship, aliens, bullets)gf.update_screen(ai_settings, screen, stats, sb, ship, aliens, bullets, play_button)

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

当前,我们的代码可能会遗漏了一些消灭的外星人,比如在一次循环中有两颗子弹击中了外星人,或者因为子弹更宽同时击中了多个外星人,在这个时候玩家只会获得一个外星人的分数,为了修复这个问题,我们调整检测碰撞的方式:

# game_function.pydef check_bullet_alien_collisions(ai_settings, screen, stats, sb, ship, aliens, bullets):"""响应子弹和外星人的碰撞,并在所有外星人全部死亡后生成新的外星人群"""# 检测是否有子弹击中了外星人,如果有,就删除子弹和外星人collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)if collisions:  # 击中外星人时增加得分for aliens in collisions.values():stats.score += ai_settings.alien_point * len(aliens)sb.prep_score()if len(aliens) == 0:# 如果外星人被全部消灭了,就删除现有子弹,重新生成外星人bullets.empty()ai_settings.increase_speed()create_fleet(ai_settings, screen, ship, aliens)

check_bullet_alien_collisions()中,与外星人碰撞的每个子弹都是字典中的一个键,而与子弹相关的值都是一个列表,我们遍历字典的值的列表,确保每个被消灭的外星人都计入得分。

提高点数

由于玩家每消灭一群外星人游戏都会变得更难,因此在处于较高的等级时,外星人的点数应该更高:

# settings.pyclass Settings:"""存储外星人入侵的有关的所有的类"""def __init__(self):--snip--self.speed_up = 1.1  # 以什么样的速度加快游戏节奏self.score_scale = 1.5  # 加快分数的速度--snipdef increase_speed(self):"""提高速度"""self.ship_speed *= self.speed_upself.bullet_speed *= self.speed_upself.alien_speed *= self.speed_upself.alien_point = int(self.alien_point * self.score_scale)# print(self.alien_point)   # 打印显示当前外星人的分数

在这里我们定义了分数按照1.5倍的速度增加,为了让计算后的点数为整数,我们这里用了函数int()

最后的print语句是为了能让我们每当提高一个等级时,能在终端窗口看到当前外星人的分值,在最后运行时,我们可以将这行代码注释掉。

将得分圆整

很多射击游戏都会将得分显示为10的倍数,因此我们这个小游戏也会遵循这个原则,并且我们还会设置数字的格式,在较大的数字中添加逗号来分割千分位,因此我们在prep_score()方法中增加以下代码

# scoreboard.pydef prep_score(self):"""将得分渲染为图像"""rounded_score = int(round(self.stats.score, -1))score_str = "{:,}".format(rounded_score)self.score_image = self.font.render(score_str, True, self.text_color, self.ai_settings.bg_color)

函数round()用来将小数精确到小数点后多少位,但是如果传递的是负数,那么它会精确到10,100,1000的整数倍等等。同时在下一行使用了字符串格式化指令,他让Python将数值转化为字符串时在其中插入逗号。

最高得分

接下来我们跟踪并且记录游戏的最高分:

# game_function.pydef __init__(self, ai_settings):self.ai_settings = ai_settingsself.reset_stats()self.game_active = False  # 游戏刚启动处于活动状态self.high_score = 0  # 在任何时候都不应该重置最高分

我们首先创建新的属性high_score,由于在任何情况下都不会重置最高分,所以我们在__init__函数中创建该属性。

接下来我们在scoreboard.py中增加显示最高分的函数,同时将其与显示当前分数区别开。

# scoreboard.pydef __init__(self, ai_settings, screen, stats):"""初始化显示得分涉及的属性"""--snip--# 准备初始得分图像self.prep_score()self.prep_high_score()

接下来我们在prep_high_score()方法中将最高分渲染为图像,并且采用和当前分数一样的输出格式

# scoreboard.pydef prep_high_score(self):"""将最高分转换为渲染的图像"""high_score = int(round(self.stats.high_score, -1))high_score_str = "{:,}".format((high_score))self.high_score_image = self.font.render(high_score_str, True, self.text_color, self.ai_settings.bg_color)# 将最高分放在屏幕中央self.high_score_rect = self.high_score_image.get_rect()self.high_score_rect.centerx = self.screen_rect.centerxself.high_score_rect.top = 20

该方法的主要功能是将最高分放在屏幕的顶部中央,并且采用了与显示当前得分一样的圆整手段,在此不再赘述。

接下来我们在show_score()方法中调用该方法,实现在屏幕上对最高分进行显示

# scoreboard.pydef show_score(self):"""显示得分"""self.screen.blit(self.score_image, self.score_rect) # 显示当前得分self.screen.blit(self.high_score_image, self.high_score_rect)   # 显示最高得分

为了检查是否诞生了新的最高得分,我们需要在game_function.py中添加新的函数check_high_score()

# game_functiondef check_high_score(stats, sb):"""检查是否诞生了最高分"""if stats.score > stats.high_score:stats.high_score = stats.scoresb.prep_high_score()

在这个函数中,我们通过比较当前的分和最高分,一旦当前得分超过最高分,我峨嵋你就修改最高分的值。为此,我们需要在每当有外星人被消灭后就调用该函数:

def check_bullet_alien_collisions(ai_settings, screen, stats, sb, ship, aliens, bullets):"""响应子弹和外星人的碰撞,并在所有外星人全部死亡后生成新的外星人群"""# 检测是否有子弹击中了外星人,如果有,就删除子弹和外星人collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)if collisions:  # 击中外星人时增加得分for aliens in collisions.values():stats.score += ai_settings.alien_point * len(aliens)sb.prep_score()check_high_score(stats, sb)if len(aliens) == 0:# 如果外星人被全部消灭了,就删除现有子弹,重新生成外星人bullets.empty()ai_settings.increase_speed()create_fleet(ai_settings, screen, ship, aliens)

字典存在的时候,我们根据消灭了多少外星人来更新得分,之后我们再调用check_high_score()函数,来判断是否需要更新最高分。

显示等级

为了在在游戏中显示玩家的等级,我们需要在GameStats类中添加一个表示当前等级的属性。同时,为了切薄每次开始新的游戏时都可以重置等级,我们在reset_stats()函数中初始化该属性。

# game_stats.pydef reset_stats(self):"""初始化游戏运行期间可能变化的信息"""# 统计游戏中剩余的飞船数目self.ships_left = self.ai_settings.ship_limitself.score = 0  # 统计游戏得分self.level = 1  # 统计游戏的等级信息

接下来为了能够在当前得分下方显示当前等级,我们在__init__方法中调用一个新的方法prep_level()用来把游戏等级渲染为图像。

# scoreboard.pyclass Scoreboard:"""显示得分信息的类"""def __init__(self, ai_settings, screen, stats):"""初始化显示得分涉及的属性"""--snip--# 准备显示当前得分和最高分的图像self.prep_score()self.prep_high_score()self.prep_level()

其中方法prep_level()的代码如下:

# scoreboard.pydef prep_level(self):"""将等级转换为图像"""self.level_image = self.font.render(str(self.stats.level), True, self.text_color, self.ai_settings.bg_color)# 将等级放在得分下方self.level_rect = self.level_image.get_rect()self.level_rect.right = self.score_rect.rightself.level_rect.top = self.score_rect.bottom + 10

这个方法将等级的数值渲染为图像,并且将图像放在当前得分的下方。

接下来,我们更新show_score()方法,来打印等级的值:

# scoreboard.pydef 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)

接下来我们需要更新下述函数,以便于每当我们消灭完一群外星人后将当前的等级提升一级:

# game-function
def check_bullet_alien_collisions(ai_settings, screen, stats, sb, ship, aliens, bullets):"""响应子弹和外星人的碰撞,并在所有外星人全部死亡后生成新的外星人群"""# 检测是否有子弹击中了外星人,如果有,就删除子弹和外星人collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)if collisions:  # 击中外星人时增加得分for aliens in collisions.values():stats.score += ai_settings.alien_point * len(aliens)sb.prep_score()check_high_score(stats, sb)if len(aliens) == 0:# 如果外星人被全部消灭了,就删除现有子弹,重新生成外星人bullets.empty()ai_settings.increase_speed()# 提高等级stats.level += 1sb.prep_level()create_fleet(ai_settings, screen, ship, aliens)

在上述函数中,如果我们将一群外星人都消灭完了,就将level的值加1,并且调用prep_level()方法,以便于能正常显示等级。

同时为了重新开始游戏时能够更新计分和等级图像,我们需要修改我们单击Play按键后的代码:

# game_function.py
def check_play_button(ai_settings, screen, stats, sb, play_button, ship, aliens, bullets, mouse_x, mouse_y):"""在玩家单击Play后开始新游戏"""# 判断鼠标单击的位置是否在按键内, 以及此时游戏状态是否活跃if play_button.rect.collidepoint(mouse_x, mouse_y) and not stats.game_active:# 重置游戏设置ai_settings.initialize_dynamic_speed()# 隐藏光标pygame.mouse.set_visible(False)# 重置游戏统计信息stats.reset_stats()stats.game_active = True# 重置记分牌图像sb.prep_score()sb.prep_high_score()sb.prep_level()# 清空外星人和子弹列表aliens.empty()bullets.empty()# 创建一群新的外星人并使其居中create_fleet(ai_settings, screen, ship, aliens)ship.center_ship()

在上述代码中我们在重置完游戏的统计信息后,重置记分牌的图像,由于我们需要调用scoreboard.py中的方法,因此我们需要在参数中传入该模块的实例。

同时我们需要修改check_events()的形参,向其中传入scoreboard模块的实例:

# game_function.py
def check_events(ai_settings, screen, stats, sb, play_button, ship, aliens, bullets):"""响应键盘和鼠标事件"""for event in pygame.event.get():if event.type == pygame.QUIT:  # 如果事件的类型是退出,相当于鼠标点击×sys.exit()elif event.type == pygame.KEYDOWN:  # 判断是否有按键按下check_keydown_events(event, ai_settings, screen, ship, bullets)elif event.type == pygame.KEYUP:  # 判断是否有按键松开check_keyup_events(event, ship)elif event.type == pygame.MOUSEBUTTONDOWN:  # 判断是否有鼠标按下mouse_x, mouse_y = pygame.mouse.get_pos()  # 返回按下的点的坐标check_play_button(ai_settings, screen, stats, sb, play_button, ship, aliens, bullets, mouse_x, mouse_y)

之后我们需要修改主函数中调用check_events()部分传递的形参

# alien.invasion.py while True:gf.check_events(ai_settings, screen, stats, sb, play_button, ship, aliens, bullets)if stats.game_active:ship.update()gf.update_bullets(ai_settings, screen, stats, sb, ship, aliens, bullets)gf.update_aliens(ai_settings, stats, screen, ship, aliens, bullets)gf.update_screen(ai_settings, screen, stats, sb, ship, aliens, bullets, play_button)

显示余下的飞船数

最后我们来显示玩家还剩余多少条飞船,但是这里我们将使用图片而不是数字。为此,我们在屏幕左上角绘制出来还有多少条飞船。

首先我们让Ship类继承Sprite,以便于我们能正确的创建飞船编组。

# ship.py
class Ship(Sprite):def __init__(self, ai_settings, screen):"""初始化飞船并且设置其初始位置"""super(Ship, self).__init__()--snip--

接下来,我们需要修改ScoreBoard,在其中创建一个可供显示的飞船编组,首先我们需要导入ship模块,并更新初始化方法

# scoreboard.py
import pygame.ftfont
from pygame.sprite import Group
from ship import Shipclass Scoreboard:"""显示得分信息的类"""def __init__(self, ai_settings, screen, stats):"""初始化显示得分涉及的属性"""--snip--# 准备显示当前得分和最高分的图像self.prep_score()self.prep_high_score()self.prep_level()self.prep_ships()

接下来,我们在prep_ship()方法中创建一个飞船编组,并使用该方法显示还剩余多少条飞船。

# scoreboard.pydef prep_ships(self):"""显示还剩下多少飞船"""self.ships = Group()for ship_number in range(self.stats.ships_left):ship = Ship(self.ai_settings, self.screen)ship.rect.x = 10 + ship_number * ship.rect.widthself.rect.y = 10self.ships.add(ship)

在这个方法中,我们创建了一个空的编组用来存储飞船的实例,为了填充这个编组,我们根据玩家还有多少艘飞船来循环相应的次数。在这个方法中,我们创建了一艘飞船,并且重新设置了其坐标,之后将其添加到编组中。

接下来我们需要开始绘制飞船了:

# scoreboard.pydef 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)

为了让玩家在游戏开始时知道他还有多少艘飞船,我们在开始游戏时调用上述函数

# game_function.py
def check_play_button(ai_settings, screen, stats, sb, play_button, ship, aliens, bullets, mouse_x, mouse_y):"""在玩家单击Play后开始新游戏"""--snip--stats.game_active = True# 重置记分牌图像sb.prep_score() # 重置当前分数sb.prep_high_score()    # 重置最高分数sb.prep_level() # 重置游戏等级sb.prep_ships() # 重置可用飞船数

接下来我们还需要在飞船被外星人撞到时调用prep_ships()方法

# game_function.py
def ship_hit(ai_settings, stats, sb, screen, ship, aliens, bullets):"""响应被外星人撞到的飞船"""if stats.ships_left > 0:stats.ships_left -= 1  # 将飞船数目减一sb.prep_ships()  # 更新记分牌# 清空子弹和外星人列表aliens.empty()bullets.empty()# 创建一群新的外星人,并把飞船放到屏幕中央create_fleet(ai_settings, screen, ship, aliens)  # 创建新的外星人ship.center_ship()  # 将飞船移动到屏幕中央# 暂停sleep(0.5)else:stats.game_active = Falsepygame.mouse.set_visible(True)

在这里我们传入了模块scoreboard的实例sb,并且在飞船数目减少时调用prep_ships()方法

同时我们需要修改下列函数,以及对应的函数调用,确保参数传递正确

def check_aliens_bottom(ai_settings, stats, sb, screen, ship, aliens, bullets):"""检查是否有外星人到达屏幕底端"""screen_rect = screen.get_rect()  # 读取屏幕的矩阵信息for alien in aliens:# 如果外星人的底部矩阵的坐标大于屏幕,就执行碰撞响应if alien.rect.bottom >= screen_rect.bottom:ship_hit(ai_settings, stats, sb, screen, ship, aliens, bullets)breakdef update_aliens(ai_settings, stats, sb, screen, ship, aliens, bullets):"""更新外星人的位置"""check_fleet_edges(ai_settings, aliens)aliens.update()# 检测外星人和飞船的碰撞if pygame.sprite.spritecollideany(ship, aliens):ship_hit(ai_settings, stats, sb, screen, ship, aliens, bullets)check_aliens_bottom(ai_settings, stats, sb, screen, ship, aliens, bullets)

最后我们在主函数中修改调用部分的代码,向其传递实参sb

# alien.invasion.pywhile True:gf.check_events(ai_settings, screen, stats, sb, play_button, ship, aliens, bullets)if stats.game_active:ship.update()gf.update_bullets(ai_settings, screen, stats, sb, ship, aliens, bullets)gf.update_aliens(ai_settings, stats, sb,screen, ship, aliens, bullets)gf.update_screen(ai_settings, screen, stats, sb, ship, aliens, bullets, play_button)

下图显示了完整的游戏计分系统,左上角指出了剩余的飞船数目

总结

项目外星人入侵的学习到此结束,在这个项目中学到了如火热在Pygame上绘制图像,如何响应鼠标,键盘按键,如果创建可视化的按钮等等,但是这个项目还有很多不完善的地方,比如每次重新运行项目,最高分都会还原成0,没有其他额外的功能,比如外星人也能发射子弹,然后控制飞船躲避等等,在后续的过程中,如果有机会会对项目进行优化。接下来主要学习图表绘制以及图像处理相关的知识。

全部代码

如下是在开篇所说的环境下可以成功运行的代码作为参照,总共包括九个模块

alien_invasion.py

# 主模块,alien_invasion.py
import pygame  # 包含了游戏开发所需的功能
from settings import Settings
from ship import Ship
import game_function as gf
from pygame.sprite import Group
from game_stats import GameStats
from button import Button
from scoreboard import Scoreboarddef run_game():"""初始化游戏并创建一个屏幕对象"""pygame.init()  # 进行初始化,检测工具包是否完整ai_settings = Settings()screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))  # 创建一个大小为800*600的窗口pygame.display.set_caption("外星人入侵")  # 设置屏幕名字play_button = Button(ai_settings, screen, "Play")stats = GameStats(ai_settings)sb = Scoreboard(ai_settings, screen, stats)ship = Ship(ai_settings, screen)  # 创建一艘飞船bullets = Group()  # 创建一个存储子弹的编组aliens = Group()  # 创建一个外星人编组gf.create_fleet(ai_settings, screen, ship, aliens)  # 创建外星人群# 开始游戏的主循环while True:gf.check_events(ai_settings, screen, stats, sb, play_button, ship, aliens, bullets)if stats.game_active:ship.update()gf.update_bullets(ai_settings, screen, stats, sb, ship, aliens, bullets)gf.update_aliens(ai_settings, stats, sb,screen, ship, aliens, bullets)gf.update_screen(ai_settings, screen, stats, sb, ship, aliens, bullets, play_button)run_game()  # 运行程序

ship.py

# 初始化飞船的相关类,ship.py
import pygame
from pygame.sprite import Spriteclass Ship(Sprite):def __init__(self, ai_settings, screen):"""初始化飞船并且设置其初始位置"""super(Ship, self).__init__()self.screen = screenself.moving_right = False  # 能否向右移动标志self.moving_left = False  # 能否向左移动标志self.ai_settings = ai_settings# 加载飞船图像,并且获取其外接矩形self.image = pygame.image.load("images/ship.bmp")self.rect = self.image.get_rect()  # 获取图像的大小属性并将其保存self.screen_rect = screen.get_rect()  # 获取屏幕的大小属性并将其保存# 将每艘新的飞船放到底部中央self.rect.centerx = self.screen_rect.centerx  # 获取屏幕的x轴的中点数据并赋值给rectself.rect.bottom = self.screen_rect.bottom  # 获取屏幕的底部位置数据并赋值给rect# 再飞船的属性center中存储小数self.center = float(self.rect.centerx)  # 设置一个存储小数的新属性def blitme(self):"""在指定位置绘制飞船"""self.screen.blit(self.image, self.rect)def update(self):"""检查标志的状态,如果标志为True就移动飞船"""# 更新飞船的center值而不是rect值if self.moving_right and self.rect.right < self.screen_rect.right:self.center += self.ai_settings.ship_speedif self.moving_left and self.rect.left > 0:self.center -= self.ai_settings.ship_speed# 根据self.center的值更新self.centerx的值self.rect.centerx = self.centerdef center_ship(self):"""让飞船在屏幕上居中"""self.center = self.screen_rect.centerx

game_function.py

# 关于游戏各种功能的函数,game_fuction.py
import sys  # 使用sys模块来退出游戏
import pygame  # 包含了游戏开发所需的功能
from bullet import Bullet
from alien import Alien
from time import sleepdef check_keydown_events(event, ai_settings, screen, ship, bullets):"""响应按键进行操作"""if event.key == pygame.K_RIGHT:  # 判断按下的是不是右箭头ship.moving_right = Trueelif event.key == pygame.K_LEFT:  # 判断按下的是不是左箭头ship.moving_left = Trueelif event.key == pygame.K_SPACE:  # 判断按下的是不是空格fire_bullet(ai_settings, screen, ship, bullets)elif event.key == pygame.K_q:  # 判断按下的是不是Q,如果是退出游戏sys.exit()def check_keyup_events(event, ship):"""响应松开按键"""if event.key == pygame.K_RIGHT:  # 判断松开的时右箭头ship.moving_right = Falseelif event.key == pygame.K_LEFT:  # 判断松开的是有箭头ship.moving_left = Falsedef check_events(ai_settings, screen, stats, sb, play_button, ship, aliens, bullets):"""响应键盘和鼠标事件"""for event in pygame.event.get():if event.type == pygame.QUIT:  # 如果事件的类型是退出,相当于鼠标点击×sys.exit()elif event.type == pygame.KEYDOWN:  # 判断是否有按键按下check_keydown_events(event, ai_settings, screen, ship, bullets)elif event.type == pygame.KEYUP:  # 判断是否有按键松开check_keyup_events(event, ship)elif event.type == pygame.MOUSEBUTTONDOWN:  # 判断是否有鼠标按下mouse_x, mouse_y = pygame.mouse.get_pos()  # 返回按下的点的坐标check_play_button(ai_settings, screen, stats, sb, play_button, ship, aliens, bullets, mouse_x, mouse_y)def check_play_button(ai_settings, screen, stats, sb, play_button, ship, aliens, bullets, mouse_x, mouse_y):"""在玩家单击Play后开始新游戏"""# 判断鼠标单击的位置是否在按键内, 以及此时游戏状态是否活跃if play_button.rect.collidepoint(mouse_x, mouse_y) and not stats.game_active:# 重置游戏设置ai_settings.initialize_dynamic_speed()# 隐藏光标pygame.mouse.set_visible(False)# 重置游戏统计信息stats.reset_stats()stats.game_active = True# 重置记分牌图像sb.prep_score()  # 重置当前分数sb.prep_high_score()  # 重置最高分数sb.prep_level()  # 重置游戏等级sb.prep_ships()  # 重置可用飞船数# 清空外星人和子弹列表aliens.empty()bullets.empty()# 创建一群新的外星人并使其居中create_fleet(ai_settings, screen, ship, aliens)ship.center_ship()def update_screen(ai_settings, screen, stats, sb, ship, aliens, bullets, play_button):"""更新屏幕上的图像,并且切换新屏幕"""screen.fill(ai_settings.bg_color)  # 用指定的颜色填充屏幕for bullet in bullets:bullet.draw_bullet()ship.blitme()  # 将飞船显示在屏幕中aliens.draw(screen)  # 让外星人显示在屏幕中sb.show_score()  # 打印计分信息if not stats.game_active:  # 如果游戏处于不活跃状态就打印开始按钮play_button.draw_button()pygame.display.flip()  # 将最近绘制的屏幕显示出来def update_bullets(ai_settings, screen, stats, sb, ship, aliens, bullets):"""更新子弹的位置,并且删除已经消失的子弹"""bullets.update()# 在for循环中不应该修改列表或者编组的数目,这样会导致遍历缺失for bullet in bullets.copy():if bullet.rect.bottom <= 0:bullets.remove(bullet)check_bullet_alien_collisions(ai_settings, screen, stats, sb, ship, aliens, bullets)# print(len(bullets)) # 显示还有多少个子弹,这个知识测试,运行时把这个语句删除可以降低内存def check_bullet_alien_collisions(ai_settings, screen, stats, sb, ship, aliens, bullets):"""响应子弹和外星人的碰撞,并在所有外星人全部死亡后生成新的外星人群"""# 检测是否有子弹击中了外星人,如果有,就删除子弹和外星人collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)if collisions:  # 击中外星人时增加得分for aliens in collisions.values():stats.score += ai_settings.alien_point * len(aliens)sb.prep_score()check_high_score(stats, sb)if len(aliens) == 0:# 如果外星人被全部消灭了,就删除现有子弹,重新生成外星人bullets.empty()ai_settings.increase_speed()# 提高等级stats.level += 1sb.prep_level()create_fleet(ai_settings, screen, ship, aliens)def fire_bullet(ai_settings, screen, ship, bullets):"""如果没有达到发射限制就发射一个子弹"""if len(bullets) < ai_settings.bullets_allowed:new_bullet = Bullet(ai_settings, screen, ship)  # 创建一个子弹的实例bullets.add(new_bullet)  # 将子弹实例放在编组中def get_number_rows(ai_settings, ship_height, alien_height):"""计算屏幕可以容纳多少行外星人"""# 计算屏幕有多少剩余空间available_space_y = ai_settings.screen_height - 4 * alien_height - ship_height# 计算屏幕有多少行number_rows = int(available_space_y / (2 * alien_height))return number_rowsdef get_number_alien_x(ai_settings, alien_width):"""计算每行可以容纳多少个外星人"""# 计算一行有多少个位置available_space_x = ai_settings.screen_width - 2 * alien_width# 计算一行能容纳多少个外星人number_alien_x = int(available_space_x / (2 * alien_width))return number_alien_xdef create_alien(ai_settings, screen, aliens, alien_number, row_number):"""创建一个外星人并放入当前行"""alien = Alien(ai_settings, screen)  # 创建一个外星人alien_width = alien.rect.width  # 获取一个外星人的宽度alien_height = alien.rect.heightalien.x = alien_width + 2 * alien_width * alien_number  # 设定每个外星人的初始位置alien.rect.x = alien.x# 确定每个外星人在垂直方向的位置alien.rect.y = alien_height + 2 * alien_height * row_numberaliens.add(alien)  # 将外星人添加到编组中def create_fleet(ai_settings, screen, ship, aliens):"""创建外星人群"""alien = Alien(ai_settings, screen)  # 创建一个外星人number_alien_x = get_number_alien_x(ai_settings, alien.rect.width)number_rows = get_number_rows(ai_settings, ship.rect.height, alien.rect.height)# 创建第一行外星人for row_number in range(number_rows):for alien_number in range(number_alien_x):create_alien(ai_settings, screen, aliens, alien_number, row_number)def check_fleet_edges(ai_settings, aliens):"""有外星人到达边缘时采取相应的措施"""for alien in aliens.sprites():if alien.check_edge():change_fleet_direction(ai_settings, aliens)breakdef change_fleet_direction(ai_settings, aliens):"""将所有外星人下移,并且改变他们的方向"""for alien in aliens.sprites():alien.rect.y += ai_settings.fleet_drop_speedai_settings.fleet_direction *= -1def ship_hit(ai_settings, stats, sb, screen, ship, aliens, bullets):"""响应被外星人撞到的飞船"""if stats.ships_left > 0:stats.ships_left -= 1  # 将飞船数目减一sb.prep_ships()  # 更新记分牌# 清空子弹和外星人列表aliens.empty()bullets.empty()# 创建一群新的外星人,并把飞船放到屏幕中央create_fleet(ai_settings, screen, ship, aliens)  # 创建新的外星人ship.center_ship()  # 将飞船移动到屏幕中央# 暂停sleep(0.5)else:stats.game_active = Falsepygame.mouse.set_visible(True)def check_aliens_bottom(ai_settings, stats, sb, screen, ship, aliens, bullets):"""检查是否有外星人到达屏幕底端"""screen_rect = screen.get_rect()  # 读取屏幕的矩阵信息for alien in aliens:# 如果外星人的底部矩阵的坐标大于屏幕,就执行碰撞响应if alien.rect.bottom >= screen_rect.bottom:ship_hit(ai_settings, stats, sb, screen, ship, aliens, bullets)breakdef update_aliens(ai_settings, stats, sb, screen, ship, aliens, bullets):"""更新外星人的位置"""check_fleet_edges(ai_settings, aliens)aliens.update()# 检测外星人和飞船的碰撞if pygame.sprite.spritecollideany(ship, aliens):ship_hit(ai_settings, stats, sb, screen, ship, aliens, bullets)check_aliens_bottom(ai_settings, stats, sb, screen, ship, aliens, bullets)def check_high_score(stats, sb):"""检查是否诞生了最高分"""if stats.score > stats.high_score:stats.high_score = stats.scoresb.prep_high_score()

alien.py

# 初始化外星人相关的模块,alien.py
import pygame
from pygame.sprite import Spriteclass Alien(Sprite):def __init__(self, ai_settings, screen):"""初始化外星人并设置其起始位置"""super().__init__()self.screen = screenself.ai_settings = ai_settings# 加载外星人图像并设置其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)  # 主要用于后边计算def blitme(self):"""在指定位置绘制外星人"""self.screen.blit(self.image, self.rect)def check_edge(self):"""如果外星人位于屏幕边缘,就返回True"""screen_rect = self.screen.get_rect()# 如果外星人的边缘大于等于屏幕的右边缘if self.rect.right >= screen_rect.right:return True# 如果外星人的边缘小于等于屏幕的左边缘,即坐标0elif self.rect.left <= 0:return Truedef update(self):"""向右移动外星人"""self.x += (self.ai_settings.alien_speed *self.ai_settings.fleet_direction)self.rect.x = self.x

settings.py

# 存储游戏相关的设置信息,settings.py
class Settings:"""存储外星人入侵的有关的所有的类"""def __init__(self):"""初始化游戏静态设置"""# 屏幕设置self.screen_width = 1200  # 设置窗口宽度self.screen_height = 700  # 设置窗口高度self.bg_color = (230, 230, 230)  # 设置背景颜色# 飞船设置self.ship_limit = 3  # 设置玩家最多的飞船数# 子弹设置self.bullet_width = 300  # 子弹的宽度self.bullet_height = 15  # 子弹的高度self.bullet_color = (60, 60, 60)  # 子弹的颜色self.bullets_allowed = 3  # 将未消失的子弹限制为3颗# 外星人设置self.fleet_drop_speed = 10  # 外星人向下移动的速度self.speed_up = 1.1  # 以什么样的速度加快游戏节奏self.score_scale = 1.5  # 加快分数的速度self.initialize_dynamic_speed()def initialize_dynamic_speed(self):"""随着游戏的进行动态变化的量"""self.ship_speed = 1.5  # 设置飞船速度的初始值self.bullet_speed = 3  # 子弹的速度self.alien_speed = 0.5  # 外星人的移动速度为0.5self.alien_point = 50  # 每击败一个外星人获得的点数# fleet_direction为1时表示右移,为-1时表示左移self.fleet_direction = 1def increase_speed(self):"""提高速度"""self.ship_speed *= self.speed_upself.bullet_speed *= self.speed_upself.alien_speed *= self.speed_upself.alien_point = int(self.alien_point * self.score_scale)# print(self.alien_point)   # 打印显示当前外星人的分数

game_stats.py

# 存储游戏统计信息的模块,game_stats.py
class GameStats:"""跟踪游戏的统计信息"""def __init__(self, ai_settings):self.ai_settings = ai_settingsself.reset_stats()self.game_active = False  # 游戏刚启动处于活动状态self.high_score = 0  # 在任何时候都不应该重置最高分def reset_stats(self):"""初始化游戏运行期间可能变化的信息"""# 统计游戏中剩余的飞船数目self.ships_left = self.ai_settings.ship_limitself.score = 0  # 统计游戏得分self.level = 1  # 统计游戏的等级信息

bullet.py

# 初始化子弹的模块,bullet.py
import pygame
from pygame.sprite import Spriteclass Bullet(Sprite):"""用来管理飞船发射子弹的类"""def __init__(self, ai_settings, screen, ship):"""在飞船所处的位置增加一个子弹对象"""super(Bullet, self).__init__()self.screen = screen# 在(0, 0)处创建一个表示子弹的矩形,再设置正确的位置。self.rect = pygame.Rect(0, 0, ai_settings.bullet_width,ai_settings.bullet_height)self.rect.centerx = ship.rect.centerx  # 将子弹的centerx设置为飞船的centerxself.rect.top = ship.rect.top  # 将子弹的top设置为飞船的top# 存储小数表示的子弹位置self.y = float(self.rect.y)  # 将子弹的y坐标按照小数存储self.color = ai_settings.bullet_color  # 设置子弹的颜色self.speed = ai_settings.bullet_speed  # 设置子弹的速度def update(self):"""向上移动子弹"""self.y -= self.speedself.rect.y = self.ydef draw_bullet(self):"""在屏幕上绘制子弹"""pygame.draw.rect(self.screen, self.color, self.rect)

button.py

# 初始化游戏按钮的相关信息,button.py
import pygame.ftfont  # 该模块能将文本渲染到屏幕上class Button:# message是我们要在按钮中显示的文本def __init__(self, ai_settings, screen, message):"""初始化按钮的属性"""self.screen = screenself.screen_rect = screen.get_rect()# 设置按钮的尺寸和其他属性self.width, self.height = 200, 50self.button_color = (0, 255, 0)  # 按钮设置为绿色self.text_color = (255, 255, 255)  # 文本内容设置为白色self.font = pygame.font.SysFont(None, 48)  # None表示使用默认字体,48表示字号# 创建按钮的rect对象,并使其居中self.rect = pygame.Rect(0, 0, self.width, self.height)self.rect.center = self.screen_rect.center# 创建按钮的标签self.prep_msg(message)def prep_msg(self, message):"""将message渲染为图像,并且使其在按钮中居中"""self.msg_image = self.font.render(message, True, self.text_color,self.button_color)    # 文本转换为图像self.msg_image_rect = self.msg_image.get_rect()self.msg_image_rect.center = self.rect.centerdef draw_button(self):"""先绘制一个按钮,再绘制文本"""self.screen.fill(self.button_color, self.rect)  # 绘制按钮self.screen.blit(self.msg_image, self.msg_image_rect)   # 绘制文本

scoreboard.py

# 统计分数并渲染为图像打印的模块,scoreboard.py
import pygame.ftfont
from pygame.sprite import Group
from ship import Shipclass Scoreboard:"""显示得分信息的类"""def __init__(self, ai_settings, screen, stats):"""初始化显示得分涉及的属性"""self.screen = screenself.screen_rect = screen.get_rect()self.ai_settings = ai_settingsself.stats = stats# 显示得分信息的字体设置self.text_color = (30, 30, 30)self.font = pygame.font.SysFont(None, 48)# 准备显示当前得分和最高分的图像self.prep_score()self.prep_high_score()self.prep_level()self.prep_ships()def prep_score(self):"""将得分渲染为图像"""rounded_score = int(round(self.stats.score, -1))score_str = "{:,}".format(rounded_score)self.score_image = self.font.render(score_str, True, self.text_color, self.ai_settings.bg_color)# 将得分显示在右上角self.score_rect = self.score_image.get_rect()self.score_rect.right = self.screen_rect.right - 20self.score_rect.top = 20def prep_high_score(self):"""将最高分转换为渲染的图像"""high_score = int(round(self.stats.high_score, -1))high_score_str = "{:,}".format((high_score))self.high_score_image = self.font.render(high_score_str, True, self.text_color, self.ai_settings.bg_color)# 将最高分放在屏幕中央self.high_score_rect = self.high_score_image.get_rect()self.high_score_rect.centerx = self.screen_rect.centerxself.high_score_rect.top = 20def prep_level(self):"""将等级转换为图像"""self.level_image = self.font.render(str(self.stats.level), True, self.text_color, self.ai_settings.bg_color)# 将等级放在得分下方self.level_rect = self.level_image.get_rect()self.level_rect.right = self.score_rect.rightself.level_rect.top = self.score_rect.bottom + 10def prep_ships(self):"""显示还剩下多少飞船"""self.ships = Group()for ship_number in range(self.stats.ships_left):ship = Ship(self.ai_settings, self.screen)ship.rect.x = 10 + ship_number * ship.rect.widthship.rect.y = 10self.ships.add(ship)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)

结语

如果有什么学习中的问题欢迎在评论区留言,看到都会回复,大家一起加油。

更多推荐

Python项目外星人入侵(终)记录分数

本文发布于:2023-07-28 17:49:15,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1267206.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:外星人   分数   项目   Python

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!