zoukankan      html  css  js  c++  java
  • 项目1 外星人入侵 第14章(计分)

    pycharm中,

    整体缩进:鼠标拉选住代码块,按下tab键。

    反向缩进:鼠标拉选住代码块,按下tab+shift键。

    14.1 添加Play按钮

      下面让游戏一开始处于非活跃状态,并提示玩家单击Play按钮来开始游戏。为此在game.stats.py中输入如下代码:

            # 让游戏一开始处于非活跃状态
            self.game_active = False

    14.1.1 创建Button类

      创建一个Button类,用于创建带标签的实心矩形。下面是Button类的第一部分,请将这个类保存为文件buuton.py:

    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)
            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
            
            # 按钮的标签只需创建一次
            self.prep_msg(msg)

      导入模块pygame.font,让Pygame能够将文本渲染到屏幕上。实参None让Pygame使用默认字体,而48指定了文本的字号。我们创建一个表示按钮的rect对象,并将其center属性设置为屏幕的center属性。Pygame通过将你要显示的字符串渲染为图像来处理文本。我们调用prep_msg()来处理这样的渲染。

      prep_msg()的代码如下:

        def prep_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

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

      我们让文本图像在按钮上居中:根据文本图像创建一个rect,并将其center属性设置为按钮的center属性。

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

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

    14.1.2 在屏幕上绘制按钮

      我们将使用Button类来创建一个Play按钮。

     # 创建Play按钮
        play_button = Button(ai_settings,screen,"Play")
    错误代码  

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

      正确代码:

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

      接下来,修改update_screen(),以便在游戏处于非活跃状态时显示Play按钮:

    def update_screen(ai_settings,screen,stats,ship,aliens,bullets,play_button):
        """更新屏幕上的图像,并切换到新屏幕上"""
        # 每次循环时都会重绘屏幕
        screen.fill(ai_settings.bg_color)
        # 在飞船和外星人后面会重绘所有子弹
        for bullet in bullets.sprites():
            bullet.draw_bullet()
        ship.blitme()
        aliens.draw(screen)
        if not stats.game_active:
            play_button.draw_button()
        # 让最近绘制的屏幕可见
        pygame.display.flip()

    14.1.3 开始游戏

            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(),它返回一个元组,其中包含玩家单击鼠标的x和y坐标。

      alien_invasion.py中调用check_event(),需要传递另外两个实参——stats和play_button:

    gf.check_events(ai_settings,screen,stats,play_button,ship,bullets)

    14.1.4 重置游戏

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

    def check_play_button(ai_settings,screen,stats,play_button,
                          ship,aliens,bullets,mouse_x,mouse_y):
        if play_button.rect.collidepoint(mouse_x,mouse_y):
            stats.reset_stats()
            stats.game_active = True
            
            #清空外星人列表和子弹列表
            aliens.empty()
            bullets.empty()
            
            # 创建一群新的外星人,并让飞船居中
            create_fleet(ai_settings,screen,ship,aliens)
            ship.center_ship()

      重置了游戏统计信息,给玩家提供了三艘新飞船。check_events()的定义需要修改,调用check_play_button()的代码亦如此:

    def 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中调用的check_events()代码也需要修改参数。

    14.1.5 将Play按钮切换到非活跃状态

      当前存在一个问题,游戏开始后,如果玩家不小心单击了Play按钮原来的区域,游戏将重新开始!

        button_clicked = play_button.rect.collidepoint(mouse_x,mouse_y)
        if button_clicked and not stats.game_active:
            stats.reset_stats()
            stats.game_active = True

    14.1.6 隐藏光标

      为了玩家能够开始游戏,要让光标可见,但游戏开始后,光标只会添乱。为修复这个问题,我们在游戏处于活动状态时让光标不可见:

        if button_clicked and not stats.game_active:
            # 隐藏光标
            pygame.mouse.set_visible(False)

      游戏结束后,我们将重新显示光标:

    def ship_hit(ai_settings,stats,screen,ship,aliens,bullets):
        """响应被外星人撞到的飞船"""
        if stats.ships_left > 0:
         --ship--
        else:
            stats.game_active = False
            pygame.mouse.set_visible(True)

      在ship_hit()中,我们在游戏进入非活动状态后,立即让光标可见。

    14.2 提高等级

    14.2.1 修改速度设置

      首先重新组织Settings类,将游戏设置划分成静态和动态的两组。对于游戏进行变化的设置,我们还确保它们在开始新游戏时被重置。

            #以什么样的速度加快游戏节奏
            self.speedup_scale = 1.1
            
            self.initialize_dynamic_settings()

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

       def initialize_dynamic_settings(self):
            """初始化随游戏进行而变化的设置"""
            self.ship_speed_factor = 1.5
            self.bullet_speed_factor = 3
            self.alien_speed_factor = 1
            
            # fleet_direction为1表示向右,为-1表示向左
            self.fleet_direction = 1
        def increase_speed(self):
            """提高速度设置"""
            self.ship_speed_factor *= self.speedup_scale
            self.bullet_speed_factor *= self.speedup_scale
            self.alien_speed_factor *= self.alien_speed_factor

      在check_bullet_alien_collisions()中,我们在整群外星人被消灭后调用increase_speed()来加快游戏的节奏,再创建一群新的外星人:

    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)

    14.2.2 重置速度

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

     if button_clicked and not stats.game_active:
            # 重置游戏设置
            ai_settings.inititalize_dynamic_settings()

    14.3 记分

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

      def reset_stats(self):
            """初始化在游戏运行期间可能变化的统计信息"""
            self.ships_left = self.ai_settings.ship_limit
            self.score = 0

    14.3.1 显示得分

      首先创建一个新类Scoreboard。

    import pygame.font
    
    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 = (28,28,28)
            self.font = pygame.font.SysFont(None,48)
            # 准备初始得分图像
            self.prep_score()
        def 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.right = self.screen_rect.right - 20
            self.score_rect.top = 20
        def show_score(self):
            """在屏幕上显示得分"""
            self.screen.blit(self.score_image,self.score_rect)

      在prep_score():中,我们首先将数字值stats.score转换为字符串,再将字符串传递给创建图像的render。我们将的放在屏幕右上角,并在得分增大时导致这个数字更宽时,让它向左延伸。最后,我们创建方法show_score(),用于显示渲染好的得分图像。

    14.3.2 创建记分牌

      为显示记分牌,我们在alien_invasion.py中创建一个Scoreboard实例:

    import pygame
    from pygame.sprite import Group
    from settings import Settings
    from ship import Ship
    from game_stats import GameStats
    from button import Button
    from scoreboard import Scoreboard
    import game_functions as gf
    
    
    def run_game():
        # 初始化pygame、设置和屏幕对象
        pygame.init()
        ai_settings = Settings()
        screen = pygame.display.set_mode(
            (ai_settings.screen_width,ai_settings.screen_height))
        pygame.display.set_caption("Alien Invasion")
        # 创建Play按钮
        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,play_button,ship,aliens,bullets)
            gf.update_screen(ai_settings, screen, stats,sb,ship, aliens, bullets,
                             play_button)
            if stats.game_active:
                ship.update()
                gf.update_bullets(ai_settings,screen,ship,aliens,bullets)
                gf.update_aliens(ai_settings,stats,screen,ship,aliens,bullets)
    
    run_game()

      为了显示得分,将update_screen()修改成这样:

    def update_screen(ai_settings,screen,stats,sb,ship,aliens,bullets,
                      play_button):
        """更新屏幕上的图像,并切换到新屏幕上"""
        # 每次循环时都会重绘屏幕
        screen.fill(ai_settings.bg_color)
        # 在飞船和外星人后面会重绘所有子弹
        for bullet in bullets.sprites():
            bullet.draw_bullet()
        ship.blitme()
        aliens.draw(screen)
        # 显示得分
        sb.show_score()
        if not stats.game_active:
            play_button.draw_button()
        # 让最近绘制的屏幕可见
        pygame.display.flip()

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

        def initialize_dynamic_settings(self):
            """初始化随游戏进行而变化的设置"""
            self.ship_speed_factor = 1.5
            self.bullet_speed_factor = 3
            self.alien_speed_factor = 1
            # 记分
            self.alien_points = 50

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

    def 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_points
            sb.prep_score()

      修改update_bullets(),确保在函数之间传递合适的实参。还需要修改主while循环中调用update_bullets()的代码。

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

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

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

        if collisions:
            for aliens in collisions.values():
                stats.score += ai_settings.alien_points * len(aliens)
                sb.prep_score()

    14.3.5 提高点数

      添加代码,以在游戏节奏加快时提高点数:

            #以什么样的速度加快游戏节奏
            self.speedup_scale = 1.1
            # 外星人点数的提高速度
            self.score_scale = 1.5   
    
        def increase_speed(self):
            """提高速度设置和外星人点数"""
            self.ship_speed_factor *= self.speedup_scale
            self.bullet_speed_factor *= self.speedup_scale
            self.alien_speed_factor *= self.alien_speed_factor
            self.alien_points = int(self.alien_points * self.score_scale) 

    14.3.6 将得分圆整

     def prep_score(self):
            """将得分转换为一幅渲染的图像"""
            round_score = round(self.stats.score,-1)
            score_str = "{:,}".format(round_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 - 20
            self.score_rect.top = 20

    14.3.7 最高得分

      将最高得分存储在GameStats中:

        def __init__(self,ai_settings):
            """初始化统计信息"""
            self.ai_settings = ai_settings
            self.reset_stats()
            # 让游戏一开始处于非活跃状态
            self.game_active = False
            self.high_score = 0

      下面来修改Scoreboard来显示最高分。

    import pygame.font
    
    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()
        def prep_score(self):
            """将得分转换为一幅渲染的图像"""
            round_score = round(self.stats.score,-1)
            score_str = "{:,}".format(round_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 - 20
            self.score_rect.top = 20
    
        def prep_high_score(self):
            """将最高得分转换为渲染的图像"""
            high_score = 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 show_score(self):
            """在屏幕上显示得分"""
            self.screen.blit(self.score_image,self.score_rect)
            self.screen_blit(self.high_score_image,self.high_score_rect)

      为了检查是否诞生了新的最高分,我们在game_functions.py中添加一个新的函数check_high_score():

    def check_high_score(stats,sb):
        """检查是否诞生了新的最高得分"""
        if stats.score > stats.high_score:
            stats.high_score = stats.score
            sb.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_points * len(aliens)
                sb.prep_score()
            check_high_score(stats,sb)

    14.3.8 显示等级

      在reset_stats()中初始化它:self.level = 1

      为了让Scorebord能够在当前得分下方显示当前等级,我们在_init_()中,调用了一个新的方法prep_level():

      prep_level()的代码如下:

        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

      更新show_sore():

        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()中提高等级,并更新图像:

        if len(aliens) == 0:
            bullets.empty()
            ai_settings.increase_speed()
            # 提高等级
            stats.level += 1
            sb.prep_level()
            create_fleet(ai_settings, screen, ship, aliens)

      为确保新游戏时更新记分和等级图像,在按钮play被单击时触发重置:

        # 重置记分牌图像
        sb.prep_score()
        sb.prep_high_score()
        sb.prep_level()

      在check_events()中,现在需要向check_play_button()传递sb,让它能够访问记分牌对象。

      最后更新alien_invasion.py中调用check_events()代码,也向它传递sb。

    14.3.9 显示余下的飞船数

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

      接下来需要修改Scoreboard,在其中创建一个可供显示的飞船编组。

    from pygame.sprite import Group
    from ship import Ship
    
    class Scoreboard():
        """显示得分信息"""
        def __init__(self,ai_settings,screen,stats):
          --ship--
            self.prep_ship()
        def prep_ship(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.width
                ship.rect.y = 10
                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)

      为在游戏开始时让玩家知道他有多少艘飞船,我们在开始新游戏时调用prep_ships()。

        # 重置记分牌图像
        sb.prep_score()
        sb.prep_high_score()
        sb.prep_level()
        sb.prep_ships()
    def update_aliens(ai_settings,stats,screen,sb,ship,aliens,bullets):
        """
        检查是否有外星人位于屏幕边缘,并更新整群外星人的位置
        """
        check_fleet_edges(ai_settings,aliens)
        aliens.update()
    
        #检测外星人和飞船之间的碰撞
        if pygame.sprite.spritecollideany(ship,aliens):
            ship_hit(ai_settings,stats,screen,ship,aliens,bullets)
        # 检查是否有外星人到达屏幕底端
        check_aliens_bottom(ai_settings,stats,screen,sb,ship,aliens,bullets)
    def ship_hit(ai_settings,stats,screen,sb,ship,aliens,bullets):
        """响应被外星人撞到的飞船"""
        if stats.ships_left > 1:
            # 将ship_left减1
            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 = False
            pygame.mouse.set_visible(True)
    def check_aliens_bottom(ai_settings,stats,screen,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,stats,screen,sb,ship,aliens,bullets)
                break
        while True:
            gf.check_events(ai_settings,screen,stats,sb,play_button,ship,aliens,bullets)
            gf.update_screen(ai_settings, screen, stats,sb,ship,aliens, bullets,
                             play_button)
            if stats.game_active:
                ship.update()
                gf.update_bullets(ai_settings,screen,stats,sb,ship,aliens,bullets)
                gf.update_aliens(ai_settings,stats,screen,sb,ship,aliens,bullets)

      项目完成了,包括一些完善,我将在下一篇展现所有的源代码。

  • 相关阅读:
    hdu 4027 Can you answer these queries?
    hdu 4041 Eliminate Witches!
    hdu 4036 Rolling Hongshu
    pku 2828 Buy Tickets
    hdu 4016 Magic Bitwise And Operation
    pku2886 Who Gets the Most Candies?(线段树+反素数打表)
    hdu 4039 The Social Network
    hdu 4023 Game
    苹果官方指南:Cocoa框架(2)(非原创)
    cocos2d 中 CCNode and CCAction
  • 原文地址:https://www.cnblogs.com/cathycheng/p/11234342.html
Copyright © 2011-2022 走看看