扩展了技能(一技能:高能子弹,即子弹与外星人碰撞一次后仍能使用而不消失,按住“1”键即可使用;二技能:超能子弹,即子弹宽度由3变为300,按住“2”键即可使用;被动技能:清屏,即外星人到达屏幕底部时,清空所有剩余外星人,被动技能仍有使用限制)并限制了其使用次数。技能在使用与数量检测方面均正常。随即为让玩家知道使用次数有或还剩多少,显示数量的位置及开始游戏前界面如图(为了能让飞船大体上不被遮挡,字号调的小,位置在下方两侧)。
遇到的问题是,原计划是每使用一次,可使用次数减1(技能数量检测方面就需要这样,所以这一步没问题),然后立即在界面中刷新一次可用次数并显示。可实际上直到技能次数用完技能不可用时,显示的还是技能数量的初始值,过程中没见任何变化。觉得应该是代码写的有地方有问题,但用了很长时间也没找出怎样达到目的的方法,便请各位帮忙解答。非常谢谢回答的各位!
以下是与问题有关的代码文件。
主程序:外星人入侵
import sys
import pygame
# 引用所需的其它模块
from time import sleep # 时间控制模块(time模块),sleep:使之暂停
# 引用与游戏成分或元素相关的类
from settings import Settings
from ship import Ship
from bullet import Bullet
from alien import Alien
from game_stats import GameStats
from button import Button
from scoreboard import Scoreboard
from use_skill import Use_skill
class AlienInvasion:
def __init__(self):
pygame.init() # 表明初始化的是与游戏元素相关的设置
# 初始化背景设置:
self.settings = Settings() # 先调用游戏设置
"""屏幕设置:pygame.display.set_mode((表示屏幕宽高的元组))"""
self.screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN) # screen的对象是一个surface,用于显示游戏元素
# 以上行代码意义:屏幕从原点(0, 0)开始布置,直至全屏【pygame.FULLSCREEN】
# 使用屏幕的rect属性width和height来更新对象settings
self.settings.screen_width = self.screen.get_rect().width
self.settings.screen_height = self.screen.get_rect().height
pygame.display.set_caption('外星人入侵') # 显示标题为:外星人入侵(Alien Invasion)
# 初始化游戏元素:
self.ship = Ship(self) # 调用飞船设置
self.bullets = pygame.sprite.Group() # 调用子弹设置,同时调用sprite.Group()对其进行批量管理
self.aliens = pygame.sprite.Group() # 调用外星人设置,同时调用sprite.Group()对其进行批量管理
self._create_fleet() # 创建一批新的外星人
self.stats = GameStats(self) # 调用游戏信息统计系统
self.sb = Scoreboard(self)
self.play_button = Button(self, 'Play') # 调用(开始)按钮
self.use_skill = Use_skill(self)
def run_game(self):
"""控制并运行游戏的方法"""
while True:
self._check_events() # 监视键盘和鼠标事件,并执行相关活动代码
if self.stats.game_active:
self.ship.update() # 执行相关代码,让飞船可移动
self._update_bullet() # 执行相关代码,使子弹出现后绘制更新
self._update_aliens() # 执行相关代码,使外星人数量与位置时刻发生着变化
self._update_screen() # 执行相关代码,使背景与飞船绘制更新
# 让子弹完全到达屏幕外面时消失(删除)
"""循环检查子弹是否完全到达屏幕外面"""
for bullet in self.bullets.copy(): # 方法copy():建立副本。注意:不能从for循环遍历的列表或元组中删除元素,只能删其副本
if bullet.rect.bottom <= 0: # 当子弹底部到达屏幕顶部时
self.bullets.remove(bullet) # 将其从编组中删除
def _check_events(self):
"""单独用来响应按键和鼠标及事件的方法"""
for event in pygame.event.get(): # 循环监视事件
# 以下两行代码之所以必要存在,是因为要使其右上角的“X”键生效
if event.type == pygame.QUIT: # 如果事件类型为“退出”
# 【练习】为防止每次重启游戏最高分都被重置,刻意保存数据:
self.sb.save_high_score(self.stats.high_score)
sys.exit() # 则退出程序
elif event.type == pygame.KEYDOWN: # 如果监视到有键按下(“有键按下”为事件类型)
self._check_keydown_events(event) # 调用按键按下方法
elif event.type == pygame.KEYUP: # 如果监视到有键有键弹起(“有键弹起”为事件类型)
self._check_keyup_events(event) # 调用按键弹起方法
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse_pos = pygame.mouse.get_pos()
self._check_play_button(mouse_pos)
def start_game(self):
self.stats.reset_stats()
self.stats.game_active = True
self.sb.prep_score()
self.sb.prep_level()
self.sb.prep_ships()
self.use_skill.prep_skill()
self.aliens.empty()
self.bullets.empty()
self._create_fleet()
self.ship.center_ship()
def _check_play_button(self, mouse_pos):
button_clicked = self.play_button.rect.collidepoint(mouse_pos)
if button_clicked and not self.stats.game_active:
self.settings.initialize_dynamic_settings()
self.start_game()
pygame.mouse.set_visible(False)
def _check_keydown_events(self, event):
"""单独用来监视按下按键名称的方法"""
if event.key == pygame.K_RIGHT: # 如果监视到的按下按键为右方向键
self.ship.moving_right = True # (允许)飞船向右移动
elif event.key == pygame.K_LEFT: # 如果监视到的按下按键为左方向键
self.ship.moving_left = True # (允许)飞船向左移动
elif event.key == pygame.K_SPACE: # 如果监视到的按下按键为空格键
self._fire_bullet() # 飞船开火
elif event.key == pygame.K_q: # 如果监视到的按下按键为“q”键
# 【练习】为防止每次重启游戏最高分都被重置,刻意保存数据:
self.sb.save_high_score(self.stats.high_score)
sys.exit() # 退出程序
# 【练习】按P键开始新游戏:
elif event.key == pygame.K_p:
self.start_game()
pygame.mouse.set_visible(False)
# 按下键允许使用技能:
elif event.key == pygame.K_1:
self.skill1_time()
elif event.key == pygame.K_2:
self.skill2_time()
def _check_keyup_events(self, event):
"""单独用来监视弹起按键名称的方法"""
if event.key == pygame.K_RIGHT: # 如果监视到的弹起按键为右方向键
self.ship.moving_right = False # 飞船停止向右移动(不允许飞船向右移动)
elif event.key == pygame.K_LEFT: # 如果监视到的弹起按键为左方向键
self.ship.moving_left = False # 飞船停止向左移动(不允许飞船向左移动)
# 按键弹起禁止使用技能:
elif event.key == pygame.K_1:
self.use_skill.use_skill1 = False
self.use_skill.prep_skill1()
elif event.key == pygame.K_2:
self.use_skill.use_skill2 = False
self.use_skill.prep_skill2()
def super_bullets(self):
if self.use_skill.use_skill2:
self.settings.bullet_width = 300
else:
self.settings.bullet_width = 3
def skill1_time(self):
if self.stats.skill1_number <= 0:
self.use_skill.use_skill1 = False
else:
self.use_skill.use_skill1 = True
self.stats.skill1_number -= 1
def skill2_time(self):
if self.stats.skill2_number <= 0:
self.use_skill.use_skill2 = False
else:
self.use_skill.use_skill2 = True
self.stats.skill2_number -= 1
def _update_screen(self):
"""更新屏幕上的图像,并切换到新屏幕"""
self.screen.fill(self.settings.bg_color)
"""每次循环都重再绘制飞船"""
self.ship.blitme()
"""绘制外星人"""
self.aliens.draw(self.screen) # 方法draw()接受一个参数,这个参数指定了要将编组中的元素绘制到哪个surface(即为self.screen)上
"""绘制所得分"""
self.sb.show_score()
self.use_skill.show()
"""调用二技能:超能子弹"""
self.super_bullets() # 对二技能实时检测,符合条件时将普通子弹转化为超能子弹
"""如果游戏处于非活动状态,就绘制play按钮"""
if not self.stats.game_active:
self.play_button.draw_button()
# 【练习】为防止每次重启游戏最高分都被重置, 刻意保存并读取后显示
self.sb.read_high_score()
self.sb.prep_high_score()
pygame.display.flip()
def _fire_bullet(self):
"""发射子弹"""
if len(self.bullets) < self.settings.bullet_allowed: # 这里是为了检查子弹数量是否到达限定数量。若未到达时
new_bullet = Bullet(self) # (继续)创建一个新子弹
self.bullets.add(new_bullet) # 并加入到其编组中
def _update_bullet(self):
"""更新屏幕上显示的子弹"""
self.bullets.update() # 调用子弹元素中的使其位置发生变化的方法update()
for bullet in self.bullets.sprites(): # 对子弹调用sprites()编组管理方法,以不断的
bullet.draw_bullet() # 在屏幕上绘制出子弹
pygame.display.flip() # 让最近的相关绘制(这里是子弹)可见
self._check_bullet_alien_collision() # 响应子弹和外星人的碰撞并执行相关代码
def _check_bullet_alien_collision(self):
"""单独响应子弹和外星人的碰撞""
""""sprite中groupcollide【汉语:编组碰撞】()方法:检查两个其编组是否有重叠部分,若有是否删除"""
"""用法:groupcollide(编组1, 编组2, 编组1是否删除, 编组2是否删除)"""
collisions = {}
if not self.use_skill.use_skill1:
collisions_1 = pygame.sprite.groupcollide(self.bullets, self.aliens, True, True)
for key, value in collisions_1.items():
collisions[key] = value
else:
collisions_0 = pygame.sprite.groupcollide(self.bullets, self.aliens, False, True)
for key, value in collisions_0.items():
collisions[key] = value
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()
if not self.aliens:
self.bullets.empty()
self._create_fleet()
self.settings.increase_speed()
self.stats.level += 1
self.sb.prep_level()
def _create_fleet(self):
alien = Alien(self)
alien_width, alien_height = alien.rect.size
available_space_x = self.settings.screen_width - (2 * alien_width)
number_alien_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_alien_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)
def _check_fleet_edges(self):
for alien in self.aliens.sprites():
if alien.check_edges():
self._check_fleet_direction()
break
def _check_fleet_direction(self):
for alien in self.aliens.sprites():
alien.rect.y += self.settings.fleet_drop_speed
self.settings.fleet_direction *= -1
def _update_aliens(self):
self._check_fleet_edges()
self.aliens.update()
if pygame.sprite.spritecollideany(self.ship, self.aliens):
self._ship_hit()
self._check_aliens_bottom()
def _check_aliens_bottom(self):
screen_rect = self.screen.get_rect()
for alien in self.aliens.sprites():
if alien.rect.bottom >= screen_rect.bottom:
if self.stats.skill0_number >= 0:
self.aliens.empty() # 这是被动技能表现
self.stats.skill0_number -= 1
self.use_skill.prep_skill0()
else:
self._ship_hit() # 如果被动技能用完,则响应飞船碰撞
break
def _ship_hit(self):
if self.stats.ship_live > 0:
self.stats.ship_live -= 1
self.sb.prep_ships()
self.aliens.empty()
self.bullets.empty()
self._create_fleet()
self.ship.center_ship()
sleep(1.0)
else:
self.stats.game_active = False
pygame.mouse.set_visible(True)
# 让程序运行:
if __name__ == '__main__':
ai = AlienInvasion() # 运行的程序
ai.run_game() # 循环运行
Use_skill.py>>>
import pygame.font
from settings import Settings
from game_stats import GameStats
from scoreboard import Scoreboard
class Use_skill:
def __init__(self, ai_game):
self.sb = Scoreboard(ai_game)
self.skill1_image = None
self.skill1_rect = None
self.skill2_image = None
self.skill2_rect = None
self.skill0_image = None
self.skill0_rect = None
self.ai_game = ai_game
self.settings = Settings()
self.stats = GameStats(self)
self.screen = self.ai_game.screen
self.screen_rect = self.screen.get_rect()
self.use_skill1 = False
self.use_skill2 = False
self.text_color = (30, 30, 30)
self.text1 = f'One skill:{self.stats.skill1_number}'
self.text2 = f'Two skill:{self.stats.skill2_number}'
self.text0 = f'Passive skill:{self.stats.skill0_number}'
self.font = pygame.font.SysFont(None, 24)
self.prep_skill()
def prep_skill1(self):
skill1 = self.text1
self.skill1_image = self.font.render(skill1, True, self.text_color, self.settings.bg_color)
self.skill1_rect = self.skill1_image.get_rect()
self.skill1_rect.left = self.screen_rect.left + 10
self.skill1_rect.bottom = self.screen_rect.bottom
def prep_skill2(self):
skill2 = self.text2
self.skill2_image = self.font.render(skill2, True, self.text_color, self.settings.bg_color)
self.skill2_rect = self.skill2_image.get_rect()
self.skill2_rect.left = self.screen_rect.left + self.skill1_rect.right + 20
self.skill2_rect.bottom = self.screen_rect.bottom
def prep_skill0(self):
skill0 = self.text0
self.skill0_image = self.font.render(skill0, True, self.text_color, self.settings.bg_color)
self.skill0_rect = self.skill0_image.get_rect()
self.skill0_rect.right = self.screen_rect.right - 20
self.skill0_rect.bottom = self.screen_rect.bottom
def prep_skill(self):
self.prep_skill1()
self.prep_skill2()
self.prep_skill0()
def show(self):
self.screen.blit(self.skill1_image, self.skill1_rect)
self.screen.blit(self.skill2_image, self.skill2_rect)
self.screen.blit(self.skill0_image, self.skill0_rect)
game_stats.py>>>
class GameStats:
def __init__(self, ai_game):
self.skill0_number = None
self.skill1_number = None
self.skill2_number = None
self.level = None # 个人认为无效操作
self.score = None # 个人认为无效操作
self.ship_live = None # 个人认为无效操作
self.settings = ai_game.settings
self.reset_stats()
self.game_active = False # 让游戏一开始处于非活动状态,以使play按钮有效
self.high_score = 0
def reset_stats(self):
self.ship_live = self.settings.ship_limit
self.score = 0
self.level = 1
self.skill1_number = 10
self.skill2_number = 8
self.skill0_number = 5