python小游戏—-外星人入侵

源代码:

AlienInvasion/game at main · CrashBugger/AlienInvasion (github.com)

本文来自作者对《python编程-从入门到实践》的学习记录,刚刚入门python,小白一个,若有错误,欢迎大佬指出。

图片资源:

python小游戏----外星人入侵python小游戏----外星人入侵

just nobibi,show me the code!话不多说,开始项目。

1.创建pygame窗口响应用户输出

导包

import pygame
import sys
import settings

接下来

开始进行初步准备sssssssss

def run_game():
    # 初始化游戏并创建一个屏幕对象
    pygame.init()
    screen = pygame.display.set_mode((1200, 800))
    pygame.display.set_caption("Alien Invasion")
    # 游戏主循环
    while True:
        # 监视键盘和鼠标时间
        for event in pygame.event.get():
            if event == pygame.QUIT:
                sys.exit()
        # 让最近的绘制的屏幕可见
        pygame.display.flip()
  • 其中pygame.init()方法初始化背景设置
  • pygame.display.set_mode()创建一个名为screen的窗口对象,以后我们将在这里面绘制图形元素,需要传入一个元组,表示窗口的长高。screen是一个surface对象
  • while循环控制游戏进行,for循环侦听事件,pygame.event.get()方法拿到事件列表,当检测到用户离开时,系统退出
    *
display.flip()方法让最近绘制的屏幕可见

2.设置背景色

先上代码

def run_game():
    # 初始化游戏并创建一个屏幕对象
    pygame.init()
    screen = pygame.display.set_mode((1200, 800))
    pygame.display.set_caption("Alien Invasion")
    # shezhibeijingse
    bg_color = (230, 230, 230)

    # 游戏主循环
    while True:
        # 监视键盘和鼠标时间
        for event in pygame.event.get():
            if event == pygame.QUIT:
                sys.exit()
        # 每次循环重新绘制屏幕
        screen.fill(bg_color)
        # 让最近的绘制的屏幕可见
        pygame.display.flip()
  • 创建一个元组bg_color存储RGB颜色,接下来在主循环中在screen中填充颜色,

3.创建设置类

这一步对前一步进行优化,创建一个settings类,用来保存我们的设置

class Settings():
    def __init__(self):
        self.screen_width = 1200
        self.screen_height = 800
        self.bg_color = (230, 230, 230)

接下来回到主方法创建这个类的实例进行设置

def run_game():
    # 初始化游戏,并创建一个屏幕对象
    pygame.init()
    ai_settings = settings.Settings()
    screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
    pygame.display.set_caption("Ailien Invasion")
    # bg_color = (230, 230, 230)
    while True:
        # 监听事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
        # 每次循环重置屏幕
        screen.fill(ai_settings.bg_color)
        # 最近绘制的屏幕可见
        pygame.display.flip()

4.创建ship类并绘制飞船

创建一个新的文件夹,命名为images,将ship.bmp放进去,接下来创建ship类

import pygame

class Ship():
    def __init__(self, screen):
        self.screen = screen
        # 加载飞船图像并获取外接矩形
        self.image = pygame.image.load("D:\PyCharm\Code\project\game\images\ship.bmp")
        self.rect = self.image.get_rect()
        self.screen_rect = screen.get_rect()
        # 将飞船放在屏幕底部中央
        self.rect.centerx = self.screen_rect.centerx
        self.rect.bottom = self.screen_rect.bottom

    def blitme(self):
        """在指定位置绘制飞船"""
        self.screen.blit(self.image, self.rect)
  • pygame可以像处理矩形一样处理游戏元素,处理rect对象,可以使用矩形中心xy坐标和矩形四角
  • 要将飞船显示在中央,需要设置rect对象的center,centerx或centery;如果要将飞船与边缘对其,可以设置其top,bottom,left,right属性;如果要调整坐标,需要设置其x,y属性,注意原点在左上角。
  • 在主方法中加入飞船
def run_game():
    # 初始化游戏,并创建一个屏幕对象
    pygame.init()
    ai_settings = settings.Settings()
    screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
    pygame.display.set_caption("Ailien Invasion")
    # 创建一艘飞船
    ship = Ship(screen)
    while True:
        # 监听事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
        # 每次循环重置屏幕
        screen.fill(ai_settings.bg_color)
        ship.blitme()
        # 最近绘制的屏幕可见
        pygame.display.flip()

5.重构模块geme_functions

  • 我们将游戏中的各个控制方法抽离到一个专门的模块
  • game_functions.py
import sys
import pygame
import ship

from game import settings

def check_events():
    """响应按键和鼠标事件"""
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

def update_screen(ai_settings: settings.Settings, screen, ship: ship.Ship):
    """更新屏幕上的图像,并切换到新屏幕"""
    # 每次循环chonghuipingmu
    screen.fill(ai_settings.bg_color)
    ship.blitme()
    # 最近绘制的屏幕可见
    pygame.display.filp()

这些都是原本主循环中的方法,现在整合到一个模块

主方法在修改一下

先把刚创建的模块导包

import game_functions as gf
def run_game():
    # 初始化游戏,并创建一个屏幕对象
    pygame.init()
    ai_settings = settings.Settings()
    screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
    pygame.display.set_caption("Ailien Invasion")
    # 创建一艘飞船
    ship = Ship(screen)
    while True:
        # 监听事件
        gf.check_events()
        # 每次循环重置屏幕
        gf.update_screen(ai_settings, screen, ship)

6.驾驶飞船

先在ship类中新加一个属性moving_right=False,当检测到移动时,置位true,并新定义update方法,更新飞船位置

  • Ship类
    def __init__(self, screen):
        self.screen = screen
        # 加载飞船图像并获取外接矩形
        self.image = pygame.image.load("D:\PyCharm\Code\project\game\images\ship.bmp")
        self.rect = self.image.get_rect()
        self.screen_rect = screen.get_rect()
        # 将飞船放在屏幕底部中央
        self.rect.centerx = self.screen_rect.centerx
        self.rect.bottom = self.screen_rect.bottom
        # 移动标志
        self.moving_right = False

    def blitme(self):
        """在指定位置绘制飞船"""
        self.screen.blit(self.image, self.rect)

    def update(self):
        if self.moving_right:
            self.rect.centerx += 1

我们在game_functions.py的check_events方法中加入检测用户键盘的代码

  • game_functions.py
def check_events(ship: ship.Ship):
    """响应按键和鼠标事件"""
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        # 用户按下键盘
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RIGHT:
                ship.moving_right = True
        elif event.type == pygame.KEYUP:
            if event.key == pygame.K_RIGHT:
                ship.moving_right = False

当用按下键盘时,我们在event检测其type属性,若为keydown,再进一步判断是否为向右键,并将ship类中的moving——righ属性置为true,当玩家抬起右键时,检测到keyup,将其置为false,

并在主方法中不断调用ship类的update方法

  • 主方法
def run_game():
    # 初始化游戏,并创建一个屏幕对象
    pygame.init()
    ai_settings = settings.Settings()
    screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
    pygame.display.set_caption("Ailien Invasion")
    # 创建一艘飞船
    ship = Ship(screen)
    while True:
        # 监听事件
        gf.check_events(ship)
        # 更新
        ship.update()
        # 每次循环重置屏幕
        gf.update_screen(ai_settings, screen, ship)

7.调整飞船速度并限制飞船移动边界

  • 在settings类中新加属性self.ship_speed_factor用于控制飞船速度
  • 接下来在ship的方法init方法传入settings对象,并在update方法中用他的属性修改坐标,因为rect只存储整数部分的值,所以为了精细控制速度,我们用float方法返回rect.centerx保存到center属性,并在update方法对center修改,最后再用center覆盖centerx。
  • 判断边界只需要在update方法中判断当前rect的left属性和right属性是否超出边界。
Ship类
class Ship():
    def __init__(self, screen, ai_settings: Settings):
        self.screen = screen
        # 加载飞船图像并获取外接矩形
        self.image = pygame.image.load("D:\PyCharm\Code\project\game\images\ship.bmp")
        self.rect = self.image.get_rect()
        self.screen_rect = screen.get_rect()
        # 将飞船放在屏幕底部中央
        self.rect.centerx = self.screen_rect.centerx
        self.rect.bottom = self.screen_rect.bottom
        # 移动标志
        self.moving_right = False
        self.moving_left = False
        # 飞船设置
        self.ai_settings = ai_settings
        # 在飞船的center属性中存储小数值
        self.center = float(self.rect.centerx)

    def blitme(self):
        """在指定位置绘制飞船"""
        self.screen.blit(self.image, self.rect)

    def update(self):
        # 加上边界判断
        if self.moving_right and self.rect.right < self.screen_rect.right:
            self.center += self.ai_settings.ship_speed_factor
        if self.moving_left and self.rect.left > 0:
            self.center -= self.ai_settings.ship_speed_factor
        # &#x8986;&#x76D6;
        self.rect.centerx = self.center

8.重构check_functions

def check_keydown_events(event, ship):
    if event.key == pygame.K_RIGHT:
        ship.moving_right = True
    elif event.key == pygame.K_LEFT:
        ship.moving_left = True

def check_keyup_events(event, ship):
    if event.key == pygame.K_RIGHT:
        ship.moving_right = False
    elif event.key == pygame.K_LEFT:
        ship.moving_left = False

def check_events(ship: ship.Ship):
    """&#x54CD;&#x5E94;&#x6309;&#x952E;&#x548C;&#x9F20;&#x6807;&#x4E8B;&#x4EF6;"""
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        # &#x7528;&#x6237;&#x6309;&#x4E0B;&#x952E;&#x76D8;
        elif event.type == pygame.KEYDOWN:
            check_keydown_events(event, ship)
        elif event.type == pygame.KEYUP:
            check_keyup_events(event, ship)
  • 提取出两个方法,如图

9.创建子弹类并加入编组

bullet.py

import pygame
from pygame.sprite import Sprite

from game.settings import Settings
from game.ship import Ship

class Bullet(Sprite):
    def __init__(self, ai_settings: Settings, screen, ship: Ship):
        """&#x5728;&#x98DE;&#x8239;&#x6240;&#x5728;&#x4F4D;&#x7F6E;&#x521B;&#x5EFA;&#x5B50;&#x5F39;"""
        super().__init__()
        self.screen = screen
        # &#x5728;&#xFF08;0.0&#xFF09;&#x5904;&#x521B;&#x5EFA;&#x4E00;&#x4E2A;&#x5B50;&#x5F39;&#x77E9;&#x5F62;
        self.rect = pygame.Rect(0, 0, ai_settings.bullet_width, ai_settings.bullet_height)
        # &#x4FEE;&#x6539;&#x4F4D;&#x7F6E;
        self.rect.centerx = ship.rect.centerx
        self.rect.top = ship.rect.top
        # &#x5B58;&#x50A8;&#x5C0F;&#x6570;&#x8868;&#x793A;&#x4F4D;&#x7F6E;
        self.y = float(self.rect.y)
        self.color = ai_settings.bullet_color
        self.speed_factor = ai_settings.bullet_speed_factor

    def update(self):
        """&#x5411;&#x4E0A;&#x79FB;&#x52A8;&#x5B50;&#x5F39;"""
        # &#x66F4;&#x65B0;y&#x5750;&#x6807;
        self.y -= self.speed_factor
        # &#x8986;&#x76D6;
        self.rect.y = self.y

    def draw_bullet(self):
        """&#x5728;&#x5C4F;&#x5E55;&#x4E0A;&#x7ED8;&#x5236;&#x5B50;&#x5F39;"""
        pygame.draw.rect(self.screen, self.color, self.rect)
  • 我们继承sprite类,可以将子弹编组,同时操作编组中的元素。并传入settings对象ship对象和screen对象
  • 用rect存储子弹矩形,并设置其centerx和top属性,让它与飞船相同
  • 跟上一个相同的原因,我们用float()精细调整子弹y坐标

我们在主方法导包并创建Group()实例,并将其传入while循环中的三个方法

from pygame.sprite import Group
&#x521D;&#x59CB;&#x5316;&#x6E38;&#x620F;&#xFF0C;&#x5E76;&#x521B;&#x5EFA;&#x4E00;&#x4E2A;&#x5C4F;&#x5E55;&#x5BF9;&#x8C61;
    pygame.init()
    ai_settings = settings.Settings()
    screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
    pygame.display.set_caption("Ailien Invasion")
    # &#x521B;&#x5EFA;&#x4E00;&#x8258;&#x98DE;&#x8239;
    ship = Ship(screen, ai_settings)
    # &#x521B;&#x5EFA;&#x7528;&#x4E8E;&#x5B58;&#x50A8;&#x5B50;&#x5F39;&#x7684;&#x7F16;&#x7EC4;
    bullets = Group()
    while True:
        # &#x76D1;&#x542C;&#x4E8B;&#x4EF6;
        gf.check_events(ship, bullets)
        # &#x66F4;&#x65B0;
        ship.update()
        # &#x66F4;&#x65B0;&#x5B50;&#x5F39;&#x4F4D;&#x7F6E;
        bullets.update()
        # &#x6BCF;&#x6B21;&#x5FAA;&#x73AF;&#x91CD;&#x7F6E;&#x5C4F;&#x5E55;
        gf.update_screen(ai_settings, screen, ship, bullets)
  • 编组的作用就是存储子弹,并且当调用update()方法时,自动对里面的每个子弹调用update方法,bullets.update()将被每颗子弹调用

10.Fire!开火

  • 这里期望玩家按空格键可以完成开火
  • 我们在functions对check_keydown方法加入对空格的判断若判断到,则在编组中新加入子弹;并在update_screen方法中重绘子弹,注意重绘子弹应在screen.flip()方法调用之前
  • 注意修改方法的参数
class Bullet(Sprite):
    def __init__(self, ai_settings: Settings, screen, ship: Ship):
        """&#x5728;&#x98DE;&#x8239;&#x6240;&#x5728;&#x4F4D;&#x7F6E;&#x521B;&#x5EFA;&#x5B50;&#x5F39;"""
        super().__init__()
        self.screen = screen
        # &#x5728;&#xFF08;0.0&#xFF09;&#x5904;&#x521B;&#x5EFA;&#x4E00;&#x4E2A;&#x5B50;&#x5F39;&#x77E9;&#x5F62;
        self.rect = pygame.Rect(0, 0, ai_settings.bullet_width, ai_settings.bullet_height)
        # &#x4FEE;&#x6539;&#x4F4D;&#x7F6E;
        self.rect.centerx = ship.rect.centerx
        self.rect.top = ship.rect.top
        # &#x5B58;&#x50A8;&#x5C0F;&#x6570;&#x8868;&#x793A;&#x4F4D;&#x7F6E;
        self.y = float(self.rect.y)
        self.color = ai_settings.bullet_color
        self.speed_factor = ai_settings.bullet_speed_factor

    def update(self):
        """&#x5411;&#x4E0A;&#x79FB;&#x52A8;&#x5B50;&#x5F39;"""
        # &#x66F4;&#x65B0;y&#x5750;&#x6807;
        self.y -= self.speed_factor
        # &#x8986;&#x76D6;
        self.rect.y = self.y

    def draw_bullet(self):
        """&#x5728;&#x5C4F;&#x5E55;&#x4E0A;&#x7ED8;&#x5236;&#x5B50;&#x5F39;"""
        pygame.draw.rect(self.screen, self.color, self.rect)
  • 成功开火后,有个问题,那些飞出屏幕外的子弹,仍然留在内存中,这样越积越多,会让我们的游戏慢的龟爬一样,所以在主方法while循环中添加一个删除子弹的方法,判断子弹的bottom属性是否小于等于0,成立则从编组中删除掉
 while True:
        # 监听事件
        gf.check_events(ai_settings, screen, ship, bullets)
        # 更新
        ship.update()
        # 更新子弹位置
        bullets.update()
        # 删除已经消失的子弹
        for bullet in bullets.copy():
            if bullet.rect.bottom
  • 下来我们在对代码进行优化,创建update_bullets函数和fire_bullets来将原本开火和更新的代码抽离
  • games_functions.py
    *
def update_bullets(bullets: Bullet):
    """&#x66F4;&#x65B0;&#x5B50;&#x5F39;&#x672A;&#x4F4D;&#x7F6E;"""
    # &#x66F4;&#x65B0;&#x4F4D;&#x7F6E;
    bullets.update()
    # &#x5220;&#x9664;&#x5DF2;&#x7ECF;&#x6D88;&#x5931;&#x7684;&#x5B50;&#x5F39;
    for bullet in bullets.copy():
        if bullet.rect.bottom <= 0: bullets.remove(bullet) def fire_bullets(ai_settings: settings, screen: surface, ship: ship.ship, bullets: bullet): # 若子弹数量小于3,创建一颗新子弹, if len(bullets) < ai_settings.bullets_allowed: new_bullet="Bullet(ai_settings," screen, ship) bullets.add(new_bullet)< code></=>

修改其中的keydown函数
*

def check_keydown_events(event, ai_settings: Settings, screen, ship, bullets: Bullet):
    if event.key == pygame.K_RIGHT:
        ship.moving_right = True
    elif event.key == pygame.K_LEFT:
        ship.moving_left = True
    elif event.key == pygame.K_SPACE:
        fire_bullets(ai_settings, screen, ship, bullets)

11.创建外星人
* 我们首先改变一下游戏退出的方式,按下Q键即可退出,在game_functions.py中的key_down方法加上下面一条
*

    elif event.key == pygame.K_q:
        sys.exit(1)

创建外星人类

import pygame
from pygame import surface
from pygame.sprite import Sprite

from game.settings import Settings

class Alien(Sprite):
    """&#x8868;&#x793A;&#x5355;&#x4E2A;&#x5916;&#x661F;&#x4EBA;&#x7684;&#x7C7B;"""

    def __init__(self, ai_settings: Settings, screen: surface):
        # &#x521D;&#x59CB;&#x5316;&#x5916;&#x661F;&#x4EBA;&#x5E76;&#x8BBE;&#x7F6E;&#x5176;&#x8D77;&#x59CB;&#x4F4D;&#x7F6E;
        super(Alien, self).__init__()
        self.screen = screen
        self.ai_settings = ai_settings
        # &#x52A0;&#x8F7D;&#x5916;&#x661F;&#x4EBA;&#x56FE;&#x50CF;&#xFF0C;&#x8BBE;&#x7F6E;&#x5176;rect&#x5C5E;&#x6027;
        self.image = pygame.image.load(r"D:\PyCharm\Code\project\game\images\alien.bmp")
        self.rect = self.image.get_rect()
        # &#x6BCF;&#x4E2A;&#x5916;&#x661F;&#x4EBA;&#x521D;&#x59CB;&#x4F4D;&#x7F6E;&#x90FD;&#x5728;&#x5C4F;&#x5E55;&#x5DE6;&#x4E0A;&#x89D2;&#x9644;&#x8FD1;
        self.rect.x = self.rect.width
        self.rect.y = self.rect.height
        # &#x5B58;&#x50A8;&#x5916;&#x661F;&#x4EBA;&#x7684;&#x51C6;&#x786E;&#x4F4D;&#x7F6E;
        self.x = float(self.rect.x)

    def blitme(self):
        """&#x5728;&#x6307;&#x5B9A;&#x4F4D;&#x7F6E;&#x7ED8;&#x5236;&#x5916;&#x661F;&#x4EBA;"""
        self.screen.blit(self.image, self.rect)
  • 讲解同ship类一样,不在赘述
  • 接下来我们要将外星人在图像上画出来,需要在主方法中创建一个实例
    *
   # &#x521B;&#x5EFA;&#x5916;&#x661F;&#x4EBA;&#x5B9E;&#x4F8B;
    alien = Alien(ai_settings, screen)

并在game_functions中修改update_screen方法
* game_functions.py

def update_screen(ai_settings: Settings, screen, ship: ship.Ship, alien: Alien, bullets: Bullet):
    """&#x66F4;&#x65B0;&#x5C4F;&#x5E55;&#x4E0A;&#x7684;&#x56FE;&#x50CF;&#xFF0C;&#x5E76;&#x5207;&#x6362;&#x5230;&#x65B0;&#x5C4F;&#x5E55;"""
    # &#x6BCF;&#x6B21;&#x5FAA;&#x73AF;&#x91CD;&#x7ED8;&#x5C4F;&#x5E55;
    screen.fill(ai_settings.bg_color)
    ship.blitme()
    # &#x7ED8;&#x5236;&#x5916;&#x661F;&#x4EBA;
    alien.blitme()
    # &#x91CD;&#x7ED8;&#x5B50;&#x5F39;
    for bullet in bullets.sprites():
        bullet.draw_bullet()
    # &#x6700;&#x8FD1;&#x7ED8;&#x5236;&#x7684;&#x5C4F;&#x5E55;&#x53EF;&#x89C1;
    pygame.display.flip()
  • 调用个blitme方法即可,注意还需要在flip方法调用之前
  • 尝试成功,接下来我们要创建一行外星人
  • 在game_functions.py中加入如下方法
  • 在主方法中循环之前加入
    *
&#x521B;&#x5EFA;&#x5916;&#x661F;&#x4EBA;&#x7FA4;
    aliens = Group()
    gf.create_fleet(ai_settings, screen, aliens)

aliens是一个空的编组,前面介绍过,编组可以对其中的每个元素调用方法,并将它传入create_fleet()方法,这个方法在game_functions.py中定义
* game_functions.py

def create_fleet(ai_settings: Settings, screen, aliens: Group):
    """&#x521B;&#x5EFA;&#x5916;&#x661F;&#x4EBA;&#x7FA4;"""
    # &#x521B;&#x5EFA;&#x4E00;&#x4E2A;&#x5916;&#x661F;&#x4EBA;&#xFF0C;&#x5E76;&#x8BA1;&#x7B97;&#x4E00;&#x884C;&#x53EF;&#x4EE5;&#x5BB9;&#x7EB3;&#x591A;&#x5C11;&#x4E2A;&#x5916;&#x661F;&#x4EBA;
    alien: Alien = Alien(ai_settings, screen)
    alien_width = alien.rect.width
    available_space_x = ai_settings.screen_width - 2 * alien_width
    # &#x5916;&#x661F;&#x4EBA;&#x95F4;&#x8DDD;&#x4E3A;&#x5916;&#x661F;&#x4EBA;&#x5BBD;&#x5EA6;
    number_aliens_x = int(available_space_x / (2 * alien_width))
    # &#x521B;&#x5EFA;&#x7B2C;&#x4E00;&#x884C;&#x5916;&#x661F;&#x4EBA;
    for alien_number in range(number_aliens_x):
        newalien = Alien(ai_settings, screen)
        newalien.x = alien_width + 2 * alien_width * alien_number
        newalien.rect.x = newalien.x
        aliens.add(newalien)
  • 首先计算一行可以容纳多少个外星人,间距我们设置为外星人的宽度,接下来在编组中加入新建的外星人

12.重构creat_fleet()****

def create_fleet(ai_settings: Settings, screen, aliens: Group):
    """创建外星人群"""
    alien = Alien(ai_settings, screen)
    number_aliens_x = get_number_aliens(ai_settings, alien.rect.width, )
    for alien_number in range(number_aliens_x):
        create_alien(ai_settings, screen, aliens, alien_number)

def get_number_aliens(ai_settings: Settings, alien_width: int) -> int:
    """获取一行容纳的外星人数量"""
    available_space_x = ai_settings.screen_width - 2 * alien_width
    # 外星人间距为外星人宽度
    number_aliens_x = int(available_space_x / (2 * alien_width))
    return number_aliens_x

def create_alien(ai_settings, screen, aliens: Group, alien_number):
    """创建一个外星人并放在当前行"""
    alien = Alien(ai_settings, screen)
    alien_width = alien.rect.width
    alien.x = alien_width + 2 * alien_width * alien_number
    alien.rect.x = alien.x
    aliens.add(alien)

将其中的几个功能提取出来
13.添加多行外星人

game_functions.py

def get_number_rows(ai_settings: Settings, ship_height, alien_height) -> int:
    """&#x8BA1;&#x7B97;&#x80FD;&#x5BB9;&#x7EB3;&#x591A;&#x5C11;&#x884C;"""
    available_space_y = (ai_settings.screen_height - (3 * alien_height) - ship_height)
    number_rows = int(available_space_y / (2 * alien_height))
    return number_rows
  • 加入上述代码
def create_fleet(ai_settings: Settings, screen, ship: ship.Ship, aliens: Group):
    """&#x521B;&#x5EFA;&#x5916;&#x661F;&#x4EBA;&#x7FA4;"""
    alien = Alien(ai_settings, screen)
    number_aliens_x = get_number_aliens(ai_settings, alien.rect.width)
    number_rows = get_number_rows(ai_settings, ship.rect.height, alien.rect.height)
    for alien_number in range(number_aliens_x):
        for alien_row in range(number_rows):
            create_alien(ai_settings, screen, aliens, alien_number, alien_row)
def create_alien(ai_settings, screen, aliens: Group, alien_number, row_number):
    """&#x521B;&#x5EFA;&#x4E00;&#x4E2A;&#x5916;&#x661F;&#x4EBA;&#x5E76;&#x653E;&#x5728;&#x5F53;&#x524D;&#x884C;"""
    alien = Alien(ai_settings, screen)
    alien_width = alien.rect.width
    # &#x5F53;&#x524D;x
    alien.x = alien_width + 2 * alien_width * alien_number
    # &#x5F53;&#x524D;y
    alien.rect.y = alien.rect.height + 2 * alien.rect.height * row_number
    alien.rect.x = alien.x
    aliens.add(alien)
  • 修改create_fleet方法和create_alien方法
  • create_alien中加入一个参数row_number,表示当前应该添加到第几行,并据此修改y坐标
  • create_fleet方法中for循环新加一层层数循环

14.外星人移动

首先在settings.py中添加设置参数

  • settings.py
   # &#x5916;&#x661F;&#x4EBA;&#x8BBE;&#x7F6E;
        self.alien_speed_factor = 0.5#&#x5916;&#x661F;&#x4EBA;&#x6C34;&#x5E73;&#x79FB;&#x52A8;&#x901F;&#x5EA6;
        self.fleet_drop_speed = 10#&#x5916;&#x661F;&#x4EBA;&#x7AD6;&#x76F4;&#x79FB;&#x52A8;&#x901F;&#x5EA6;
        self.fleet_direction = 1  #&#x79FB;&#x52A8;&#x65B9;&#x5411;&#xFF1A; 1&#x4E3A;&#x5411;&#x53F3;&#xFF0C;-1&#x5411;&#x5DE6;
  • Alien.py中加入下述代码
  def update(self):
        """&#x5411;&#x5DE6;&#x5411;&#x53F3;&#x79FB;&#x52A8;&#x5916;&#x661F;&#x4EBA;"""
        self.x += self.ai_settings.alien_speed_factor * self.ai_settings.fleet_direction
        self.rect.x = self.x

    def check_edges(self):
        """&#x5982;&#x679C;&#x5916;&#x661F;&#x4EBA;&#x5230;&#x8FBE;&#x8FB9;&#x7F18;&#xFF0C;&#x8FD4;&#x56DE;True"""
        screen_rect = self.screen.get_rect()
        if self.rect.right >= screen_rect.right:
            return True
        elif self.rect.left <= 0: return true< code></=>
  • game_functions.py中加入
def change_fleet_direction(ai_settings, aliens):
    """&#x5C06;&#x5916;&#x661F;&#x4EBA;&#x6574;&#x4F53;&#x4E0B;&#x79FB;&#xFF0C;&#x5E76;&#x6539;&#x53D8;&#x4ED6;&#x4EEC;&#x65B9;&#x5411;"""
    for alien in aliens.sprites():
        alien.rect.y += ai_settings.fleet_drop_speed
    ai_settings.fleet_direction *= -1

def check_fleet_edges(ai_settings: Settings, aliens):
    """&#x6709;&#x5916;&#x661F;&#x4EBA;&#x5230;&#x8FBE;&#x8FB9;&#x7F18;&#x65F6;&#x91C7;&#x53D6;&#x76F8;&#x5E94;&#x7684;&#x63AA;&#x65BD;"""
    for alien in aliens.sprites():
        if alien.check_edges():
            change_fleet_direction(ai_settings, aliens)
            break

def update_aliens(ai_settings, aliens):
    """&#x68C0;&#x67E5;&#x662F;&#x5426;&#x6709;&#x5916;&#x661F;&#x4EBA;&#x4F4D;&#x4E8E;&#x5C4F;&#x5E55;&#x8FB9;&#x7F18;&#xFF0C;&#x5E76;&#x66F4;&#x65B0;&#x8C03;&#x6574;&#x5916;&#x661F;&#x4EBA;&#x4F4D;&#x7F6E;"""
    check_fleet_edges(ai_settings, aliens)
    aliens.update()
  • update_aliens为我们对主方法提供的接口,上面两个为辅助函数
  • 主方法while中调用update_aliens方法
 # &#x66F4;&#x65B0;aliens
        gf.update_aliens(ai_settings, aliens)

15.射杀外星人并生成新的外星人群

我们需要调用pygame.sprite.groupcollied方法,这个方法检测两个编组中的元素是否重合,重合的话进行处理,并返回一个字典,每个键值对为子弹-外星人。其中第一个True表示重合的话删除第一个编组中的元素,第二个true表示删除第二个编组中的元素。我们在更新子弹位置时调用这个方法,并增加主方法中的参数调用

  • 击杀外星人后,我们需要判断是否需要生成新的外星人,若编组为空,重新调用生成外星人的方法
  • game_functions.py
def update_bullets(ai_settings, screen, ship, aliens: Alien, bullets: Sprite):
    """&#x66F4;&#x65B0;&#x5B50;&#x5F39;&#x672A;&#x4F4D;&#x7F6E;"""
    # &#x66F4;&#x65B0;&#x4F4D;&#x7F6E;
    bullets.update()
    # &#x5220;&#x9664;&#x5DF2;&#x7ECF;&#x6D88;&#x5931;&#x7684;&#x5B50;&#x5F39;
    for bullet in bullets.copy():
        if bullet.rect.bottom <= 0: bullets.remove(bullet) check_bullet_alien_collision(ai_settings, screen, ship, aliens, bullets) def bullets): # 检查是否有子弹击中外星人 collisions="pygame.sprite.groupcollide(bullets," true, true) 更新外星人 if len(aliens)="=" bullets.empty() create_fleet(ai_settings, aliens)< code></=>

16.Boom!!!飞船与外星人相撞

如果飞船与外星人相撞或者外星人到底部,我们让游戏重置,重新生成外星人,并将飞船居中

ship.py

  def center_ship(self):
        """&#x8BA9;&#x98DE;&#x8239;&#x5C45;&#x4E2D;"""
        self.center = self.screen_rect.centerx
  • 我们还需要将飞船的命数减一,为此我们新建一个GameStatus类,用于记录游戏状态信息
  • 在Settings.py中新加属性
    *
   self.ship_limit = 3
  • GameStatus.py
class GameStatus():
    """跟踪游戏的统计信息"""

    def __init__(self, ai_settings):
        """初始化统计的信息"""
        self.ai_settings = ai_settings
        self.reset_status()

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

game_functions.py

def update_aliens(ai_settings, stats, screen, ship, bullets, aliens):
    """检查是否有外星人位于屏幕边缘,并更新调整外星人位置"""
    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(aliens, ai_settings, stats, screen, ship, bullets)

def ship_hit(ai_settings: Settings, status: GameStatus, screen, ship: ship.Ship, aliens, bullets):
    """响应外星人撞到飞船"""
    # 将飞船数量ship_left减一
    status.ships_left -= 1
    # 清空外星人和子弹
    aliens.empty()
    bullets.empty()
    # 创建一群新的外星人,重置飞船
    create_fleet(ai_settings, screen, ship, aliens)
    ship.center_ship()
    # 暂停
    sleep(0.5)

def check_aliens_bottom(aliens: Alien, ai_settings: Settings, stats: GameStatus,
                        screen: Surface, ship: ship.Ship, bullets):
    """检查是否有外星人到达底部"""
    screen_rect = screen.get_rect()
    for alien in aliens:
        if alien.rect.bottom >= screen_rect.bottom:
            # 像飞船撞到飞船一样处理
            ship_hit(ai_settings, stats, screen, ship, aliens, bullets)
            break

我们调用update_aliens方法时,顺便检查一下飞船与外星人是否相撞,通过

pygame.sprite.spritecollideany(ship, aliens)

方法,第一个参数为一个飞船对象,第二个参数为一个编组,
17.游戏结束

game_functions.py的__ini__t中加入

  # &#x6E38;&#x620F;&#x521A;&#x542F;&#x52A8;&#x5904;&#x4E8E;&#x6D3B;&#x52A8;&#x72B6;&#x6001;
        self.game_active = True
  • 这个属性让我们可以再玩家命数耗尽后置为False退出游戏。
  • 改动game_functions.py方法,加入判断
def ship_hit(ai_settings: Settings, status: GameStatus, screen, ship: ship.Ship, aliens, bullets):
    """&#x54CD;&#x5E94;&#x5916;&#x661F;&#x4EBA;&#x649E;&#x5230;&#x98DE;&#x8239;"""
    # &#x5C06;&#x98DE;&#x8239;&#x6570;&#x91CF;ship_left&#x51CF;&#x4E00;
    if status.ships_left > 0:
        status.ships_left -= 1
        # &#x6E05;&#x7A7A;&#x5916;&#x661F;&#x4EBA;&#x548C;&#x5B50;&#x5F39;
        aliens.empty()
        bullets.empty()
        # &#x521B;&#x5EFA;&#x4E00;&#x7FA4;&#x65B0;&#x7684;&#x5916;&#x661F;&#x4EBA;&#xFF0C;&#x91CD;&#x7F6E;&#x98DE;&#x8239;
        create_fleet(ai_settings, screen, ship, aliens)
        ship.center_ship()
        # &#x6682;&#x505C;
        sleep(0.5)
    else:
        status.game_active = False
  • 主方法的while循环改为
    *
 while True:
        # &#x76D1;&#x542C;&#x4E8B;&#x4EF6;
        gf.check_events(ai_settings, screen, ship, bullets)
        if stats.game_active:
            # &#x66F4;&#x65B0;
            ship.update()
            # &#x66F4;&#x65B0;&#x5B50;&#x5F39;
            gf.update_bullets(ai_settings, screen, ship, aliens, bullets)
            # &#x66F4;&#x65B0;aliens
            gf.update_aliens(ai_settings, stats, screen, ship, bullets, aliens)
            # &#x6BCF;&#x6B21;&#x5FAA;&#x73AF;&#x91CD;&#x7F6E;&#x5C4F;&#x5E55;
            gf.update_screen(ai_settings, screen, ship, aliens, bullets)

18.添加开始按钮
* button.py

import pygame.font
class Button():
    def __init__(self, ai_settings: Settings, screen: Surface, msg):
        """&#x521D;&#x59CB;&#x5316;&#x6309;&#x94AE;&#x7684;&#x5C5E;&#x6027;"""
        self.screen = screen
        self.screen_rect = screen.get_rect()
        # &#x8BBE;&#x7F6E;&#x6309;&#x94AE;&#x7684;&#x5C3A;&#x5BF8;&#x548C;&#x5176;&#x4ED6;&#x5C5E;&#x6027;
        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)
        # &#x521B;&#x5EFA;&#x6309;&#x94AE;&#x7684;rect&#x5BF9;&#x8C61;&#xFF0C;&#x5E76;&#x4F7F;&#x5176;&#x5C45;&#x4E2D;
        self.rect = pygame.Rect(0, 0, self.width, self.height)
        self.rect.center = self.screen_rect.center
        # &#x6309;&#x94AE;&#x6807;&#x7B7E;&#x53EA;&#x9700;&#x521B;&#x5EFA;&#x4E00;&#x6B21;
        self.prep_msg(msg)
  • font属性中我们调用pygame.font()方法,None表示用默认字体,48指的是字体字号。为了让按钮在屏幕居中,我们创建一个rect对象,并让他居中
  • 接下来在新建一个方法prep_msg方法来处理按钮的渲染和绘制按钮的方法
    def prep_msg(self, msg):
        """&#x5C06;msg&#x6E32;&#x67D3;&#x4E3A;&#x56FE;&#x50CF;&#xFF0C;&#x5E76;&#x5C06;&#x5176;&#x5728;&#x6309;&#x94AE;&#x4E0A;&#x5C45;&#x4E2D;"""
        self.msg_image = self.font.render(self, 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):
        """&#x7ED8;&#x5236;&#x4E00;&#x4E2A;&#x7528;&#x989C;&#x8272;&#x586B;&#x5145;&#x7684;&#x6309;&#x94AE;&#xFF0C;&#x5728;&#x7ED8;&#x5236;&#x6587;&#x672C;"""
        self.screen.fill(self.button_color, self.rect)
        self.screen.blit(self.msg_image, self.msg_image_rect)
  • 其中的font.render方法将存储在msg中的文本转换为图像,并将其存储在msg_image中,其中的True参数表示开启抗锯齿功能,可以让文字边缘更光滑。接下来最后调通过rect调整按钮位置。
  • 接下来draw_button来绘制按钮。
  • game_functions.py的update_screen中加入判断游戏活动状态的if语句,若不在活动状态,绘制一个按钮
    *
def update_screen(ai_settings: Settings, screen, stats: GameStatus, ship: ship.Ship, aliens: Group, bullets: Bullet,
                  play_button: Button):
    """&#x66F4;&#x65B0;&#x5C4F;&#x5E55;&#x4E0A;&#x7684;&#x56FE;&#x50CF;&#xFF0C;&#x5E76;&#x5207;&#x6362;&#x5230;&#x65B0;&#x5C4F;&#x5E55;"""
    # &#x6BCF;&#x6B21;&#x5FAA;&#x73AF;&#x91CD;&#x7ED8;&#x5C4F;&#x5E55;
    screen.fill(ai_settings.bg_color)
    ship.blitme()
    # &#x7ED8;&#x5236;&#x5916;&#x661F;&#x4EBA;
    aliens.draw(screen)
    # &#x91CD;&#x7ED8;&#x5B50;&#x5F39;
    for bullet in bullets.sprites():
        bullet.draw_bullet()
    if not stats.game_active:
        play_button.draw_button()
    # &#x6700;&#x8FD1;&#x7ED8;&#x5236;&#x7684;&#x5C4F;&#x5E55;&#x53EF;&#x89C1;
    pygame.display.flip()

主方法中创建按钮实例,并将其传入update_screen方法中
*

 # &#x521B;&#x5EFA;play&#x6309;&#x94AE;
    play_button = Button(ai_settings, screen, "Play")

19.开始游戏或重置游戏
* 在game_functions.py中加入监视与按钮相关的鼠标事件
*

def check_events(ai_settings, screen, ship, bullets, stats: GameStatus, play_button: Button):
    """&#x54CD;&#x5E94;&#x6309;&#x952E;&#x548C;&#x9F20;&#x6807;&#x4E8B;&#x4EF6;"""
    for event in pygame.event.get():
        # &#x7528;&#x6237;&#x6309;&#x4E0B;&#x952E;&#x76D8;
        if 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.K_SPACE:
            fire_bullets(ai_settings, )
        elif event.type == pygame.MOUSEBUTTONDOWN:
            # &#x82E5;&#x6309;&#x4E0B;&#x6309;&#x94AE;
            mouse_x, mouse_y = pygame.mouse.get_pos()
            check_play_button(stats, play_button, mouse_x, mouse_y)
def check_play_button(stats: GameStatus, play_button: Button, mouse_x, mouse_y):
    """&#x5728;&#x73A9;&#x5BB6;&#x5355;&#x51FB;play&#x65F6;&#x5F00;&#x59CB;&#x6E38;&#x620F;"""
    if play_button.rect.collidepoint(mouse_x, mouse_y):
        stats.game_active = True

其中mouse.get_pos返回一个元组,表示鼠标点击的坐标,我们用play_button.rect.collidepoint方法判断是否与按钮坐标重合
* 接下来重置游戏,因为上面仅对第一次点击有用
* 再次修改check_lpay_button方法
*

def check_play_button(ai_settings, screen, stats: GameStatus, play_button: Button, mouse_x, mouse_y, aliens, ship,
                      bullets):
    """&#x5728;&#x73A9;&#x5BB6;&#x5355;&#x51FB;play&#x65F6;&#x5F00;&#x59CB;&#x6E38;&#x620F;"""
    if play_button.rect.collidepoint(mouse_x, mouse_y):
        # &#x91CD;&#x7F6E;&#x6E38;&#x620F;&#x5E76;&#x7EDF;&#x8BA1;&#x4FE1;&#x606F;
        stats.reset_status()
        stats.game_active = True
        # &#x6E05;&#x7A7A;&#x5916;&#x661F;&#x4EBA;&#x548C;&#x5B50;&#x5F39;&#x7684;&#x5217;&#x8868;
        bullets.empty()
        aliens.empty()
        # &#x521B;&#x5EFA;&#x5916;&#x661F;&#x4EBA;&#xFF0C;&#x5E76;&#x8BA9;&#x98DE;&#x8239;&#x5C45;&#x4E2D;
        create_fleet(ai_settings, screen, ship, aliens)
        ship.center_ship()

我们新加了这个方法的参数,不要忘了在调用这个方法的位置加上(pycharm中ctrl+alt+h可以查看调用次方法的方法)
* 现在还是有个问题,如果玩家在点击按钮后再次点击,会出现再次重置的情况,所以我们再加入对stats.game_active的判断,同时再让点击后光标不可见
*

def check_play_button(ai_settings, screen, stats: GameStatus, play_button: Button, mouse_x, mouse_y, aliens, ship,
                      bullets):
    """&#x5728;&#x73A9;&#x5BB6;&#x5355;&#x51FB;play&#x65F6;&#x5F00;&#x59CB;&#x6E38;&#x620F;"""
    button_clicked = play_button.rect.collidepoint(mouse_x, mouse_y)
    if button_clicked and not stats.game_active:
        #&#x8BA9;&#x5149;&#x6807;&#x4E0D;&#x53EF;&#x89C1;
        pygame.mouse.set_visible(False)
        # &#x91CD;&#x7F6E;&#x6E38;&#x620F;&#x5E76;&#x7EDF;&#x8BA1;&#x4FE1;&#x606F;
        stats.reset_status()
        stats.game_active = True
        # &#x6E05;&#x7A7A;&#x5916;&#x661F;&#x4EBA;&#x548C;&#x5B50;&#x5F39;&#x7684;&#x5217;&#x8868;
        bullets.empty()
        aliens.empty()
        # &#x521B;&#x5EFA;&#x5916;&#x661F;&#x4EBA;&#xFF0C;&#x5E76;&#x8BA9;&#x98DE;&#x8239;&#x5C45;&#x4E2D;
        create_fleet(ai_settings, screen, ship, aliens)
        ship.center_ship()

修改ship_hit方法,让游戏重置时,鼠标可见
*

def ship_hit(ai_settings: Settings, status: GameStatus, screen, ship: ship.Ship, aliens, bullets):
    """响应外星人撞到飞船"""
    # 将飞船数量ship_left减一
    if status.ships_left > 0:
        status.ships_left -= 1
        # 清空外星人和子弹
        aliens.empty()
        bullets.empty()
        # 创建一群新的外星人,重置飞船
        create_fleet(ai_settings, screen, ship, aliens)
        ship.center_ship()
        # 暂停
        sleep(0.5)
    else:
        status.game_active = False
        pygame.mouse.set_visible(True)

20.加快游戏节奏
* 我们修改Settings.py中的属性来加快游戏
*

class Settings():
    def __init__(self):
        """  初始化游戏时的静态设置"""
        self.screen_width = 1200
        self.screen_height = 800
        self.bg_color = (230, 230, 230)
        # 飞船设置
        self.ship_speed_factor = 1.0
        self.ship_limit = 3
        # 子弹设置
        self.bullet_speed_factor = 0.7
        self.bullet_width = 3
        self.bullet_height = 15
        self.bullet_color = 60, 60, 60
        # 限制子弹数量
        self.bullets_allowed = 3
        # 外星人设置
        self.alien_speed_factor = 0.5  # 外星人水平移动速度
        self.fleet_drop_speed = 10  # 外星人竖直移动速度
        self.fleet_direction = 1  # 移动方向: 1为向右,-1向左
        # 以什么样的速度加快游戏节奏
        self.speedup_scale = 1.1
        self.initialize_dynamic_settings()

    def initialize_dynamic_settings(self):
        """初始化游戏进行而变化的设置"""
        self.ship_speed_factor = 1.5
        self.bullet_speed_factor = 3
        self.alien_speed_factor = 1
        self.fleet_direction = 1

    def increase_speed(self):
        """提高速度设置"""
        self.ship_speed_factor *= self.speedup_scale
        self.alien_speed_factor *= self.speedup_scale
        self.alien_speed_factor *= self.speedup_scale

增加了一个倍率属性和两个方法

def check_play_button(ai_settings: Settings, screen, stats: GameStatus, play_button: Button, mouse_x, mouse_y, aliens,
                      ship,
                      bullets):
    """&#x5728;&#x73A9;&#x5BB6;&#x5355;&#x51FB;play&#x65F6;&#x5F00;&#x59CB;&#x6E38;&#x620F;"""
    button_clicked = play_button.rect.collidepoint(mouse_x, mouse_y)
    if button_clicked and not stats.game_active:
        #&#x91CD;&#x7F6E;&#x6E38;&#x620F;&#x8282;&#x594F;
        ai_settings.initialize_dynamic_settings()
         &#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;
  • 接下来修改

game_functions.py中的check_bullet_alien_collision

def check_bullet_alien_collision(ai_settings: 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)
  • 但是现在还有个问题,我们重置游戏时游戏节奏没有重置
    *
def check_play_button(ai_settings: Settings, screen, stats: GameStatus, play_button: Button, mouse_x, mouse_y, aliens,
                      ship,
                      bullets):
    """在玩家单击play时开始游戏"""
    button_clicked = play_button.rect.collidepoint(mouse_x, mouse_y)
    if button_clicked and not stats.game_active:
        #重置游戏节奏
        ai_settings.initialize_dynamic_settings()

21.计分
* 新增game_status.py中GameStatus类中的属性
*

    def reset_status(self):
        """&#x521D;&#x59CB;&#x5316;&#x5728;&#x6E38;&#x620F;&#x8FD0;&#x884C;&#x671F;&#x95F4;&#x53EF;&#x80FD;&#x53D8;&#x5316;&#x7684;&#x4FE1;&#x606F;"""
        self.ships_left = self.ai_settings.ship_limit
        self.score = 0

新建scoreboard.py模块,并创建Scoreboard类:
*

class Scoreboard():
    """显示得分信息的类"""

    def __init__(self, ai_settings: Settings, screen: Surface, status: GameStatus):
        """初始化得分涉及的属性"""
        self.screen = screen
        self.screen_rect = screen.get_rect()
        self.ai_settings = ai_settings
        self.status = status
        # 显示得分时的字体设置
        self.text_color = (30, 30, 30)
        self.font = pygame.font.SysFont(None, 48)
        # 准备初始得分图像
        self.prep_score()

    def prep_score(self):
        """将得分转换为渲染图像"""
        score_str = str(self.status.score)
        self.score_image = self.font.render(score_str, True, self.text_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)

22.创建记分牌
* 在主方法中循环前创建得分牌实例
*

 # &#x521B;&#x5EFA;&#x4E00;&#x4E2A;&#x8BB0;&#x5206;&#x724C;
    sb = Scoreboard(ai_settings, screen, stats)

并将其传入循环中的update_screen方法中
* game_functions.py
*

def update_screen(ai_settings: Settings, screen, stats: GameStatus, ship: ship.Ship, aliens: Group, bullets: Bullet,
                  play_button: Button, sb: Scoreboard):
    """&#x66F4;&#x65B0;&#x5C4F;&#x5E55;&#x4E0A;&#x7684;&#x56FE;&#x50CF;&#xFF0C;&#x5E76;&#x5207;&#x6362;&#x5230;&#x65B0;&#x5C4F;&#x5E55;"""
    &#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;
    # &#x663E;&#x793A;&#x5F97;&#x5206;
    sb.show_score()
    # &#x6700;&#x8FD1;&#x7ED8;&#x5236;&#x7684;&#x5C4F;&#x5E55;&#x53EF;&#x89C1;
    pygame.display.flip()

接下来我们开始增加分数
* settings.py中加入分数设置
*

    def initialize_dynamic_settings(self):
        """&#x521D;&#x59CB;&#x5316;&#x6E38;&#x620F;&#x8FDB;&#x884C;&#x800C;&#x53D8;&#x5316;&#x7684;&#x8BBE;&#x7F6E;"""
        self.ship_speed_factor = 1.5
        self.bullet_speed_factor = 0.7
        self.alien_speed_factor = 0.7
        self.fleet_direction = 1
        # &#x8BA1;&#x5206;
        self.alien_points = 50
  • game_functions.py中修改方法
def check_bullet_alien_collision(ai_settings: Settings, screen, ship, aliens, bullets, stats: GameStatus,
                                 sb: Scoreboard):
    # 检查是否有子弹击中外星人
    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()
    # 更新外星人
    if len(aliens) == 0:
        bullets.empty()
        # 增加游戏节奏
        ai_settings.increase_speed()
        create_fleet(ai_settings, screen, ship, aliens)

其中collisions是一个字典,我们拿到他的值,这个值是一个列表,其中包括我们一颗子弹打掉的全部飞船(不排除一箭双雕),所以求出列表长度来算分
* 不要忘了在形参中加入新的值并修改调用此方法传入的参数
* 接下来我们设置游戏难度提高后的点数
* settings.py
*

class Settings():
    def __init__(self):
       。。。。。。。。。。。。。。
        # 点数提高速度
        self.score_scale = 1.5

    def increase_speed(self):
     。。。。。。。。。。。。。。
        """提高点数"""
        self.alien_points = int(self.alien_points * self.score_scale)

为了让记分牌跟以前那种街机风格的记分牌一样,每三位用”,”隔开,我们修改prep_score方法
* scoreboard.py
*

    def prep_score(self):
        """将得分转换为渲染图像"""
        # 将得分圆整
        rounded_score = int(round(self.status.score, -1))
        score_str = "{:,}".format(rounded_score)
        # score_str = str(self.status.score)删去
        self.score_image = self.font.render(score_str, True, self.text_color)
        # 将得分显示在屏幕右上角
        self.score_rect = self.score_image.get_rect()
        self.score_rect.right = self.screen_rect.right - 20
        self.score_rect.top = 20

其中round()中的-1表示我们向10取整
* 23.显示最高得分
* scoreboard.py
*

 def __init__(self, ai_settings: Settings, screen: Surface, status: GameStatus):
        """初始化得分涉及的属性"""
       。。。。。。。。。。。。
        self.prep_high_score()

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

    def prep_high_score(self):
        """将最高得分转换为渲染的图像"""
        high_score = int(round(self.status.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

接下来我们判断是否诞生了最高分
* game_functions.py加入
*

def check_high_score(stats: GameStatus, sb: Scoreboard):
    """&#x68C0;&#x67E5;&#x662F;&#x5426;&#x8BDE;&#x751F;&#x4E86;&#x6700;&#x9AD8;&#x5206;"""
    if stats.high_score < stats.score:
        stats.high_score = stats.score
        sb.prep_high_score()

我们需要在check_bullet_collision中调用分数判断

def check_bullet_alien_collision(ai_settings: Settings, screen, ship, aliens, bullets, stats: GameStatus,
                                 sb: Scoreboard):
    # 检查是否有子弹击中外星人
    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)
  。。。。。。。。。。。。。。。

24、显示等级
* 增加属性:
* game_status.py

    def reset_status(self):
        """&#x521D;&#x59CB;&#x5316;&#x5728;&#x6E38;&#x620F;&#x8FD0;&#x884C;&#x671F;&#x95F4;&#x53EF;&#x80FD;&#x53D8;&#x5316;&#x7684;&#x4FE1;&#x606F;"""
       &#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;
        self.level = 1

scoreboard.py
*

 def __init__(self, ai_settings: Settings, screen: Surface, status: GameStatus):
        """&#x521D;&#x59CB;&#x5316;&#x5F97;&#x5206;&#x6D89;&#x53CA;&#x7684;&#x5C5E;&#x6027;"""
        &#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;
        self.prep_level()
def show_score(self):
        """&#x5728;&#x5C4F;&#x5E55;&#x4E0A;&#x663E;&#x793A;&#x5F97;&#x5206;"""
       &#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;
        self.screen.blit(self.level_image, self.level_rect)
 def prep_level(self):
        """&#x5C06;&#x7B49;&#x7EA7;&#x6E32;&#x67D3;&#x4E3A;&#x56FE;&#x50CF;"""
        self.level_image
        self.level_image = self.font.render(str(self.status.level), True, self.text_color,
             self.ai_settings.bg_color)
        """&#x5C06;&#x7B49;&#x7EA7;&#x653E;&#x5728;&#x5F97;&#x5206;&#x4E0B;&#x65B9;"""
        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

game_functions.py中修改
*

def check_bullet_alien_collision(ai_settings: Settings, screen, ship, aliens, bullets, stats: GameStatus,sb: Scoreboard):
   &#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;
    # &#x66F4;&#x65B0;&#x5916;&#x661F;&#x4EBA;
    if len(aliens) == 0:
        bullets.empty()
        # &#x589E;&#x52A0;&#x6E38;&#x620F;&#x8282;&#x594F;
        ai_settings.increase_speed()
        # &#x5982;&#x679C;&#x5916;&#x661F;&#x4EBA;&#x90FD;&#x88AB;&#x6D88;&#x706D;&#xFF0C;&#x5C31;&#x63D0;&#x9AD8;&#x4E00;&#x4E2A;&#x7B49;&#x7EA7;
        stats.level += 1
        sb.prep_level()
        create_fleet(ai_settings, screen, ship, aliens)

为了每次点击play按钮时重置记分牌,我们修改
*

def check_play_button(ai_settings: Settings, screen, stats: GameStatus, play_button: Button, mouse_x, mouse_y, aliens,
                      ship, bullets, sb: Scoreboard, ):
    """&#x5728;&#x73A9;&#x5BB6;&#x5355;&#x51FB;play&#x65F6;&#x5F00;&#x59CB;&#x6E38;&#x620F;"""
    ...........................................

        # &#x91CD;&#x7F6E;&#x5F97;&#x5206;&#x724C;&#x56FE;&#x50CF;
        sb.prep_score()
        sb.prep_high_score()
        sb.prep_level()
        # &#x6E05;&#x7A7A;&#x5916;&#x661F;&#x4EBA;&#x548C;&#x5B50;&#x5F39;&#x7684;&#x5217;&#x8868;
        bullets.empty()
        aliens.empty()
        # &#x521B;&#x5EFA;&#x5916;&#x661F;&#x4EBA;&#xFF0C;&#x5E76;&#x8BA9;&#x98DE;&#x8239;&#x5C45;&#x4E2D;
        create_fleet(ai_settings, screen, ship, aliens)
        ship.center_ship()

我们新传进来了个参数sb(ScoreBoard类)
* 25.显示剩余飞船
* ship.py
*

class Ship(Sprite):
    def __init__(self, screen, ai_settings: Settings):
        """&#x521D;&#x59CB;&#x5316;&#x98DE;&#x8239;&#xFF0C;&#x5E76;&#x8BBE;&#x7F6E;&#x5176;&#x8D77;&#x59CB;&#x4F4D;&#x7F6E;"""
        super(Ship, self).__init__()
&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;

让飞船继承sprite类,这样我们可以加入编组中管理
* scoreboard.py类修改
*


class Scoreboard():
    """&#x663E;&#x793A;&#x5F97;&#x5206;&#x4FE1;&#x606F;&#x7684;&#x7C7B;"""

    def __init__(self, ai_settings: Settings, screen: Surface, status: GameStatus):
        """&#x521D;&#x59CB;&#x5316;&#x5F97;&#x5206;&#x6D89;&#x53CA;&#x7684;&#x5C5E;&#x6027;"""
        &#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;
        self.prep_ships()

    def show_score(self):
        """&#x5728;&#x5C4F;&#x5E55;&#x4E0A;&#x663E;&#x793A;&#x5F97;&#x5206;"""
      &#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;
        self.ships.draw(self.screen)

    def prep_ships(self):
        """&#x663E;&#x793A;&#x8FD8;&#x5269;&#x4E0B;&#x591A;&#x5C11;&#x98DE;&#x8239;"""
        self.ships = Group()
        for ship_number in range(self.status.ships_left):
            ship = Ship(self.screen, self.ai_settings)
            ship.rect.x = 10 + ship_number * ship.rect.width
            ship.rect.y = 10
            self.ships.add(ship)

game_functions.py修改,调用prep_ships方法
*

def check_play_button(ai_settings: Settings, screen, stats: GameStatus, play_button: Button, mouse_x, mouse_y, aliens,
                      ship, bullets, sb: Scoreboard, ):
    """&#x5728;&#x73A9;&#x5BB6;&#x5355;&#x51FB;play&#x65F6;&#x5F00;&#x59CB;&#x6E38;&#x620F;"""
   &#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;
        # &#x91CD;&#x7F6E;&#x5F97;&#x5206;&#x724C;&#x56FE;&#x50CF;
        sb.prep_score()
        sb.prep_high_score()
        sb.prep_level()
        sb.prep_ships()
        # &#x6E05;&#x7A7A;&#x5916;&#x661F;&#x4EBA;&#x548C;&#x5B50;&#x5F39;&#x7684;&#x5217;&#x8868;
        bullets.empty()
        aliens.empty()
        # &#x521B;&#x5EFA;&#x5916;&#x661F;&#x4EBA;&#xFF0C;&#x5E76;&#x8BA9;&#x98DE;&#x8239;&#x5C45;&#x4E2D;
        create_fleet(ai_settings, screen, ship, aliens)
        ship.center_ship()

接下来我们在飞船撞到外星人时更新ships编组
*

def ship_hit(ai_settings: Settings, status: GameStatus, screen, ship: ship.Ship, aliens, bullets, sb: Scoreboard):
    """&#x54CD;&#x5E94;&#x5916;&#x661F;&#x4EBA;&#x649E;&#x5230;&#x98DE;&#x8239;"""
    # &#x5C06;&#x98DE;&#x8239;&#x6570;&#x91CF;ship_left&#x51CF;&#x4E00;
    if status.ships_left > 0:
        status.ships_left -= 1
        # &#x66F4;&#x65B0;&#x8BB0;&#x5206;&#x724C;
        sb.prep_ships()
        &#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;&#x3002;

我们又加了一个sb参数,按ctrl+alt+h查看调用此方法的函数,依次修改函数形参
* 最终结果

python小游戏----外星人入侵

完结撒花,本来没想到需要这么多字,打的我手疼(哭)。。。。没有辛劳也有苦劳,祝点赞的各位看官bug少少,头发多多~~~~

Original: https://blog.csdn.net/qq_61579824/article/details/124090681
Author: 烟花落607
Title: python小游戏—-外星人入侵

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/779233/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球