《Python编程:从入门到实践》外星人入侵

news2024/12/24 3:31:22

一、规划

在游戏《外星人入侵》中,玩家控制着一艘最初出现在屏幕底部中央的飞船。玩家可以使用箭头键左右移动飞船,还可使用空格键进行射击。游戏开始时,一群外星人出现在天空中,他们在屏
幕中向下移动。玩家的任务是射杀这些外星人。玩家将所有外星人都消灭干净后,将出现一群新的外星人,他们移动的速度更快。只要有外星人撞到了玩家的飞船或到达了屏幕底部,玩家就损失一艘飞船。玩家损失三艘飞船后,游戏结束。

二、依赖库

Pygame

三、创建飞船

1.创建Pygame窗口以及响应用户输入

import sys
import pygame

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.type == pygame.QUIT:
                sys.exit()
        #让最近绘制的屏幕可见
        pygame.display.flip()

run_game()

pygame.init()初始化背景设置,让Pygame能够正确地工作。调用pygame.display.set_mode()来创建一个名为screen的显示窗口,这个游戏的所有图形元素都将在其中绘制。实参(1200, 800)是一个元组,指定了游戏窗口的尺寸。

对象screen是一个surface。在Pygame中,surface是屏幕的一部分,用于显示游戏元素。在这个游戏中,每个元素都是一个surface。display.set_mode()返回的surface表示整个游戏窗口。我们激活游戏的动画循环后,每经过一次循环都将自动重绘这个surface。

这个游戏由一个while循环控制,其中包含一个事件循环以及管理屏幕更新的代码。事件是用户玩游戏时执行的操作,如按键或移动鼠标。为让程序响应事件,我们编写一个事件循环,以侦听事件,并根据发生的事件执行相应的任务。

为访问Pygame检测到的事件,我们使用方法pygame.event.get()。所有键盘和鼠标事件都将促使for循环运行。在这个循环中,我们将编写一系列的if语句来检测并响应特定的事件。玩家单击游戏窗口的关闭按钮时,将检测到pygame.QUIT事件,而我们调用sys.exit()来退出游戏。

调用了pygame.display.flip(),命令Pygame让最近绘制的屏幕可见。在这里,它在每次执行while循环时都绘制一个空屏幕,并擦去旧屏幕,使得只有新屏幕可见。在我们移动游戏元素时,pygame.display.flip()将不断更新屏幕,以显示元素的新位置,并在原来的位置隐藏元素,从而营造平滑移动的效果。

2.设置背景色

import sys
import pygame

def run_game():
    # 初始化游戏并创建一个屏幕对象
    pygame.init()
    screen=pygame.display.set_mode((1200,600))
    pygame.display.set_caption("Alien Invasion")
    # 设置背景色
    bg_color = (230, 230, 230)
    #游戏开始的主循环
    while True:
        #监视键盘和鼠标
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
        # 每次循环时都重绘屏幕
        screen.fill(bg_color)
        #让最近绘制的屏幕可见
        pygame.display.flip()

run_game()

在Pygame中,颜色是以RGB值指定的。这种颜色由红色、绿色和蓝色值组成,其中每个值的可能取值范围都为0~255。我们调用方法screen.fill(),用背景色填充屏幕;这个方法只接受一个实参:一种颜色。

3.创建设置类

class Settings():
    """存储《外星人入侵》的所有设置的类"""
    def __init__(self):
        """初始化游戏的设置"""
    # 屏幕设置
        self.screen_width = 1200
        self.screen_height = 600
        self.bg_color = (230, 230, 230)

import sys
import pygame
from settings import Settings

def run_game():
    # 初始化游戏并创建一个屏幕对象
    pygame.init()
    ai_settings = Settings()
    screen=pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
    pygame.display.set_caption("Alien 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()

run_game()

编写一个名为settings的模块,其中包含一个名为Settings的类,用于将所有设置存储在一个地方,以免在代码中到处添加设置。这样,我们就能传递一个设置对象,而不是众多不同的设置。

4.添加飞船图像

在游戏中几乎可以使用任何类型的图像文件,但使用位图(.bmp)文件最为简单,因为Pygame默认加载位图。虽然可配置Pygame以使用其他文件类型,但有些文件类型要求你在计算机上安装相应的图像库。图灵社区

5.创建Ship类

import pygame

class Ship():
    def __init__(self,screen):
        """初始化飞船并设置其初始位置"""
        self.screen = screen
        # 加载飞船图像并获取其外接矩形
        self.image = pygame.image.load('images/ship.bmp')
        self.rect = self.image.get_rect()
        self.screen_rect = screen.get_rect()
        # 将每艘新飞船放在屏幕底部中央
        self.rect.centerx=self.screen_rect.centerx
        self.rect.bottom=self.screen_rect.bottom

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

Ship的方法__init__()接受两个参数:引用self和screen,其中后者指定了要将飞船绘制到什么地
方。为加载图像,我们调用了pygame.image.load()。这个函数返回一个表示飞船的surface,而我们将这个surface存储到了self.image中。

加载图像后,我们使用get_rect()获取相应surface的属性rect。Pygame的效率之所以如此高,一个原因是它让你能够像处理矩形一样处理游戏元素,即便它们的形状并非矩形。

处理rect对象时,可使用矩形四角和中心的 x 和 y 坐标。可通过设置这些值来指定矩形的位置。要将游戏元素居中,可设置相应rect对象的属性center、centerx或centery。要让游戏元素与屏幕边缘对齐,可使用属性top、bottom、left或right;要调整游戏元素的水平或垂直位置,可使用属性x和y,它们分别是相应矩形左上角的 x 和 y 坐标。

6.在屏幕上绘制飞船

import sys
import pygame
from settings import Settings
from ship import Ship
def run_game():
    # 初始化游戏并创建一个屏幕对象
    pygame.init()
    ai_settings = Settings()
    screen=pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
    pygame.display.set_caption("Alien 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()

run_game()

7.重构:模块game_functions

在大型项目中,经常需要在添加新代码前重构既有代码。重构旨在简化既有代码的结构,使其更容易扩展。

(1)函数check_events()

import pygame
import sys

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

(2)函数update_screen()

import pygame
import sys

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

def update_screen(ai_settings,screen,ship):
    """更新屏幕上的图像,并切换到新屏幕"""
    # 每次循环时都重绘屏幕
    screen.fill(ai_settings.bg_color)
    ship.blitme()
    # 让最近绘制的屏幕可见
    pygame.display.flip()

8.驾驶飞船

(1)响应按键

每当用户按键时,都将在Pygame中注册一个事件。事件都是通过方法pygame.event.get()获取的,因此在函数check_events()中,我们需要指定要检查哪些类型的事件。每次按键都被注册为一个KEYDOWN事件。

def check_events(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.rect.center_x += 1

(2)允许不断移动

玩家按住右箭头键不放时,我们希望飞船不断地向右移动,直到玩家松开为止。我们将让游戏检测pygame.KEYUP事件,以便玩家松开右箭头键时我们能够知道这一点;然后,我们将结合使用KEYDOWN和KEYUP事件,以及一个名为moving_right的标志来实现持续移动。

飞船不动时,标志moving_right将为False。玩家按下右箭头键时,我们将这个标志设置为True;而玩家松开时,我们将这个标志重新设置为False。

飞船的属性都由Ship类控制,因此我们将给这个类添加一个名为moving_right的属性和一个名为update()的方法。方法update()检查标志moving_right的状态,如果这个标志为True,就调整飞船的位置。每当需要调整飞船的位置时,我们都调用这个方法。

import pygame

class Ship():
    def __init__(self,screen):
        """初始化飞船并设置其初始位置"""
        self.screen = screen
        # 加载飞船图像并获取其外接矩形
        self.image = pygame.image.load('images/ship.bmp')
        self.rect = self.image.get_rect()
        self.screen_rect = screen.get_rect()
        # 将每艘新飞船放在屏幕底部中央
        self.rect.centerx=self.screen_rect.centerx
        self.rect.bottom=self.screen_rect.bottom
        # 移动标志
        self.moving_right = False
    def update(self):
        """根据移动标志调整飞船的位置"""
        if self.moving_right:
            self.rect.centerx+=1
    def blitme(self):
        """在指定位置绘制飞船"""
        self.screen.blit(self.image, self.rect)
def check_events(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

修改alien_invasion.py中的while循环 

    while True:
        #监视键盘和鼠标
        gf.check_events(ship)
        ship.update()
        gf.update_screen(ai_settings, screen, ship)

(3)左右移动

ship.py更新

import pygame

class Ship():
    def __init__(self,screen):
        """初始化飞船并设置其初始位置"""
        self.screen = screen
        # 加载飞船图像并获取其外接矩形
        self.image = pygame.image.load('images/ship.bmp')
        self.rect = self.image.get_rect()
        self.screen_rect = screen.get_rect()
        # 将每艘新飞船放在屏幕底部中央
        self.rect.centerx=self.screen_rect.centerx
        self.rect.bottom=self.screen_rect.bottom
        # 移动标志
        self.moving_right = False
        self.moving_left =False
    def update(self):
        """根据移动标志调整飞船的位置"""
        if self.moving_right:
            self.rect.centerx+=1
        if self.moving_left:
            self.rect.centerx-=1
    def blitme(self):
        """在指定位置绘制飞船"""
        self.screen.blit(self.image, self.rect)

game_functions.py更新

def check_events(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.key == pygame.K_LEFT:
                ship.moving_left = True
        elif event.type == pygame.KEYUP:
            if event.key == pygame.K_RIGHT:
                ship.moving_right = False
            elif event.key == pygame.K_LEFT:
                ship.moving_left = False

(4)调整飞船的速度

在Settings类中添加属性ship_speed_factor,用于控制飞船的速度。

class Settings():
    """存储《外星人入侵》的所有设置的类"""
    def __init__(self):
        """初始化游戏的设置"""
        # 屏幕设置
        self.screen_width = 1200
        self.screen_height = 600
        self.bg_color = (230, 230, 230)
        self.ship_speed_factor = 1.5

通过将速度设置指定为小数值,可在后面加快游戏的节奏时更细致地控制飞船的速度。然而,rect的centerx等属性只能存储整数值,因此我们需要对Ship类做些修改。

import pygame

class Ship():
    def __init__(self,ai_settings,screen):
        """初始化飞船并设置其初始位置"""
        self.screen = screen
        self.ai_settings = ai_settings
        # 加载飞船图像并获取其外接矩形
        self.image = pygame.image.load('images/ship.bmp')
        self.rect = self.image.get_rect()
        self.screen_rect = screen.get_rect()
        # 将每艘新飞船放在屏幕底部中央
        self.rect.centerx=self.screen_rect.centerx
        self.rect.bottom=self.screen_rect.bottom
        # 在飞船的属性center中存储小数值
        self.center = float(self.rect.centerx)
        # 移动标志
        self.moving_right = False
        self.moving_left =False
    def update(self):
        """根据移动标志调整飞船的位置"""
        # 更新飞船的center值,而不是rect
        if self.moving_right:
            self.center +=self.ai_settings.ship_speed_factor
        if self.moving_left:
            self.center -=self.ai_settings.ship_speed_factor
        # 根据self.center更新rect对象
        self.rect.centerx=self.center
    def blitme(self):
        """在指定位置绘制飞船"""
        self.screen.blit(self.image, self.rect)

(5)限制飞船的活动范围

将修改Ship类的方法update()

    def update(self):
        """根据移动标志调整飞船的位置"""
        # 更新飞船的center值,而不是rect
        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
        # 根据self.center更新rect对象
        self.rect.centerx=self.center

(6)重构check_events()

我们将其部分代码放在两个函数中:一个处理KEYDOWN事件,另一个处理KEYUP事件

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):
    """响应按键和鼠标事件"""
    for event in pygame.event.get():
        if event.type ==pygame.QUIT:
            sys.exit()
        elif event.type == pygame.KEYDOWN:
            check_keydown_events(event,ship)
        elif event.type == pygame.KEYUP:
            check_keyup_events(event, ship)

9.射击

(1)添加子弹设置

class Settings():
    """存储《外星人入侵》的所有设置的类"""
    def __init__(self):
        """初始化游戏的设置"""
        # 屏幕设置
        self.screen_width = 1200
        self.screen_height = 600
        self.bg_color = (230, 230, 230)
        self.ship_speed_factor = 1.5
        # 子弹设置
        self.bullet_speed_factor = 1
        self.bullet_width = 3
        self.bullet_height = 15
        self.bullet_color = 60, 60, 60

(2)创建Bullet类

import pygame
from pygame.sprite import Sprite

class Bullet(Sprite):
    """一个对飞船发射的子弹进行管理的类"""
    def __init__(self, ai_settings, screen, ship):
        """在飞船所处的位置创建一个子弹对象"""
        super().__init__()
        self.screen = screen
        # 在(0,0)处创建一个表示子弹的矩形,再设置正确的位置
        self.rect=pygame.Rect(0,0,ai_settings.bullet_width,ai_settings.bullet_height)
        self.rect.centerx=ship.rect.centerx
        self.rect.top=ship.rect.top
        # 存储用小数表示的子弹位置
        self.y=float(self.rect.y)
        self.color = ai_settings.bullet_color
        self.speed_factor = ai_settings.bullet_speed_factor
    def update(self):
        """向上移动子弹"""
        # 更新表示子弹位置的小数值
        self.y-=self.ship_speed_factor
        # 更新表示子弹的rect的位置
        self.rect.y=self.y
    def draw_bullet(self):
        """在屏幕上绘制子弹"""
        pygame.draw.rect(self.screen, self.color, self.rect)

Bullet类继承了我们从模块pygame.sprite中导入的Sprite类。通过使用Sprite,可将游戏中相关的元素编组,进而同时操作编组中的所有元素。

需要绘制子弹时,我们调用draw_bullet()。函数draw.rect()使用存储在self.color中的颜色填充表示子弹的rect占据的屏幕部分。

(3)将子弹存储到编组中

import pygame

from settings import Settings
from ship import Ship
import game_functions as gf
from pygame.sprite import Group
def run_game():
    # 初始化游戏并创建一个屏幕对象
    pygame.init()
    ai_settings = Settings()
    screen=pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
    pygame.display.set_caption("Alien Invasion")
    ship = Ship(ai_settings,screen)
    # 创建一个用于存储子弹的编组
    bullets = Group()
    #游戏开始的主循环
    while True:
        #监视键盘和鼠标
        gf.check_events(ai_settings, screen, ship, bullets)
        ship.update()
        bullets.update()
        gf.update_screen(ai_settings, screen, ship, bullets)

run_game()

我们将在alien_invasion.py中创建一个编组(group),用于存储所有有效的子弹,以便能够管理发射出去的所有子弹。这个编组将是pygame.sprite.Group类的一个实例;pygame.sprite.Group类类似于列表,但提供了有助于开发游戏的额外功能。

我们将bullets传递给了check_events()和update_screen()。在check_events()中,需要在玩家按空格键时处理bullets;而在update_screen()中,需要更新要绘制到屏幕上的bullets。当你对编组调用update()时,编组将自动对其中的每个精灵调用update(),因此代码行bullets.update()将为编组bullets中的每颗子弹调用bullet.update()。

(4)开火

在game_functions.py中,我们需要修改check_keydown_events(),以便在玩家按空格键时发射一颗子弹。我们无需修改check_keyup_events(),因为玩家松开空格键时什么都不会发生。我们还需修改update_screen(),确保在调用flip()前在屏幕上重绘每颗子弹。

import pygame
import sys
from bullet import Bullet

def check_keydown_events(event,ai_settings, screen, ship,
bullets):
    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:
        # 创建一颗子弹,并将其加入到编组bullets中
        new_bullet = Bullet(ai_settings, screen, ship)
        bullets.add(new_bullet)

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(ai_settings, screen, ship, bullets):
    """响应按键和鼠标事件"""
    for event in pygame.event.get():
        if event.type ==pygame.QUIT:
            sys.exit()
        elif event.type == pygame.KEYDOWN:
            check_keydown_events(event, ai_settings, screen, ship,
                                 bullets)
        elif event.type == pygame.KEYUP:
            check_keyup_events(event, ship)

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

编组bulltes传递给了check_keydown_events()。玩家按空格键时,创建一颗新子弹,并使用方法add()将其加入到编组bullets中。

我们给在屏幕上绘制子弹的update_screen()添加了形参bullets。方法bullets.sprites()返回一个列表,其中包含编组bullets中的所有sprite。为在屏幕上绘制发射的所有子弹,我们遍历编组bullets中的sprite,并对每个sprite都调用draw_bullet()

(5)删除已消失的子弹

我们检查每颗子弹,看看它是否已从屏幕顶端消失。如果是这样,就将其从bullets中删除。我们使用了一条print语句,以显示当前还有多少颗子弹,从而核实已消失的子弹确实删除了。

#游戏开始的主循环
    while True:
        #监视键盘和鼠标
        gf.check_events(ai_settings, screen, ship, bullets)
        ship.update()
        bullets.update()

        # 删除已消失的子弹
        for bullet in bullets.copy():
            if bullet.rect.bottom <= 0: 
                bullets.remove(bullet)
        print(len(bullets))

        gf.update_screen(ai_settings, screen, ship, bullets)

(6)限制子弹数量

在settings.py中存储所允许的最大子弹数

 # 子弹设置
        self.bullet_speed_factor = 1
        self.bullet_width = 3
        self.bullet_height = 15
        self.bullet_color = 60, 60, 60
        self.bullets_allowed = 3

在game_functions.py的check_keydown_events()中,我们在创建新子弹前检查未消失的子弹数是否小于该设置

    elif event.key == pygame.K_SPACE:
        # 创建一颗子弹,并将其加入到编组bullets中
        if len(bullets) < ai_settings.bullets_allowed:
            new_bullet = Bullet(ai_settings, screen, ship)
            bullets.add(new_bullet)

(7)创建函数update_bullets()

我们创建一个名为update_bullets()的新函数,并将其添加到game_functions.py的末尾

def update_bullets(bullets):
    """更新子弹的位置,并删除已消失的子弹"""
    # 更新子弹的位置
    bullets.update()
    # 删除已消失的子弹
    for bullet in bullets.copy():
        if bullet.rect.bottom <= 0:
            bullets.remove(bullet)

alien_invasion.py中的while循环又变得很简单

#游戏开始的主循环
    while True:
        #监视键盘和鼠标
        gf.check_events(ai_settings, screen, ship, bullets)
        ship.update()
        gf.update_bullets(bullets)
        gf.update_screen(ai_settings, screen, ship, bullets)

(8)创建函数fire_bullet()

下面将发射子弹的代码移到一个独立的函数中,这样,在check_keydown_events()中只需使用一行代码来发射子弹,让elif代码块变得非常简单.


def check_keydown_events(event,ai_settings, screen, ship,
bullets):
    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_bullet(ai_settings, screen, ship, bullets)


def fire_bullet(ai_settings, screen, ship, bullets):
    """如果还没有到达限制,就发射一颗子弹"""
    # 创建新子弹,并将其加入到编组bullets中
    if len(bullets) < ai_settings.bullets_allowed:
        new_bullet = Bullet(ai_settings, screen, ship)
        bullets.add(new_bullet)

(9)添加一个结束游戏的快捷键Q

def check_keydown_events(event,ai_settings, screen, ship,
bullets):
    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_bullet(ai_settings, screen, ship, bullets)
    elif event.key == pygame.K_q:
        sys.exit()

四、创建外星人

1.创建Alien类

import pygame
from pygame.sprite import Sprite

class Alien(Sprite):
    """表示单个外星人的类"""
    def __init__(self, ai_settings, screen):
        """初始化外星人并设置其起始位置"""
        super().__init__()
        self.screen = screen
        self.ai_settings = ai_settings
        # 加载外星人图像,并设置其rect属性
        self.image = pygame.image.load('images/alien.bmp')
        self.rect = self.image.get_rect()
        # 每个外星人最初都在屏幕左上角附近
        self.rect.x = self.rect.width
        self.rect.y = self.rect.height
        # 存储外星人的准确位置
        self.x = float(self.rect.x)
    def blitme(self):
        """在指定位置绘制外星人"""
        self.screen.blit(self.image, self.rect)
        

2.创建Alien实例

import pygame

from settings import Settings
from ship import Ship
import game_functions as gf
from alien import Alien
from pygame.sprite import Group
def run_game():
    # 初始化游戏并创建一个屏幕对象
    pygame.init()
    ai_settings = Settings()
    screen=pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
    pygame.display.set_caption("Alien Invasion")
    ship = Ship(ai_settings,screen)
    # 创建一个用于存储子弹的编组
    bullets = Group()
    # 创建一个外星人
    alien = Alien(ai_settings, screen)
    #游戏开始的主循环
    while True:
        #监视键盘和鼠标
        gf.check_events(ai_settings, screen, ship, bullets)
        ship.update()
        gf.update_bullets(bullets)
        gf.update_screen(ai_settings, screen, ship, alien,bullets)

run_game()

3.让外星人出现在屏幕上

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

4.创建一群外星人

为创建一行外星人,首先在alien_invasion.py中创建一个名为aliens的空编组,用于存储全部外星人,再调用game_functions.py中创建外星人群的函数

import pygame

from settings import Settings
from ship import Ship
import game_functions as gf
from pygame.sprite import Group
def run_game():
    # 初始化游戏并创建一个屏幕对象
    pygame.init()
    ai_settings = Settings()
    screen=pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
    pygame.display.set_caption("Alien Invasion")
    # 创建一艘飞船、一个子弹编组和一个外星人编组
    ship = Ship(ai_settings, screen)
    bullets = Group()
    aliens = Group()
    # 创建外星人群
    gf.create_fleet(ai_settings, screen, aliens)
    #游戏开始的主循环
    while True:
        #监视键盘和鼠标
        gf.check_events(ai_settings, screen, ship, bullets)
        ship.update()
        gf.update_bullets(bullets)
        gf.update_screen(ai_settings, screen, ship, aliens,
                         bullets)

run_game()

我们还需要修改update_screen()

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

5.创建外星人群

下面是新函数create_fleet()

def create_fleet(ai_settings, screen, aliens):
    """创建外星人群"""
    # 创建一个外星人,并计算一行可容纳多少个外星人
    # 外星人间距为外星人宽度
    alien = Alien(ai_settings, screen)
    alien_width = alien.rect.width
    available_space_x = ai_settings.screen_width - 2 * alien_width
    number_aliens_x = int(available_space_x / (2 * alien_width))
    # 创建第一行外星人
    for alien_number in range(number_aliens_x):
        # 创建一个外星人并将其加入当前行
        alien = Alien(ai_settings, screen)
        alien.x = alien_width + 2 * alien_width * alien_number
        alien.rect.x = alien.x
        aliens.add(alien)

(1)重构create_fleet()

下面是create_fleet()和两个新函数,get_number_aliens_x()和create_alien()。

def get_number_aliens_x(ai_settings, alien_width):
    """计算每行可容纳多少个外星人"""
    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, 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)
def create_fleet(ai_settings, screen, aliens):
    """创建外星人群"""
    # 创建一个外星人,并计算每行可容纳多少个外星人
    alien = Alien(ai_settings, screen)
    number_aliens_x = get_number_aliens_x(ai_settings,
                                          alien.rect.width)
    # 创建第一行外星人
    for alien_number in range(number_aliens_x):
        create_alien(ai_settings, screen, aliens, alien_number)

(2)添加行

def create_fleet(ai_settings, screen, ship,aliens):
    """创建外星人群"""
    # 创建一个外星人,并计算每行可容纳多少个外星人
    alien = Alien(ai_settings, screen)
    number_aliens_x = get_number_aliens_x(ai_settings,
                                          alien.rect.width)
    number_rows = get_number_rows(ai_settings, ship.rect.height,
                                  alien.rect.height)
    # 创建外星人群
    for row_number in range(number_rows):
        for alien_number in range(number_aliens_x):
            create_alien(ai_settings, screen, aliens,alien_number,row_number)

def get_number_rows(ai_settings, ship_height, alien_height):
    """计算屏幕可容纳多少行外星人"""
    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_alien(ai_settings, screen, aliens, alien_number,row_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
    alien.rect.y = alien.rect.height + 2 * alien.rect.height * row_number
    aliens.add(alien)

6.让外星人群移动

(1)向右移动外星人

我们将使用alien.py中的方法update(),且对外星人群中的每个外星人都调用它。

def __init__(self):
    --snip--
    # 外星人设置
    self.alien_speed_factor = 1
    def update(self):
        """向右移动外星人"""
        self.x += self.ai_settings.alien_speed_factor 
        self.rect.x = self.x

在主while循环中已调用了更新飞船和子弹的方法,但现在还需更新每个外星人的位置:

#游戏开始的主循环
    while True:
        #监视键盘和鼠标
        gf.check_events(ai_settings, screen, ship, bullets)
        ship.update()
        gf.update_bullets(bullets)
        gf.update_aliens(aliens)
        gf.update_screen(ai_settings, screen, ship, aliens,
                         bullets)

最后,在文件game_functions.py末尾添加新函数update_aliens()

def update_aliens(aliens):
    """更新外星人群中所有外星人的位置"""
    aliens.update()

我们对编组aliens调用方法update(),这将自动对每个外星人调用方法update()。

(2)创建表示外星人移动方向的设置

下面来创建让外星人撞到屏幕右边缘后向下移动、再向左移动的设置。

# 外星人设置
self.alien_speed_factor = 1
self.fleet_drop_speed = 10
# fleet_direction为1表示向右移,为-1表示向左移
self.fleet_direction = 1

设置fleet_drop_speed指定了有外星人撞到屏幕边缘时,外星人群向下移动的速度。

(3)检查外星人是否撞到了屏幕边缘

需要编写一个方法来检查是否有外星人撞到了屏幕边缘,还需修改update(),以让每个外星人都沿正确的方向移动。

    def update(self):
        """向左或向右移动外星人"""
        self.x += (self.ai_settings.alien_speed_factor * self.ai_settings.fleet_direction)
        self.rect.x = self.x

    def check_edges(self):
        """如果外星人位于屏幕边缘,就返回True"""
        screen_rect = self.screen.get_rect()
        if self.rect.right >= screen_rect.right:
            return True
        elif self.rect.left <= 0:
            return True

(4)向下移动外星人群并改变移动方向

需要对game_functions.py做重大修改,我们编写函数check_fleet_edges()和change_fleet_direction(),并对update_aliens()进行修改。

def check_fleet_edges(ai_settings, aliens):
    """有外星人到达边缘时采取相应的措施"""
    for alien in aliens.sprites():
        if alien.check_edges():
            change_fleet_direction(ai_settings, aliens)
            break
def change_fleet_direction(ai_settings, aliens):
    """将整群外星人下移,并改变它们的方向"""
    for alien in aliens.sprites():
        alien.rect.y += ai_settings.fleet_drop_speed
        ai_settings.fleet_direction *= -1
def update_aliens(ai_settings, aliens):
    """检查是否有外星人位于屏幕边缘,并更新整群外星人的位置"""
    check_fleet_edges(ai_settings, aliens)
    aliens.update()

alien_invasion.py也需要进行相应修改

# 开始游戏主循环
    while True:
        gf.check_events(ai_settings, screen, ship, bullets)
        ship.update()
        gf.update_bullets(bullets)
        gf.update_aliens(ai_settings, aliens)
        gf.update_screen(ai_settings, screen, ship, aliens,
                     bullets)

五、射杀外星人

我们将使用sprite.groupcollide()检测两个编组的成员之间的碰撞。

1.检测子弹与外星人的碰撞

方法sprite.groupcollide()将每颗子弹的rect同每个外星人的rect进行比较,并返回一个字典,其中包含发生了碰撞的子弹和外星人。

在函数update_bullets()中,使用下面的代码来检查碰撞。

def update_bullets(aliens, bullets):
    """更新子弹的位置,并删除已消失的子弹"""
    # 更新子弹的位置
    bullets.update()
    # 删除已消失的子弹
    for bullet in bullets.copy():
        if bullet.rect.bottom <= 0:
            bullets.remove(bullet)
    # 检查是否有子弹击中了外星人
    # 如果是这样,就删除相应的子弹和外星人
    collisions = pygame.sprite.groupcollide(bullets, aliens, True,
                                            True)

每当有子弹和外星人的rect重叠时,groupcollide()就在它返回的字典中添加一个键-值对。两个实参True告诉Pygame删除发生碰撞的子弹和外星人。 

我们调用update_bullets()时,传递了实参aliens

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

2.生成新的外星人群

首先需要检查编组aliens是否为空。如果为空,就调用create_fleet()。我们将在update_bullets()中执行这种检查,因为外星人都是在这里被消灭的。

def update_bullets(ai_settings, screen, ship, aliens, bullets):
    --snip--
    # 检查是否有子弹击中了外星人
    # 如果是,就删除相应的子弹和外星人
    collisions = pygame.sprite.groupcollide(bullets, aliens, True,True)
    if len(aliens) == 0: 
        # 删除现有的子弹并新建一群外星人
        bullets.empty() 
        create_fleet(ai_settings, screen, ship, aliens)

我们需要更新alien_invasion.py中对update_bullets()的调用

# 开始游戏主循环
while True:
    gf.check_events(ai_settings, screen, ship, bullets)
    ship.update()
    gf.update_bullets(ai_settings, screen, ship, aliens,bullets)
    gf.update_aliens(ai_settings, aliens)
    gf.update_screen(ai_settings, screen, ship, aliens,bullets)

3.重构update_bullets()

def update_bullets(ai_settings, screen, ship, aliens, bullets):
    --snip--
    # 删除已消失的子弹
    for bullet in bullets.copy():
        if bullet.rect.bottom <= 0:
            bullets.remove(bullet)
    check_bullet_alien_collisions(ai_settings, screen, ship,aliens, bullets)
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()
        create_fleet(ai_settings, screen, ship, aliens)

我们创建了一个新函数——check_bullet_alien_collisions(),以检测子弹和外星人之间的碰撞,以及在整群外星人都被消灭干净时采取相应的措施。这避免了update_bullets()太长。

六、结束游戏

1.检测外星人和飞船碰撞

我们在更新每个外星人的位置后立即检测外星人和飞船之间的碰撞。

def update_aliens(ai_settings, ship, aliens):
    """检查是否有外星人到达屏幕边缘然后更新所有外星人的位置"""
    check_fleet_edges(ai_settings, aliens)
    aliens.update()
    # 检测外星人和飞船之间的碰撞
    if pygame.sprite.spritecollideany(ship, aliens): 
        print("Ship hit!!!")

方法spritecollideany()接受两个实参:一个sprite和一个编组。它检查编组是否有成员与sprite发生了碰撞,并在找到与sprite发生了碰撞的成员后就停止遍历编组。

如果没有发生碰撞,spritecollideany()将返回None,因此if代码块不会执行。如果找到了与飞船发生碰撞的外星人,它就返回这个外星人。

现在,我们需要将ship传递给update_aliens()

# 开始游戏主循环
while True:
    gf.check_events(ai_settings, screen, ship, bullets)
    ship.update()
    gf.update_bullets(ai_settings, screen, ship, aliens,bullets)
    gf.update_aliens(ai_settings, ship, aliens)
    gf.update_screen(ai_settings, screen, ship, aliens,bullets)

2.响应外星人和飞船碰撞

编写一个用于跟踪游戏统计信息的新类——GameStats,并将其保存为文件game_stats.py

class GameStats():
    """跟踪游戏的统计信息"""
    def __init__(self, ai_settings):
        """初始化统计信息"""
        self.ai_settings = ai_settings
        self.reset_stats() 
    def reset_stats(self):
        """初始化在游戏运行期间可能变化的统计信息"""
        self.ships_left = self.ai_settings.ship_limit

在这个游戏运行期间,我们只创建一个GameStats实例,但每当玩家开始新游戏时,需要重置一些统计信息。为此,我们在方法reset_stats()中初始化大部分统计信息,而不是在__init__()中直接初始化它们。

一开始玩家拥有的飞船数存储在settings.py的ship_limit中:

# 飞船设置
self.ship_speed_factor = 1.5
self.ship_limit = 3

我们还需对alien_invasion.py做些修改,以创建一个GameStats实例

--snip--
from settings import Settings
from game_stats import GameStats 
--snip--
def run_game():
    --snip--
    pygame.display.set_caption("Alien Invasion")
    # 创建一个用于存储游戏统计信息的实例
    stats = GameStats(ai_settings) 
    --snip--
    # 开始游戏主循环
    while True:
        --snip--
        gf.update_bullets(ai_settings, screen, ship, aliens,bullets)
        gf.update_aliens(ai_settings, stats, screen, ship, aliens,bullets) 
        --snip--

实现响应功能的大部分代码放到函数ship_hit()中

import sys
from time import sleep 
import pygame
--snip--
def ship_hit(ai_settings, stats, screen, ship, aliens, bullets):
    """响应被外星人撞到的飞船"""
    # 将ships_left减1
    stats.ships_left -= 1 
    # 清空外星人列表和子弹列表
    aliens.empty() 
    bullets.empty()
    # 创建一群新的外星人,并将飞船放到屏幕底端中央
    create_fleet(ai_settings, screen, ship, aliens) 
    ship.center_ship()
    # 暂停
    sleep(0.5) 
def update_aliens(ai_settings, stats, screen, ship, aliens,bullets): 
    --snip--
    # 检测外星人和飞船碰撞
    if pygame.sprite.spritecollideany(ship, aliens):
        ship_hit(ai_settings, stats, screen, ship, aliens,bullets)
def center_ship(self):
    """让飞船在屏幕上居中"""
    self.center = self.screen_rect.centerx

3.有外星人到达屏幕底端

def check_aliens_bottom(ai_settings, stats, screen, 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, ship, aliens,bullets)
            break
def update_aliens(ai_settings, stats, screen, ship, aliens,bullets):
    --snip--
    # 检查是否有外星人到达屏幕底端
    check_aliens_bottom(ai_settings, stats, screen, ship, aliens,bullets) 

4.游戏结束

下面在GameStats中添加一个作为标志的属性game_active,以便在玩家的飞船用完后结束游戏

def __init__(self, settings):
    --snip--
    # 游戏刚启动时处于活动状态
    self.game_active = True

现在在ship_hit()中添加代码,在玩家的飞船都用完后将game_active设置为False

def ship_hit(ai_settings, stats, screen, ship, aliens, bullets):
    """响应飞船被外星人撞到"""
    if stats.ships_left > 0:    
        # 将ships_left减1
        stats.ships_left -= 1
        --snip--
         #暂停一会儿
        sleep(0.5)
    else:
        stats.game_active = False

七、确定应运行游戏的哪些部分

在alien_invasion.py中,我们需要确定游戏的哪些部分在任何情况下都应运行,哪些部分仅在游戏处于活动状态时才运行

# 开始游戏主循环
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, ship, aliens,bullets)

八、记分

1.添加Play按钮

当前,这个游戏在玩家运行alien_invasion.py时就开始了。下面让游戏一开始处于非活动状态,并提示玩家单击Play按钮来开始游戏。为此,在game_stats.py中输入如下代码

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

(1)创建Button类

我们创建一个Button类,用于创建带标签的实心矩形。将这个类保存为文件button.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能够将文本渲染到屏幕上。Pygame通过将你要显示的字符串渲染为图像来处理文本。我们调用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

方法prep_msg()接受实参self以及要渲染为图像的文本。调用font.render()将存储在msg中的文本转换为图像,然后将该图像存储在msg_image中。方法font.render()还接受一个布尔实参,该实参指定开启还是关闭反锯齿功能。余下的两个实参分别是文本颜色和背景色。我们启用了反锯齿功能,并将文本的背景色设置为按钮的颜色。

我们让文本图像在按钮上居中:根据文本图像创建一个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)

我们调用screen.fill()来绘制表示按钮的矩形,再调用screen.blit(),并向它传递一幅图像以及与该图像相关联的rect对象,从而在屏幕上绘制文本图像。

(2)在屏幕上绘制按钮

--snip--
from game_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按钮.

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()

(3)开始游戏

为在玩家单击Play按钮时开始新游戏,需在game_functions.py中添加如下代码,以监视与这个按钮相关的鼠标事件

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(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

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

# 开始游戏主循环
while True:
    gf.check_events(ai_settings, screen, stats, play_button,ship,bullets)
    --snip--

(4)重置游戏

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

def check_play_button(ai_settings, screen, stats, play_button,ship, aliens,bullets, mouse_x, mouse_y):
    """在玩家单击Play按钮时开始新游戏"""
    if play_button.rect.collidepoint(mouse_x, mouse_y):
        # 重置游戏统计信息
        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:
        --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)

下面来修改alien_invasion.py中调用check_events()的代码,以将实参aliens传递给它

# 开始游戏主循环
while True:
    gf.check_events(ai_settings, screen, stats, play_button,ship,aliens, bullets)
    --snip--

(5)将Play按钮切换到非活动状态

当前,Play按钮存在一个问题,那就是即便Play按钮不可见,玩家单击其原来所在的区域时,游戏依然会作出响应。为修复这个问题,可让游戏仅在game_active为False时才开始.

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 button_clicked and not stats.game_active: 
        #重置游戏统计信息
        --snip--

标志button_clicked的值为True或False,仅当玩家单击了Play按钮且游戏当前处于非活动状态时,游戏才重新开始。

(6)隐藏光标

我们在游戏处于活动状态时让光标不可见

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 button_clicked and not stats.game_active:
        # 隐藏光标
        pygame.mouse.set_visible(False)
        --snip--

通过向set_visible()传递False,让Pygame在光标位于游戏窗口内时将其隐藏起来。游戏结束后,我们将重新显示光标,让玩家能够单击Play按钮来开始新游戏。

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

2.提高等级

(1)修改速度设置

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

def __init__(self):
    """初始化游戏的静态设置"""
    # 屏幕设置
    self.screen_width = 1200
    self.screen_height = 800
    self.bg_color = (230, 230, 230)
    # 飞船设置
    self.ship_limit = 3
    # 子弹设置
    self.bullet_width = 3
    self.bullet_height = 15
    self.bullet_color = 60, 60, 60
    self.bullets_allowed = 3
    # 外星人设置
    self.fleet_drop_speed = 10
    # 以什么样的速度加快游戏节奏
    self.speedup_scale = 1.1 
    self.initialize_dynamic_settings() 

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

每当玩家提高一个等级时,我们都使用increase_speed()来提高飞船、子弹和外星人的速度

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

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

def check_bullet_alien_collisions(ai_settings, screen, ship,aliens, bullets):
    --snip--
    if len(aliens) == 0:
        # 删除现有的子弹,加快游戏节奏,并创建一群新的外星人
        bullets.empty()
        ai_settings.increase_speed()
        create_fleet(ai_settings, screen, ship, aliens)

(2)重置速度

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 button_clicked and not stats.game_active:
        # 重置游戏设置
        ai_settings.initialize_dynamic_settings()
        # 隐藏光标
        pygame.mouse.set_visible(False)
        --snip--

3.记分

下面来实现一个记分系统,以实时地跟踪玩家的得分,并显示最高得分、当前等级和余下的飞船数。得分是游戏的一项统计信息,因此我们在GameStats中添加一个score属性。

class GameStats():
    --snip--
    def reset_stats(self):
        """初始化随游戏进行可能变化的统计信息"""
        self.ships_left = self.ai_settings.ship_limit
        self.score = 0

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

(1)显示得分

为在屏幕上显示得分,我们首先创建一个新类Scoreboard。就当前而言,这个类只显示当前得分,但后面我们也将使用它来显示最高得分、等级和余下的飞船数。下面是这个类的前半部分,它被保存为文件scoreboard.py

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() 

为将要显示的文本转换为图像,我们调用了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 

最后,我们创建方法show_score(),用于显示渲染好的得分图像

def show_score(self):
    """在屏幕上显示得分"""
    self.screen.blit(self.score_image, self.score_rect)

(2)创建记分牌

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

--snip--
from game_stats import GameStats
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()

为显示得分,将update_screen()修改成下面这样

def update_screen(ai_settings, screen, stats, sb, ship, aliens,bullets,play_button):
    --snip--
    # 显示得分
    sb.show_score()
    # 如果游戏处于非活动状态,就显示Play按钮
    if not stats.game_active:
        play_button.draw_button()
    # 让最近绘制的屏幕可见
    pygame.display.flip()

(3)在外星人被消灭时更新得分

def initialize_dynamic_settings(self):
    --snip--
    # 记分
    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()
        --snip--

我们需要修改update_bullets(),确保在函数之间传递合适的实参

def update_bullets(ai_settings, screen, stats, sb, ship, aliens,bullets):
    """更新子弹的位置,并删除已消失的子弹"""
    --snip--
    check_bullet_alien_collisions(ai_settings, screen, stats, sb,ship, aliens, bullets)

我们还需要修改主while循环中调用update_bullets()的代码

# 开始游戏主循环
while True:
    gf.check_events(ai_settings, screen, stats, play_button,ship,aliens, bullets)
    if stats.game_active:
        ship.update()
        gf.update_bullets(ai_settings, screen, stats, sb,ship, aliens,bullets)
        --snip--

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

我们的代码可能遗漏了一些被消灭的外星人。例如,如果在一次循环中有两颗子弹射中了外星人,或者因子弹更宽而同时击中了多个外星人,玩家将只能得到一个被消灭的外星人的点数。

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

def check_bullet_alien_collisions(ai_settings, screen, stats, sb,ship,aliens, bullets):
    --snip--
    if collisions:
        for aliens in collisions.values(): 
            stats.score += ai_settings.alien_points * len(aliens)
            sb.prep_score()
            --snip--

(5)提高点数

玩家每提高一个等级,游戏都变得更难,因此处于较高的等级时,外星人的点数应更高。

class Settings():
    """存储游戏《外星人入侵》的所有设置的类"""
    def __init__(self):
        --snip--
        # 加快游戏节奏的速度
        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) 

(6)将得分圆整

将得分显示为10的整数倍,还将设置得分的格式,在大数字中添加用逗号表示的千位分隔符。我们在Scoreboard中执行这种修改

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

函数round()通常让小数精确到小数点后多少位,其中小数位数是由第二个实参指定的。然而,如果将第二个实参指定为负数,round()将圆整到最近的10、100、1000等整数倍。使用了一个字符串格式设置指令,它让Python将数值转换为字符串时在其中插入逗号。

(7)最高得分

每个玩家都想超过游戏的最高得分记录。下面来跟踪并显示最高得分,给玩家提供要超越的目标。我们将最高得分存储在GameStats中。

def __init__(self, ai_settings):
    --snip--
    # 在任何情况下都不应重置最高得分
    self.high_score = 0

下面来修改Scoreboard以显示最高得分。先来修改方法__init__()

def __init__(self, ai_settings, screen, stats):
    --snip--
    # 准备包含最高得分和当前得分的图像
    self.prep_score()
    self.prep_high_score() 

方法prep_high_score()的代码如下

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 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()

在check_bullet_alien_collisions()中,每当有外星人被消灭,都需要在更新得分后调用check_high_score()

def check_bullet_alien_collisions(ai_settings, screen, stats, sb,ship,aliens, bullets):
    --snip--
    if collisions:
        for aliens in collisions.values():
            stats.score += ai_settings.alien_points * len(aliens)
            sb.prep_score()
        check_high_score(stats, sb)
    --snip--

(8)显示等级

为在游戏中显示玩家的等级,首先需要在GameStats中添加一个表示当前等级的属性。为确保每次开始新游戏时都重置等级,在reset_stats()中初始化它

def reset_stats(self):
    """初始化随游戏进行可能变化的统计信息"""
    self.ships_left = self.ai_settings.ship_limit
    self.score = 0
    self.level = 1

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

def __init__(self, ai_settings, screen, stats):
    --snip--
    # 准备包含得分的初始图像
    self.prep_score()
    self.prep_high_score()
    self.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_score()

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

def check_bullet_alien_collisions(ai_settings, screen, stats, sb,ship,aliens, bullets):
    --snip--
    if len(aliens) == 0:
        # 如果整群外星人都被消灭,就提高一个等级
        bullets.empty()
        ai_settings.increase_speed()
        # 提高等级
        stats.level += 1 
        sb.prep_level() 
        create_fleet(ai_settings, screen, ship, aliens)

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

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 button_clicked and not stats.game_active:
        --snip--
        # 重置游戏统计信息
        stats.reset_stats()
        stats.game_active = True
        # 重置记分牌图像
        sb.prep_score() 
        sb.prep_high_score()
        sb.prep_level()
        # 清空外星人列表和子弹列表
        aliens.empty()
        bullets.empty()
        --snip--

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

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)

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

# 开始游戏主循环
while True:
    gf.check_events(ai_settings, screen, stats, sb,play_button, ship,aliens, bullets)
    --snip--

(9)显示余下的飞船数

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

import pygame
from pygame.sprite import Sprite

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

接下来,需要修改Scoreboard,在其中创建一个可供显示的飞船编组。下面是其中的import语句和方法__init__()

import pygame.font
from pygame.sprite import Group
from ship import Ship

class Scoreboard():
    """报告得分信息的类"""
    def __init__(self, ai_settings, screen, stats):
        --snip--
        self.prep_level()
        self.prep_ships()
        --snip--

prep_ships()的代码如下

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

现在需要在屏幕上绘制飞船了

def show_score(self):
    --snip--
    self.screen.blit(self.level_image, self.level_rect)
    # 绘制飞船
    self.ships.draw(self.screen)

为在游戏开始时让玩家知道他有多少艘飞船,我们在开始新游戏时调用prep_ships()。这是在game_functions.py的check_play_button()中进行的

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 button_clicked and not stats.game_active:
        --snip--
        # 重置记分牌图像
        sb.prep_score()
        sb.prep_high_score()
        sb.prep_level()
        sb.prep_ships()
        --snip--

我们还在飞船被外星人撞到时调用prep_ships(),从而在玩家损失一艘飞船时更新飞船图像

def update_aliens(ai_settings, screen, stats, sb, ship, aliens,bullets): 
    --snip--
    # 检测外星人和飞船之间的碰撞
    if pygame.sprite.spritecollideany(ship, aliens):
        ship_hit(ai_settings, screen, stats, 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.ships_left > 0:
        # 将ships_left减1
        stats.ships_left -= 1
        # 更新记分牌
        sb.prep_ships() 
        # 清空外星人列表和子弹列表
        --snip--

在check_aliens_bottom()中需要调用ship_hit(),因此对这个函数进行更新

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

最后,在alien_invasion.py中修改调用update_aliens()的代码,向它传递实参sb

# 开始游戏主循环
while True:
    --snip--
    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--

九、源码来源

Python编程:从入门到实践 (ituring.com.cn)或星途辛某人/pydemo1 - 码云 - 开源中国 (gitee.com)或GitHub - 11xy11/pydemo1

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2124334.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

进程查看和计划任务

进程查看 用top查看 其中stopped为前台任务在后台暂停 jobs&#xff1a;查看系统有多少暂停任务 fg 进程序号&#xff1a;进入暂停任务 M&#xff1a;按内存占比排序 P&#xff1a;按cpu占比排序 写一个死循环脚本并杀死 #!/bin/bash NUM0 while(($NUM<3)) doecho &qu…

基于SSM的驾校管理系统的设计与实现 (含源码+sql+视频导入教程)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于SSM的驾校管理系统拥有三种角色&#xff0c;分别为管理员、教员、学员。实现了学员信息管理、课表信息管理、车辆信息管理、缴费管理、反馈管理、课程安排等功能&#xff0c;具体请参…

口含烟怎么使用?

“口含烟”通常指的是在吸烟过程中将烟草放在嘴里&#xff0c;但不直接吸入肺部。这种方法的使用方式如下&#xff1a; 1. 点燃烟草&#xff1a;使用打火机或火柴点燃香烟的端部。 2. 将烟草放在口中&#xff1a;吸气时&#xff0c;将烟草含在口腔内&#xff0c;不要深吸到肺…

大数据-130 - Flink CEP 详解 - CEP开发流程 与 案例实践:恶意登录检测实现

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

【数字IC自整资料】笔试相关

目录 1、使用连续赋值来模拟buffer惯性延时的写法正确的是?&#xff08;A&#xff09; 2、AXI总线位宽32bit&#xff0c;频率500MHZ,假设AXI cmd是基于burst8传输的&#xff0c;第一笔数据返回的延迟是200ns。那么outstanding 数量至少(13)才能满带宽传输 3、一个线性反馈移…

ICETEK-DM6437-AICOM—— DMA直接存储器访问设计

#一、设计目的&#xff1a; 1 进一步了解 ICETEK-DM6437-AF 的内部存储器空间的分配及指令寻址方式&#xff1a; 内部存储器空间分配&#xff1a;研究 ICETEK-DM6437-AF 的存储器架构&#xff0c;包括但不限于片内 SRAM、片外 DRAM 和其他存储器模块。了解这些存储器的大小、起…

Pytorch深度学习快速入门笔记【小土堆】

目录 1 Python学习中两大重要函数 2 Python代码编辑的三种方式 3 Pytorch学习 3.1 Dataset和DataLoader 3.1.1 Dataset 3.1.2 DataLoader 3.2 TensorBoard 3.2.1 add_scalar 3.2.2 add_image 3.3 Transforms 3.3.1 ToTensor 3.3.2 Normalize 3.3.3 Resize 3.3.4 C…

数据场景练习

1 行列转换 (1) 列拆分为多行 把指定字段按指定分隔符进行拆分为多行,然后其它字段直接复制. select字段列,hobby2 from tbl -- lateral view udtf(expression) tablealias as columnalias (‘,’ columnalias) lateral view explode(split(hobby,;)) temp as hobby2 (2) 行扁…

uniapp小程序,使用腾讯地图获取定位

本篇文章分享一下在实际开发小程序时遇到的需要获取用户当前位置的问题&#xff0c;在小程序开发过程中经常使用到获取定位功能。uniapp官方也提供了相应的API供我们使用。 官网地址&#xff1a;uni.getLocation(OBJECT)) 官网获取位置的详细介绍这里就不再讲述了&#xff0c;大…

区块链的可伸缩性以及面临的挑战

1. 可伸缩性 在过去的几年中&#xff0c;可伸缩性&#xff08;Scalability,也称为可扩展性) 问题一直是激烈辩论、严格研究和媒体关注的焦点。 这是一个至关重要的问题&#xff0c;因为它可能意味着区块链不适于广泛应用&#xff0c;而仅限于联盟许可的私有网络。在经过对该领域…

C++设计模式——Mediator中介者模式

一&#xff0c;中介者模式的定义 中介者模式是一种行为型设计模式。它通过一个中介者对象将多个对象之间的交互关系进行封装&#xff0c;使得对象之间的交互需要通过中介者对象来完成。该设计模式的结构很容易理解&#xff0c;以中介者为中心。 中介者模式的设计思想侧重于在…

遗传算法与深度学习实战(12)——粒子群优化详解与实现

遗传算法与深度学习实战&#xff08;12&#xff09;——粒子群优化详解与实现 0. 前言1. 粒子群优化1.1 粒子群优化原理1.2 算法流程 2. 实现 PSO 解决方程2.1 问题描述2.2 代码实现 小结系列链接 0. 前言 粒子群优化 (Particle Swarm Optimization, PSO) 是一种借鉴适者生存和…

医疗行业怎么节约和管理能源

医院建筑能耗平台 医院智能照明平台 医院能源综合管理平台 目前&#xff0c;能源短缺已成为一个全球性问题。在建筑业的发展中&#xff0c;建筑电气照明系统的节能水平与中国的能源利用率有关。照明系统中的低功率因数和高电压波动将导致较大的功率损失。因此&#xff0c;要认…

计算机网络——ARP篇(二)

上一次学习了ARP的基本概念&#xff0c;ARP缓存&#xff0c;ARP类型&#xff0c;以及ARP协议在网络中是如何工作的。这一次&#xff0c;我又深入的了解了ARP协议的工作原理&#xff0c;下面是我的学习笔记&#xff1a; 在学习之前&#xff0c;首先提出三个问题&#xff1a;ARP协…

BizDevOps落地实践

我理解BizDevOps就是端到端&#xff0c;从战略业务机会到开发上线 参考资料 十六年所思所感&#xff0c;聊聊这些年我所经历的 DevOps 系统 必致&#xff08;BizDevOps&#xff09;白皮书2022免费下载_在线阅读_藏经阁-阿里云开发者社区 具体落地实践 战略规划 战略&…

C#使用TCP-S7协议读写西门子PLC(一)

之前本人发布西门子S7协议的报文 西门子PLC的S7协议报文解析说明_西门子报文详解-CSDN博客 西门子PLC的S7协议是西门子公司在ModbusTcp协议的基础上自定义的一种协议,仅支持西门子PLC,S7协议本质仍然属于TCP协议的一种自定义具体实现 第一步,准备工作。VS2022中新建窗体应…

动态规划及其MATLAB实现

目录 引言 动态规划的基本原理 动态规划的常见应用 动态规划的求解步骤 动态规划的复杂度分析 表格总结&#xff1a;动态规划常见问题及其复杂度 结论 引言 动态规划&#xff08;Dynamic Programming, DP&#xff09;是一种求解最优化问题的有效方法&#xff0c;特别适合…

华为 HCIP-Datacom H12-821 题库 (16)

1.需要题库的小伙伴至博客最下方添加微信公众号关注后回复题库 2.有兴趣交流IT问题的小伙伴微信公众号回复交流群&#xff0c;加入微信IT交流群 1. OSPF 邻居关系建立出现故障&#xff0c;通过 display ospf error 命令来检查&#xff0c;输出结果如图所示&#xff0c;根据图中…

从零开始配置 TypeScript 项目

ESLint 配置 从背景的介绍中可以理解&#xff0c;对于全新的 TypeScript 项目&#xff08;直接抛弃 TSLint&#xff09;需要包含解析 AST 的解析器 typescript-eslint/parser 和使用校验规则的插件 typescript-eslint/eslint-plugin&#xff0c;这里需要在项目中进行安装&…

CentOS 安装Squid代理

环境&#xff1a; 华为云服务器一台&#xff1a;123.60.53.69&#xff0c;放行3128端口 Windows 11 电脑&#xff1a;动态IP 需求&#xff1a; 客户端电脑通过华为云服务器实现代理上网 一、服务器设置 1、安装 yum install squid httpd-tools -y 2、创建用户&#x…