python3.7 + pygame1.9.3实现小游戏《外星人入侵》(五):计分

本小节首先在游戏画面中添加一个Play按钮,用于根据需要启动游戏,为此在game_stats.py中输入以下代码:

python 复制代码
class GameStats():
	def __init__(self,ai_settings):
	        # 初始化统计信息
	        self.ai_settings = ai_settings
	        self.reset_stats()
			#让游戏一开始处于非活动状态
	        self.game_active = False
	
	    def reset_stats(self):
	        # 初始化在游戏运行期间可能变化的统计信息
	        self.ship_left = self.ai_settings.ship_limit

现在,游戏一开始将处于非活动状态,等我们创建Play按钮后,玩家才能开始游戏。由于Pygame没有内置创建按钮的方法,为此新建一个Button类。
button.py

python 复制代码
import pygame.font
class Button():
	def __init__(self,ai_settings,screen,msg):
		"初始化按钮属性"
		self.screen = screen
		self.screen_rect = screen.get_rect()

		#设置按钮尺寸及其他属性
		self.width,self.height = 200,50
		self.button_color = (0,255,0)
        #指定颜色为亮绿
		self.text_color = (255,255,255)
        #指定字体None为默认,48字号
		self.font = pygame.font.SysFont(None,48)

		#创建按钮的rect对象,并使其居中
		self.rect = pygame.Rect(0,0,self.width,self.height)
		self.rect.center =self.screen_rect.center
        
    #按钮的标签只需创建一次
    def preg_msg(self,msg):
        "将要显示的文本msg渲染成图像,并使其在按钮上居中"
        self.msg_image = self.font.render(msg,True,self.text_color,self.button_color)
        self.msg_image_rect = self.msg_image.get_rect()
        self.msg_image_rect.center = self.rect.center
    def draw_button(self):
        #绘制一个用颜色填充的按钮,再绘制文本
        self.screen.fill(self.button_color,self.rect)
        self.screen.blit(self.msg_image,self.msg_image_rect)

在屏幕上绘制按钮:

alien_invasion.py

python 复制代码
--snip--
from gam_stats import GameStats
from button import Button
--snip--

def run_game():
    --snip--
    pygame.display.set_caption("Alien Invasion")
    
    #创建Play按钮
    play_button = Button(ai_settings,screen,"Play")
    --snip--
    
    
    # 游戏的主循环
    while True:
    --snip--
    gf.update_screen(ai_settings,screen,stats,ship,aliens,bullets,play_button)
    run_game()

成功显示按钮后,再考虑逐步添加按钮的功能:

  • 修改update_screen(),使得游戏处于非活动状态下才显示Play按钮,且按钮位于其他所有屏幕元素上面

game_functions.py

python 复制代码
def update_screen(ai_settings.screen,stats,ship,aliens,bullets,play_button):
    #更新屏幕上的图像,并切换到新屏幕
    --snip--
    
    #如果游戏处于非活动状态,就绘制Play按钮
    if not stats.game_active:
        play_button.draw_button()
        
    # 让最近绘制的屏幕可见
    pygame.display.flip()

开始游戏

  • 单击按钮时,检测MOUSEKEYDOWN事件,获取其位置(使用pygame.mouse.get_pos()返回其坐标值),以判断是否处于按钮的rect内,如果是,就将game_active设置为True,并重置统计信息、删除现有外星人和子弹、创建一批新的外星人群、让飞船居中,游戏就此开始。
  • 为观感体验,游戏开始后,需要隐藏光标的出现。同时给游戏新增开始游戏的快捷键P。

game_functions.py

python 复制代码
def check_events(ai_settings,screen,stats,play_button,ship,bullets):
    # 响应按键和鼠标事件
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            --snip--
        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)
            
def check_play_button(ai_settings,screen,stats,play_button,ship,aliens,bullets,mouse_x,mouse_y)::
    #在玩家单击Play按钮时开始游戏
    button_clicked = play_button.rect.collidepoint(mouse_x,mouse_y)
    if play_button.rect.collidepoint(mouse_x,mouse_y):
        stats.game_active = True

    #重置游戏统计信息
    if button_clicked 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()

--snip--
def ship_hit(ai_settings,stats,screen,ship,aliens,bullets):
    # 响应被外星人撞到的飞船
    if stats.ship_left > 0:
        --snip--
    else:
        stats.game_active = False
        #游戏结束后,重新显示光标
        pygame.mouse.set_visible(True)

alien_invasion.py

相应的,修改传入参数

python 复制代码
# 游戏的主循环
    while True:
    	gf.check_events(ai_settings,screen,stats,play_button,ship,bullets)

提高等级

  • 在消灭完整群外星人后,适当提高游戏难度,比如提高外星人的移动速度。
    settings.py
python 复制代码
def __init__(self):
        # 初始化游戏的静态设置
        # 屏幕设置
        self.screen_width = 1200
        self.screen_height = 800
        self.bg_color = (230,230,230)         # 设置背景色,由RGB指定,(230,230,230)为浅灰色
        self.ship_speed_factor = 1.5
        
        #飞船设置
        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
         #外星人点数的提高速度
        self.score_scale = 1.5
        #初始化随游戏进行而变化的属性
        self.initialize_dynamic_settings()

def increase_speed(self):
    #提高速度设置
    self.ship_speed_factor *= self.speedup_scale
    self.bullet_speed_factor *= self.speedup_scale
    self.alien_speed_factor *= self.speedup_scale
    
    #提高点数并设置为整数
    self.alien_points = int(self.alien_points * self.score_scale)

相应的,修改游戏速度调用代码
game_functions.py

python 复制代码
def check_bullet_alien_collisions(ai_settings,screen,ship,aliens,bullets):
    # 检查是否有子弹击中外星人,并进行删除
    collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)
    if len(aliens) == 0:
        # 删除现有的子弹,加快游戏节奏,并新建一群外星人
        bullets.empty()
        ai_settings.increase_speed()
        create_fleet(ai_settings,screen,ship,aliens)
--snip--
def check_play_button(ai_settings,screen,stats,play_button,ship,aliens,bullets,mouse_x,mouse_y)::
    #在玩家单击Play按钮时开始游戏
    button_clicked = play_button.rect.collidepoint(mouse_x,mouse_y)
    if play_button.rect.collidepoint(mouse_x,mouse_y):
        stats.game_active = True

    #重置游戏统计信息
    if button_clicked and not stats.game_active:   
        #重置游戏设置
        ai_settings.initialize_dynamic_settings()
        --snip--

现在,游戏更加具备了挑战性,每当玩家将屏幕上的外星人消灭干净后,游戏都将加快节奏。根据游戏体验找到最佳的settings.speedup_scale值。

  • 添加计分属性,实时跟踪玩家的得分,并显示最高得分、当前等级和余下的飞船数。

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

而鉴于在任何情况下都不会重置最高分,我们在__init__()中初始化high_score:
game_stas.py

python 复制代码
class GameStats():
    def __init__(self,ai_settings):
        # 初始化统计信息
        self.ai_settings = ai_settings
        self.reset_stats()
        self.game_active = True
        
        #在任何情况下都不应重置最高分
        self.high_score=0
        
    def reset_stats(self):
        #初始化随游戏进行可能变化的统计信息
        self.ship_left = self.ai_settings.ship_limit
        self.score = 0
        self.level=1

-为在屏幕上显示得分,创建一个新类Scoreboard:
scoreboard.py

python 复制代码
import pygame.font
from pygame.sprite import Group
from ship import Ship

class Scoreboard():
    #显示得分信息的类
    
    def __init__(self,ai_settings,screen,stats):
        #初始化显示得分涉及的属性
        self.screen = screen
        self.screen_rect = screen.get_rect()
        self.ai_settings = ai_settings
        self.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):
        #将得分转换为一副渲染的图像
        #将得分圆整到最近的10的整数倍
        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()
        #确保得分始终锚定在右边,让其右边缘与屏幕右边缘相距20像素
        self.score_rect.right = self.score_rect.right - 20
        #使上边缘与屏幕上边缘相距20像素
        self.score_rect.top = 20
        
    def 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.centerx
        self.high_score_rect.top = self.score_rect.top
        
    def 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.right
        #给得分和等级之间留出空间
        self.level_rect.top = self.score_rect.bottom + 10
        
    def prep_ships(self):
        #显示还余下多少飞船
        self.ships = Group()
        for ship_number in range(self.stats,ships_left):
            #创建一搜新飞船
            ship = Ship(self.ai_settings,self.screen)
            #让整个飞船编组都位于屏幕左边,且左边距都为10像素
            ship.rect.x = 10 + ship_number * ship.rect.width
            #y坐标设置为离屏幕上边缘10像素,让所有飞船与得分图像对齐
            ship.rect.y = 10
            #将每搜新飞船都添加到编组ships中
            self.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)
  • 为显示得分,更新alien_invasion.py
python 复制代码
# 游戏的主循环
    while True:
        gf.check_events(ai_settings,screen,stats,play_button,ship,aliens,bullets)
        
--snip--  
from scoreboard import Scoreboard        
--snip--
def run_game():
    --snip--
    #创建存储游戏统计信息的实例,并创建记分牌
    stats = GameStats(ai_settings)
    sb = Scoreboard(ai_settings,screen,stats)
    --snip--
    #游戏的主循环
    while True:
        --snip--
        gf.update_screen(ai_settings,screen,stats,sb,ship,aliens,bullets,play_button)
        
run_game()
  • 显示得分,并在外星人被消灭时更新得分
  • 提高速度

settings.py

python 复制代码
def initialize_dynamic_settings(self):
	--snip--
    #指定击落一个外星人的得分
    self.alien_points = 50

def increase_speed(self):
    #提高速度设置
    self.ship_speed_factor *= self.speedup_scale
    self.bullet_speed_factor *= self.speedup_scale
    self.alien_speed_factor *= self.speedup_scale
    
    #提高点数并设置为整数
    self.alien_points = int(self.alien_points * self.score_scale)
    #显示分数,注意:确认点数在不断增加后要删除该语句,以防影响性能及分散玩家注意力
    print(self.alien_points)
python 复制代码
import pygame
from pygame.sprite import Sprite

class Ship(Sprite):

    def __init__(self,ai_settings,screen):
        # 初始化飞船并设置其初始位置
        #导入Sprite,让ship继承
        super(Ship,self).__init__()
        --snip--
  • 确保开始游戏时充分调用
    game_functions.py
python 复制代码
--snip--
def check_events(ai_settings,screen,stats,sb,play_button,ship,aliens,bullets):
    # 响应按键和鼠标事件
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            --snip--
        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按钮时开始游戏
    button_clicked = play_button.rect.collidepoint(mouse_x,mouse_y)
    if play_button.rect.collidepoint(mouse_x,mouse_y):
        stats.game_active = True

    #重置游戏统计信息
    if button_clicked and not stats.game_active:   
        #重置游戏设置
        ai_settings.initialize_dynamic_settings()
        
        #重置记分图像
        sb.prep_score()
        sb.prep_high_score()
        sb.prep_level()
        sb.prep_ships()
        #隐藏光标
        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()

--snip--

def check_bullet_alien_collisions(ai_settings,screen,stats,sb,ship,aliens,bullets):
    # 检查是否有子弹击中外星人,并进行删除
    collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)
    if len(aliens) == 0:
        # 删除现有的子弹,加快游戏节奏,并新建一群外星人
        bullets.empty()
        ai_settings.increase_speed()
        #提高等级
        stats.level += 1
        sb.prep_level()
        
        create_fleet(ai_settings,screen,ship,aliens)
    
    #子弹撞到外星人时,返回一个字典collisions,更新得分
    if collisions:
        #考虑同时击中多外星人的情况
        for aliens in collisions.values():
            stats.score += ai_settings.alien_points
            sb.prep_score()
        check_high_score(stats,sb)
    
def update_bullets(ai_settings,screen,stats,sb,ship,aliens,bullets):
    # 更新子弹的位置,并删除已消失的子弹
    bullets.update()
    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)
     
def check_aliens_bottom(ai_settings,screen,stats,sb,ship,aliens,bullets):
    # 检查是否有外星人到达底端
    screen_rect = screen.get_rect()
    for alien in aliens.sprites():
        if alien.rect.bottom >= screen_rect.bottom:
            # 像飞船被外星人撞到一样处理
            ship_hit(ai_settings,screen,stats,sb,ship,aliens,bullets)
            break
    
def update_aliens(ai_settings,screen,stats,sb,ship,aliens,bullets):
    # 检查并更新外星人群中所有外星人的位置
    check_fleet_edges(ai_settings,aliens)
    aliens.update()
    check_aliens_bottom(ai_settings,stats,screen,ship,aliens,bullets)

    # 检查外星人和飞船之间的碰撞
    if pygame.sprite.spritecollideany(ship,aliens):
        ship_hit(ai_settings,stats,screen,sb,ship,aliens,bullets)
        
    #检查是否有外星人抵达屏幕底端
    check_aliens_bottom(ai_settings,screen,stats,sb,ship,aliens,bullets)

        
def ship_hit(ai_settings,screen,stats,sb,ship,aliens,bullets):
    # 响应被外星人撞到的飞船
    if stats.ship_left > 0:
        stats.ship_left -= 1
        #更新记分牌
        sb.prep_ships()
        # 清空外星人列表和子弹列表
        --snip--
    else:
        stats.game_active = False
        #游戏结束后,重新显示光标
        pygame.mouse.set_visible(True)
        
def check_high_score(stats,sb):
    #检查是否诞生了新的最高得分
    if stats.score > stats.high_score:
        stats.high_score = stats.score
        sb.prep_high_score()
    --snip--

相应的,修改主while循环中的代码,传递实参sb
alien_invasion.py*

python 复制代码
--snip--  
from scoreboard import Scoreboard        
--snip--
def run_game():
    --snip--
    #创建存储游戏统计信息的实例,并创建记分牌
    stats = GameStats(ai_settings)
    sb = Scoreboard(ai_settings,screen,stats)
    
    #游戏的主循环
    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,screen,stats,sb,ship,aliens,bullets)
        --snip--
        gf.update_screen(ai_settings,screen,stats,sb,ship,aliens,bullets,play_button)
        
run_game()
        --snip--
相关推荐
深度学习lover44 分钟前
<项目代码>YOLOv8 苹果腐烂识别<目标检测>
人工智能·python·yolo·目标检测·计算机视觉·苹果腐烂识别
XiaoLeisj2 小时前
【JavaEE初阶 — 多线程】单例模式 & 指令重排序问题
java·开发语言·java-ee
API快乐传递者2 小时前
淘宝反爬虫机制的主要手段有哪些?
爬虫·python
励志成为嵌入式工程师3 小时前
c语言简单编程练习9
c语言·开发语言·算法·vim
捕鲸叉3 小时前
创建线程时传递参数给线程
开发语言·c++·算法
A charmer3 小时前
【C++】vector 类深度解析:探索动态数组的奥秘
开发语言·c++·算法
Peter_chq3 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
阡之尘埃4 小时前
Python数据分析案例61——信贷风控评分卡模型(A卡)(scorecardpy 全面解析)
人工智能·python·机器学习·数据分析·智能风控·信贷风控
记录成长java5 小时前
ServletContext,Cookie,HttpSession的使用
java·开发语言·servlet
前端青山5 小时前
Node.js-增强 API 安全性和性能优化
开发语言·前端·javascript·性能优化·前端框架·node.js