PyGame:Python 游戏编程入门

一、说明

        当我在上个世纪末开始学习计算机编程时,我的愿望是编写计算机游戏。我试图弄清楚如何在我学到的每种语言和每个平台上编写游戏,包括 Python。这就是我发现pygame并学习如何使用它来编写游戏和其他图形程序的方式。当时,我真的很想要一本入门书pygame

        读完本文后,您将能够:

  • 在屏幕上绘制项目
  • 播放音效和音乐
  • 处理用户输入
  • 实施事件循环
  • 描述游戏编程与标准过程式 Python 编程有何不同

        目录

  • 背景和设置
  • 基本 PyGame 程序
  • PyGame 概念
    • 初始化和模块
    • 显示器和表面
    • 图像和矩形
  • 基础游戏设计
    • 导入并初始化 PyGame
    • 设置显示
    • 设置游戏循环
    • 处理事件
    • 在屏幕上绘图
    • 使用 .blit() 和 .flip()
  • 精灵
    • 玩家
    • 用户输入
    • 敌人
  • 精灵组
  • 自定义事件
  • 碰撞检测
  • 精灵图像
    • 改变对象构造函数
    • 添加背景图片
  • 游戏速度
  • 声音特效
  • 关于来源的说明
  • 结论

        您可以获得本文中的所有代码以进行后续操作:

  •         示例代码: 单击此处下载本教程中使用的 PyGame 示例项目的源代码。

二、背景和设置

pygame是SDL 库的 Python 包装器,SDL 库代表Simple DirectMedia Layer。SDL 提供对系统底层多媒体硬件组件(例如声音、视频、鼠标、键盘和操纵杆)的跨平台访问。作为停滞的PySDL 项目pygame的替代品而开始。SDL 的跨平台特性意味着您可以为每个支持它们的平台编写游戏和丰富的多媒体 Python 程序!pygame

pygame在您的平台上安装,请使用适当的pip命令:

$ pip install pygame

您可以通过加载该库附带的示例之一来验证安装:

$ python3 -m pygame.examples.aliens

如果出现游戏窗口,则pygame说明安装正确!如果您遇到问题,入门指南概述了所有平台的一些已知问题和注意事项。

 

三、基本 PyGame 程序

        在讨论具体细节之前,让我们先看一个基本pygame程序。该程序创建一个窗口,用白色填充背景,并在中间绘制一个蓝色圆圈:

Python
 1# Simple pygame program
 2
 3# Import and initialize the pygame library
 4import pygame
 5pygame.init()
 6
 7# Set up the drawing window
 8screen = pygame.display.set_mode([500, 500])
 9
10# Run until the user asks to quit
11running = True
12while running:
13
14    # Did the user click the window close button?
15    for event in pygame.event.get():
16        if event.type == pygame.QUIT:
17            running = False
18
19    # Fill the background with white
20    screen.fill((255, 255, 255))
21
22    # Draw a solid blue circle in the center
23    pygame.draw.circle(screen, (0, 0, 255), (250, 250), 75)
24
25    # Flip the display
26    pygame.display.flip()
27
28# Done! Time to quit.
29pygame.quit()

当您运行该程序时,您将看到一个如下所示的窗口:

一个简单的 pygame 程序

让我们逐节分解这段代码:

  • 第 4 行和第 5 行导入并初始化pygame库。没有这些线,就没有pygame.

  • 第 8 行设置程序的显示窗口。您提供一个列表或元组来指定要创建的窗口的宽度和高度。该程序使用列表创建一个每边 500 像素的方形窗口。

  • 第 11 行和第 12 行设置了一个游戏循环来控制程序何时结束。您将在本教程后面介绍游戏循环。

  • 第 15 至 17 行扫描并处理游戏循环内的事件。您也会稍后参加活动。在本例中,唯一处理的事件是 pygame.QUIT,该事件在用户单击窗口关闭按钮时发生。

  • 第 20 行用纯色填充窗口。screen.fill()接受指定颜色 RGB 值的列表或元组。自从(255, 255, 255)提供以来,窗口就充满了白色。

  • 第 23 行使用以下参数在窗口中绘制一个圆:

    • screen要在其上绘图的窗口
    • (0, 0, 255)包含 RGB 颜色值的元组
    • (250, 250)指定圆心坐标的元组
    • 75要绘制的圆的半径(以像素为单位)
  • 第 26行将显示内容更新到屏幕上。如果没有这个调用,窗口中就不会出现任何内容!

  • 29号线出口pygame。这仅在循环结束后发生。

这就是pygame“你好,世界”的版本。现在让我们更深入地了解这段代码背后的概念。

四、PyGame 概念

        由于pygameSDL 库可跨不同平台和设备移植,因此它们都需要定义和使用各种硬件现实的抽象。理解这些概念和抽象将帮助您设计和开发自己的游戏。

4.1 初始化和模块

        该pygame库由许多 Python 结构组成,其中包括几个不同的模块。这些模块提供对系统上特定硬件的抽象访问,以及使用该硬件的统一方法。例如,display允许统一访问您的视频显示,同时joystick允许对操纵杆进行抽象控制。

        导入上面示例中的库后,您所做pygame的第一件事是使用. 该函数调用所有包含模块的单独函数。由于这些模块是特定硬件的抽象,因此需要执行此初始化步骤,以便您可以在 Linux、Windows 和 Mac 上使用相同的代码。pygame.init()init()pygame

4.2 显示器和表面

        除了模块之外,pygame还包括几个Python,它们封装了非硬件相关的概念。其中之一是Surface,从最基本的角度来说,它定义了一个可以在其上绘图的矩形区域。Surface对象在 中的许多上下文中使用pygame。稍后您将了解如何将图像加载到 a 中Surface并将其显示在屏幕上。

        在 中pygame,所有内容都在单个用户创建的 上查看display,该用户可以是一个窗口或全屏。显示是使用 创建的.set_mode(),它返回Surface代表窗口可见部分的 。您可以Surface将其传递给 、 等绘图函数pygame.draw.circle(),并且在您调用时将其内容Surface推送到显示器上pygame.display.flip()。

4.4 图像和矩形

        您的基本pygame程序直接在显示器上绘制形状Surface,但您也可以处理磁盘上的图像。该image模块允许您加载和保存各种流行格式的图像。图像被加载到Surface对象中,然后可以通过多种方式对其进行操作和显示。

        如上所述,Surface对象由矩形表示,就像 中的许多其他对象一样pygame,例如图像和窗口。矩形的使用如此频繁,以至于有一个特殊的Rect类来处理它们。您将Rect在游戏中使用对象和图像来绘制玩家和敌人,并管理他们之间的碰撞。

好吧,理论已经足够了。让我们设计并编写一个游戏!

五、基础游戏设计

        在开始编写任何代码之前,最好先进行一些设计。由于这是一款教程游戏,我们也为其设计一些基本的游戏玩法:

  • 游戏的目标是避开传入的障碍物:
    • 播放器从屏幕左侧开始。
    • 障碍物从右侧随机进入并沿直线向左移动。
  • 玩家可以向左、向右、向上或向下移动以避开障碍物。
  • 玩家无法移出屏幕。
  • 当玩家被障碍物击中或用户关闭窗口时游戏结束。

        我的一位前同事在描述软件项目时常常说:“除非你知道自己不做什么,否则你不知道自己在做什么。” 考虑到这一点,以下是本教程中不会介绍的一些内容:

  • 没有多重生命
  • 没有记分
  • 没有玩家攻击能力
  • 没有进步的水平
  • 没有boss角色

        您可以随意尝试将这些功能和其他功能添加到您自己的程序中。我们开始吧!

5.1 导入并初始化 PyGame

        导入后pygame,您还需要对其进行初始化。这允许pygame将其抽象连接到您的特定硬件:

Python
 1# Import the pygame module
 2import pygame
 3
 4# Import pygame.locals for easier access to key coordinates
 5# Updated to conform to flake8 and black standards
 6from pygame.locals import (
 7    K_UP,
 8    K_DOWN,
 9    K_LEFT,
10    K_RIGHT,
11    K_ESCAPE,
12    KEYDOWN,
13    QUIT,
14)
15
16# Initialize pygame
17pygame.init()

  pygame除了模块和类之外,该库还定义了许多东西。它还为击键、鼠标移动和显示属性等定义了一些局部常量。您可以使用语法引用这些常量pygame.<CONSTANT>。通过从 导入特定常量pygame.locals,您可以改用语法<CONSTANT>。这将为您节省一些击键次数并提高整体可读性。

5.2 设置显示

        现在你需要一些可以借鉴的东西!创建一个屏幕作为整体画布:

Python
 1# Import the pygame module
 2import pygame
 3
 4# Import pygame.locals for easier access to key coordinates
 5# Updated to conform to flake8 and black standards
 6from pygame.locals import (
 7    K_UP,
 8    K_DOWN,
 9    K_LEFT,
10    K_RIGHT,
11    K_ESCAPE,
12    KEYDOWN,
13    QUIT,
14)
15
16# Initialize pygame
17pygame.init()
18
19# Define constants for the screen width and height 20SCREEN_WIDTH = 800 21SCREEN_HEIGHT = 600 22
23# Create the screen object 24# The size is determined by the constant SCREEN_WIDTH and SCREEN_HEIGHT 25screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) 

  pygame.display.set_mode()您可以通过调用并传递具有所需宽度和高度的元组或列表来创建要使用的屏幕。SCREEN_WIDTH在本例中,窗口大小为 800x600,由第 20 行和SCREEN_HEIGHT第 21 行上的常量定义。这将返回Surface表示窗口内部尺寸的 a。这是您可以控制的窗口部分,而操作系统控制窗口边框和标题栏。

        如果您现在运行该程序,您将看到一个窗口短暂弹出,然后随着程序退出而立即消失。不要眨眼,否则你可能会错过它!在下一节中,您将重点关注主游戏循环,以确保程序仅在给出正确的输入时退出。

5.3 设置游戏循环

        从 Pong 到 Fortnite,每个游戏都使用游戏循环来控制游戏玩法。游戏循环做了四件非常重要的事情:

  1. 处理用户输入
  2. 更新所有游戏对象的状态
  3. 更新显示和音频输出
  4. 保持游戏的速度

        游戏循环的每个周期称为一个,每个周期做事的速度越快,游戏运行的速度就越快。帧会继续出现,直到满足退出游戏的某些条件。在您的设计中,有两个条件可以结束游戏循环:

  1. 玩家与障碍物发生碰撞。(稍后您将介绍碰撞检测。)
  2. 玩家关闭窗口。

        游戏循环所做的第一件事是处理用户输入以允许玩家在屏幕上移动。因此,您需要某种方法来捕获和处理各种输入。您可以使用pygame事件系统来执行此操作。

5.4 处理事件

        按键、鼠标移动、甚至操纵杆移动都是用户提供输入的一些方式。所有用户输入都会生成一个事件。事件可以随时发生,并且通常(但并非总是)源自程序之外。中的所有事件pygame都放置在事件队列中,然后可以访问和操作事件队列。处理事件称为处理事件,执行此操作的代码称为事件处理程序

        中的每个事件pygame都有一个与其关联的事件类型。对于您的游戏,您将关注的事件类型是按键和窗口关闭。按键事件具有事件类型KEYDOWN,窗口关闭事件具有类型QUIT。不同的事件类型还可能具有与其关联的其他数据。例如,KEYDOWN事件类型还有一个变量调用key来指示按下了哪个键。

        您可以通过调用 来访问队列中所有活动事件的列表pygame.event.get()。然后,您循环遍历此列表,检查每个事件类型,并做出相应响应:

Python
27# Variable to keep the main loop running
28running = True
29
30# Main loop
31while running:
32    # Look at every event in the queue
33    for event in pygame.event.get():
34        # Did the user hit a key?
35        if event.type == KEYDOWN:
36            # Was it the Escape key? If so, stop the loop.
37            if event.key == K_ESCAPE:
38                running = False
39
40        # Did the user click the window close button? If so, stop the loop.
41        elif event.type == QUIT:
42            running = False

        让我们仔细看看这个游戏循环:

  • 第 28行为游戏循环设置一个控制变量。要退出循环和游戏,您可以设置running = False。游戏循环从第 29 行开始。

  • 第 31行启动事件处理程序,遍历事件队列中当前的每个事件。如果没有事件,则列表为空,并且处理程序不会执行任何操作。

  • 第 35 至 38 行检查当前是否event.typeKEYDOWN事件。如果是,则程序通过查看该event.key属性来检查按下的是哪个键。如果该键是Esc键,由 表示K_ESCAPE,则通过设置 退出游戏循环running = False

  • 第 41 行和第 42 行对名为 的事件类型执行类似的检查QUIT。仅当用户单击窗口关闭按钮时才会发生此事件。用户还可以使用任何其他操作系统操作来关闭窗口。

当您将这些行添加到前面的代码并运行它时,您将看到一个带有空白或黑色屏幕的窗口:

一个空的但持久的 pygame 窗口

Esc在您按下该键或QUIT通过关闭窗口触发事件之前,该窗口不会消失。

5.5 在屏幕上绘图

在示例程序中,您使用两个命令在屏幕上绘图:

  1. screen.fill()来填充背景
  2. pygame.draw.circle()画一个圆

现在您将了解在屏幕上绘图的第三种方法:使用Surface.

回想一下,aSurface是一个可以在其上绘图的矩形对象,就像一张白纸一样。该screen对象是,您可以独立于显示屏幕Surface创建自己的对象。Surface让我们看看它是如何工作的:

Python
44# Fill the screen with white
45screen.fill((255, 255, 255))
46
47# Create a surface and pass in a tuple containing its length and width
48surf = pygame.Surface((50, 50))
49
50# Give the surface a color to separate it from the background
51surf.fill((0, 0, 0))
52rect = surf.get_rect()

        在第 45 行用白色填充屏幕后,Surface在第 48 行创建一个新的。它Surface宽 50 像素,高 50 像素,并分配给surf。此时,您就像对待screen. 所以在线上,51你用黑色填充它。您还可以Rect使用访问其底层.get_rect()。这被存储以rect供以后使用。

5.6 使用.blit().flip()

        仅创建一个新内容Surface并不足以在屏幕上看到它。为此,您需要将blit转移Surface到另一个Surface. 该术语blit代表“块传输”.blit()是指将一个数据块的内容复制Surface到另一个数据块的方式。您只能.blit()从一个屏幕Surface转到另一个屏幕,但由于屏幕只是另一个屏幕Surface,所以这不是问题。surf以下是在屏幕上绘图的方法:

Python
54# This line says "Draw surf onto the screen at the center"
55screen.blit(surf, (SCREEN_WIDTH/2, SCREEN_HEIGHT/2))
56pygame.display.flip()

第 55 行的调用.blit()采用两个参数:

  1. Surface画的
  2. 在源上绘制它的位置Surface

坐标(SCREEN_WIDTH/2, SCREEN_HEIGHT/2)告诉你的程序放置surf在屏幕的正中心,但它看起来并不完全是这样:

将表面传输到屏幕上

        图像看起来偏离中心的原因是.blit()左上角放在surf给定的位置。如果你想surf居中,那么你必须做一些数学运算才能将其向上和向左移动。您可以通过surf从屏幕的宽度和高度中减去 的宽度和高度,然后除以 2 来定位中心,然后将这些数字作为参数传递给screen.blit()

Python
54# Put the center of surf at the center of the display
55surf_center = (
56    (SCREEN_WIDTH-surf.get_width())/2,
57    (SCREEN_HEIGHT-surf.get_height())/2
58)
59
60# Draw surf at the new coordinates
61screen.blit(surf, surf_center)
62pygame.display.flip()

        请注意调用 topygame.display.flip()之后的调用blit()。这将使用自上次翻转以来绘制的所有内容更新整个屏幕。如果不调用.flip(),则不会显示任何内容。

六、精灵

        在你的游戏设计中,玩家从左侧开始,障碍物从右侧进入。您可以用对象来表示所有障碍物,Surface以便更轻松地绘制所有内容,但是您如何知道在哪里绘制它们呢?你如何知道障碍物是否与玩家发生碰撞?当障碍物飞出屏幕时会发生什么?如果你想绘制也会移动的背景图像怎么办?如果您希望图像动画化怎么办?您可以使用精灵来处理所有这些情况以及更多情况。

        用编程术语来说,精灵是屏幕上某物的 2D 表示。本质上,它是一张图片。pygame提供一个Sprite类,该类旨在保存要在屏幕上显示的任何游戏对象的一个​​或多个图形表示。要使用它,您需要创建一个扩展的新类Sprite。这允许您使用其内置方法。

6.1 玩家

以下是如何在当前游戏中使用Sprite对象来定义玩家。在第 18 行后插入此代码:

Python
20# Define a Player object by extending pygame.sprite.Sprite
21# The surface drawn on the screen is now an attribute of 'player'
22class Player(pygame.sprite.Sprite):
23    def __init__(self):
24        super(Player, self).__init__()
25        self.surf = pygame.Surface((75, 25))
26        self.surf.fill((255, 255, 255))
27        self.rect = self.surf.get_rect()

        您首先Player通过在第 22 行扩展来定义pygame.sprite.Sprite。然后.__init__()使用.super()来调用.__init__()的方法Sprite。有关为什么需要这样做的更多信息,您可以阅读Supercharge Your Classes With Python super()。

        接下来,您定义并初始化.surf以保存要显示的图像,该图像当前是一个白框。您还可以定义并初始化.rect,稍后将使用它来绘制玩家。要使用这个新类,您需要创建一个新对象并更改绘图代码。展开下面的代码块以查看所有内容:

        展开查看完整代码显示隐藏

        运行这段代码。您会在屏幕中间大致看到一个白色矩形:

绘制的基本玩家精灵

        如果将第 59 行更改为 ,您认为会发生什么screen.blit(player.surf, player.rect)?尝试一下,看看:

Python
55# Fill the screen with black
56screen.fill((0, 0, 0))
57
58# Draw the player on the screen
59screen.blit(player.surf, player.rect) 60
61# Update the display
62pygame.display.flip()

当您将 a 传递Rect给时.blit(),它会使用左上角的坐标来绘制曲面。稍后您将使用它来让您的玩家移动!

6.2 用户输入

pygame到目前为止,您已经学习了如何在屏幕上设置和绘制对象。现在,真正的乐趣开始了!您将使玩家可以使用键盘进行控制。

之前,您看到它pygame.event.get()返回事件队列中的事件列表,您可以在其中扫描KEYDOWN事件类型。嗯,这不是读取按键的唯一方法。pygame还提供pygame.event.get_pressed(),它返回一个包含队列中所有当前事件的字典。KEYDOWN

将其放在事件处理循环之后的游戏循环中。这将返回一个字典,其中包含在每帧开始时按下的键:

Python
54# Get the set of keys pressed and check for user input
55pressed_keys = pygame.key.get_pressed()

接下来,您编写一个方法来Player接受该字典。这将根据按下的键定义精灵的行为。看起来可能是这样的:

Python
29# Move the sprite based on user keypresses
30def update(self, pressed_keys):
31    if pressed_keys[K_UP]:
32        self.rect.move_ip(0, -5)
33    if pressed_keys[K_DOWN]:
34        self.rect.move_ip(0, 5)
35    if pressed_keys[K_LEFT]:
36        self.rect.move_ip(-5, 0)
37    if pressed_keys[K_RIGHT]:
38        self.rect.move_ip(5, 0)

K_UPK_DOWN、 、K_LEFTK_RIGHT分别对应键盘上的方向键。如果该键的字典条目是True,则该键按下,您可以将玩家移动.rect到正确的方向。这里你使用.move_ip(),它代表移动到位,来移动当前Rect

然后,您可以调用.update()每一帧来移动玩家精灵以响应按键。在调用后立即添加此调用.get_pressed()

Python
52# Main loop
53while running:
54    # for loop through the event queue
55    for event in pygame.event.get():
56        # Check for KEYDOWN event
57        if event.type == KEYDOWN:
58            # If the Esc key is pressed, then exit the main loop
59            if event.key == K_ESCAPE:
60                running = False
61        # Check for QUIT event. If QUIT, then set running to false.
62        elif event.type == QUIT:
63            running = False
64
65    # Get all the keys currently pressed
66    pressed_keys = pygame.key.get_pressed()
67
68 # Update the player sprite based on user keypresses 69 player.update(pressed_keys) 70
71    # Fill the screen with black
72    screen.fill((0, 0, 0))

现在您可以使用箭头键在屏幕上移动播放器矩形:

在 pygame 中按键移动精灵

您可能会注意到两个小问题:

  1. 如果按住某个键,玩家矩形可以快速移动。你稍后会处理这个问题。
  2. 玩家矩形可以移出屏幕。现在让我们解决这个问题。

为了让玩家保持在屏幕上,您需要添加一些逻辑来检测玩家是否rect会移出屏幕。为此,您需要检查rect坐标是否已超出屏幕边界。如果是这样,那么您指示程序将其移回边缘:

Python
25# Move the sprite based on user keypresses
26def update(self, pressed_keys):
27    if pressed_keys[K_UP]:
28        self.rect.move_ip(0, -5)
29    if pressed_keys[K_DOWN]:
30        self.rect.move_ip(0, 5)
31    if pressed_keys[K_LEFT]:
32        self.rect.move_ip(-5, 0)
33    if pressed_keys[K_RIGHT]:
34        self.rect.move_ip(5, 0)
35
36 # Keep player on the screen 37 if self.rect.left < 0: 38 self.rect.left = 0 39 if self.rect.right > SCREEN_WIDTH: 40 self.rect.right = SCREEN_WIDTH 41 if self.rect.top <= 0: 42 self.rect.top = 0 43 if self.rect.bottom >= SCREEN_HEIGHT: 44 self.rect.bottom = SCREEN_HEIGHT 

这里,不使用,而是直接更改、、、 或.move()对应的坐标。测试一下,你会发现玩家矩形不能再移出屏幕。.top.bottom.left.right

现在让我们添加一些敌人!

6.3 敌人

        没有敌人的游戏算什么?您将使用已经学到的技术来创建基本的敌人类别,然后创建大量敌人以供玩家躲避。首先,导入random库:

Python
 4# Import random for random numbers
 5import random

        然后创建一个名为 的新精灵类Enemy,遵循您使用的相同模式Player

Python
55# Define the enemy object by extending pygame.sprite.Sprite
56# The surface you draw on the screen is now an attribute of 'enemy'
57class Enemy(pygame.sprite.Sprite):
58    def __init__(self):
59        super(Enemy, self).__init__()
60        self.surf = pygame.Surface((20, 10))
61        self.surf.fill((255, 255, 255))
62        self.rect = self.surf.get_rect(
63            center=(
64                random.randint(SCREEN_WIDTH + 20, SCREEN_WIDTH + 100),
65                random.randint(0, SCREEN_HEIGHT),
66            )
67        )
68        self.speed = random.randint(5, 20)
69
70    # Move the sprite based on speed
71    # Remove the sprite when it passes the left edge of the screen
72    def update(self):
73        self.rect.move_ip(-self.speed, 0)
74        if self.rect.right < 0:
75            self.kill()

Enemy和之间有四个显着区别Player

  1. 在第 62 至 67 行,您更新rect为沿屏幕右边缘的随机位置。矩形的中心就在屏幕之外。它位于距离右边缘 20 到 100 像素之间的某个位置,并且位于顶部边缘和底部边缘之间的某个位置。

  2. 在第 68 行,您定义.speed为 5 到 20 之间的随机数。这指定了敌人向玩家移动的速度。

  3. 在第 73 至 76 行,您定义了.update()。由于敌人会自动移动,所以不需要任何参数。相反,将敌人按照创建时定义的位置.update()移向屏幕左侧。.speed

  4. 在第 74 行,您检查敌人是否已移出屏幕。为了确保Enemy完全离开屏幕并且不会在仍然可见时消失,您需要检查 的右侧是否.rect已经超出了屏幕的左侧。一旦敌人离开屏幕,您就可以调用.kill()以防止其被进一步处理。

那么,它有什么.kill()作用呢?要弄清楚这一点,您必须了解Sprite Groups

七、精灵组

        提供的另一个超级有用的类pygame是Sprite Group. 这是一个包含一组Sprite对象的对象。那么为什么要使用它呢?您不能只跟踪Sprite列表中的对象吗?嗯,可以,但是使用 a 的优点Group在于它公开的方法。这些方法有助于检测是否有任何Enemy与 发生冲突Player,这使得更新更加容易。

        让我们看看如何创建精灵组。您将创建两个不同的Group对象:

  1. 第一个Group将容纳Sprite游戏中的所有内容。
  2. 第二个Group将只容纳Enemy物体。

代码如下:

Python
82# Create the 'player'
83player = Player()
84
85# Create groups to hold enemy sprites and all sprites 86# - enemies is used for collision detection and position updates 87# - all_sprites is used for rendering 88enemies = pygame.sprite.Group() 89all_sprites = pygame.sprite.Group() 90all_sprites.add(player) 91
92# Variable to keep the main loop running
93running = True

当您调用 时.kill(),将从它所属的Sprite每个对象中删除。Group这也会删除对 的引用Sprite,从而允许Python 的垃圾收集器根据需要回收内存。

现在您已经有了一个all_sprites组,您可以更改对象的绘制方式。您可以迭代 中的所有内容,而不是.blit()仅仅调用:Playerall_sprites

Python
117# Fill the screen with black
118screen.fill((0, 0, 0))
119
120# Draw all sprites 121for entity in all_sprites: 122 screen.blit(entity.surf, entity.rect) 123
124# Flip everything to the display
125pygame.display.flip()

现在,放入的任何内容都all_sprites将在每一帧中绘制,无论是敌人还是玩家。

只有一个问题……你没有任何敌人!你可以在游戏开始时创建一堆敌人,但当他们在几秒钟后全部离开屏幕时,游戏很快就会变得无聊。相反,让我们探索如何随着游戏的进展保持稳定的敌人供应。

八、自定义事件

        该设计要求敌人定期出现。这意味着在设定的时间间隔内,您需要做两件事:

  1. 创建一个新的Enemy.
  2. 将其添加到all_spritesenemies

        您已经拥有处理随机事件的代码。事件循环旨在查找每帧发生的随机事件并适当地处理它们。幸运的是,它pygame并不限制您只能使用它定义的事件类型。您可以定义自己的事件来根据您的需要进行处理。

        让我们看看如何创建每隔几秒生成一次的自定义事件。您可以通过命名来创建自定义事件:

Python
78# Create the screen object
79# The size is determined by the constant SCREEN_WIDTH and SCREEN_HEIGHT
80screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
81
82# Create a custom event for adding a new enemy 83ADDENEMY = pygame.USEREVENT + 1 84pygame.time.set_timer(ADDENEMY, 250) 85
86# Instantiate player. Right now, this is just a rectangle.
87player = Player()

   pygame在内部将事件定义为整数,因此您需要使用唯一的整数定义一个新事件。最后一个事件pygame保留名为USEREVENT,因此ADDENEMY = pygame.USEREVENT + 1在第 83 行定义可确保它是唯一的。

        接下来,您需要在整个游戏过程中定期将这个新事件插入到事件队列中。这就是该time模块的用武之地。第 84 行每 250 毫秒触发一次新ADDENEMY事件,即每秒四次。.set_timer()您可以在游戏循环之外调用,因为您只需要一个计时器,但它会在整个游戏过程中触发。

        添加代码来处理您的新事件:

Python
100# Main loop
101while running:
102    # Look at every event in the queue
103    for event in pygame.event.get():
104        # Did the user hit a key?
105        if event.type == KEYDOWN:
106            # Was it the Escape key? If so, stop the loop.
107            if event.key == K_ESCAPE:
108                running = False
109
110        # Did the user click the window close button? If so, stop the loop.
111        elif event.type == QUIT:
112            running = False
113
114 # Add a new enemy? 115 elif event.type == ADDENEMY: 116 # Create the new enemy and add it to sprite groups 117 new_enemy = Enemy() 118 enemies.add(new_enemy) 119 all_sprites.add(new_enemy) 120
121    # Get the set of keys pressed and check for user input
122    pressed_keys = pygame.key.get_pressed()
123    player.update(pressed_keys)
124
125 # Update enemy position 126 enemies.update() 

        每当事件处理程序在第 115 行看到新ADDENEMY事件时,它就会创建 anEnemy并将其添加到enemiesall_sprites。由于 Enemyall_sprites,所以每一帧都会绘制它。您还需要调用enemies.update()第 126 行,该行更新 中的所有内容enemies,以确保它们正确移动:

pygame 中的敌人飞过

然而,这并不是有一个团体的唯一原因enemies

九、碰撞检测

        您的游戏设计要求每当敌人与玩家发生碰撞时游戏就结束。检查碰撞是游戏编程的一项基本技术,通常需要一些重要的数学来确定两个精灵是否会相互重叠。

        这就是像这样的框架pygame派上用场的地方!编写碰撞检测代码很乏味,但是pygame有很多碰撞检测方法可供您使用。

        在本教程中,您将使用一种名为 的方法.spritecollideany(),其读作“sprite collide any”。该方法接受 aSprite和 aGroup作为参数。它查看 中的每个对象Group并检查其是否.rect.rect的相交Sprite。如果是,则返回True。否则,它返回False。这对于这个游戏来说是完美的,因为你需要检查单个是否player与 a 之一Group碰撞enemies

代码如下:

Python
130# Draw all sprites
131for entity in all_sprites:
132    screen.blit(entity.surf, entity.rect)
133
134# Check if any enemies have collided with the player 135if pygame.sprite.spritecollideany(player, enemies): 136 # If so, then remove the player and stop the loop 137 player.kill() 138 running = False 

        第 135 行测试是否player与 中的任何对象发生碰撞enemies。如果是,则player.kill()调用 then 将其从其所属的每个组中删除。由于唯一被渲染的对象位于 中all_sprites,因此player将不再渲染。一旦玩家被杀死,您也需要退出游戏,因此您running = False在第 138 行设置了跳出游戏循环。

        至此,您已经掌握了游戏的基本元素:

Pygame 窗口

现在,让我们稍微打扮一下它,使其更具可玩性,并添加一些高级功能以帮助它脱颖而出。

十、精灵图像

        好吧,你有一个游戏,但说实话……有点难看。玩家和敌人只是黑色背景上的白色块。当Pong刚出现时,这已经是最先进的了,但现在已经不再适用了。让我们用一些更酷的图像替换所有那些无聊的白色矩形,这将使游戏感觉像一个真正的游戏。

  Surface之前,您了解到磁盘上的图像可以在模块的帮助下加载到 a 中image。在本教程中,我们为玩家制作了一架小型喷气机,为敌人制作了一些导弹。欢迎您使用此艺术作品,绘制自己的艺术作品,或下载一些免费的游戏艺术资源来使用。您可以单击下面的链接下载本教程中使用的艺术作品:

示例代码: 单击此处下载本教程中使用的 PyGame 示例项目的源代码。

10.1 改变对象构造函数

        在使用图像来表示玩家和敌人精灵之前,您需要对其构造函数进行一些更改。下面的代码替换了之前使用的代码:

Python
 7# Import pygame.locals for easier access to key coordinates
 8# Updated to conform to flake8 and black standards
 9# from pygame.locals import *
10from pygame.locals import (
11 RLEACCEL, 12    K_UP,
13    K_DOWN,
14    K_LEFT,
15    K_RIGHT,
16    K_ESCAPE,
17    KEYDOWN,
18    QUIT,
19)
20
21# Define constants for the screen width and height
22SCREEN_WIDTH = 800
23SCREEN_HEIGHT = 600
24
25
26# Define the Player object by extending pygame.sprite.Sprite
27# Instead of a surface, use an image for a better-looking sprite
28class Player(pygame.sprite.Sprite):
29    def __init__(self):
30        super(Player, self).__init__()
31 self.surf = pygame.image.load("jet.png").convert() 32 self.surf.set_colorkey((255, 255, 255), RLEACCEL) 33        self.rect = self.surf.get_rect()

        让我们稍微解压一下第 31 行。pygame.image.load()从磁盘加载图像。您将文件的路径传递给它。它返回 a Surface,并且.convert()调用优化Surface,使将来的.blit()调用更快。

        第 32 行用于.set_colorkey()指示颜色pygame将呈现为透明。在本例中,您选择白色,因为这是喷射图像的背景颜色。RLEACCEL常量是一个可选参数,有助于pygame在非加速显示器上更快地渲染。这被添加到pygame.locals第 11 行的导入语句中。

        其他什么都不需要改变。该图像仍然是一个Surface,只不过现在上面画了一张图片。你仍然以同样的方式使用它。

        以下是类似的外观变化Enemy

Python
59# Define the enemy object by extending pygame.sprite.Sprite
60# Instead of a surface, use an image for a better-looking sprite
61class Enemy(pygame.sprite.Sprite):
62    def __init__(self):
63        super(Enemy, self).__init__()
64 self.surf = pygame.image.load("missile.png").convert() 65 self.surf.set_colorkey((255, 255, 255), RLEACCEL) 66        # The starting position is randomly generated, as is the speed
67        self.rect = self.surf.get_rect(
68            center=(
69                random.randint(SCREEN_WIDTH + 20, SCREEN_WIDTH + 100),
70                random.randint(0, SCREEN_HEIGHT),
71            )
72        )
73        self.speed = random.randint(5, 20)

        现在运行该程序应该会显示这与您之前玩的游戏相同,只是现在您添加了一些带有图像的漂亮图形皮肤。但为什么只停留在让玩家和敌人精灵看起来好看呢?让我们添加几朵飘过的云,给人一种喷气式飞机在天空中飞翔的感觉。

10.2 添加背景图片

        对于背景云,您使用与Player和相同的原则Enemy

  1. 创建Cloud班级。
  2. 添加云的图像。
  3. 创建一个将向屏幕左侧.update()移动的方法。cloud
  4. 创建自定义事件和处理程序以cloud按设定的时间间隔创建新对象。
  5. 将新创建的对象添加到名为 的cloud新对象中。Groupclouds
  6. clouds在游戏循环中更新并绘制。

        看起来像这样Cloud

Python
 83# Define the cloud object by extending pygame.sprite.Sprite
 84# Use an image for a better-looking sprite
 85class Cloud(pygame.sprite.Sprite):
 86    def __init__(self):
 87        super(Cloud, self).__init__()
 88        self.surf = pygame.image.load("cloud.png").convert()
 89        self.surf.set_colorkey((0, 0, 0), RLEACCEL)
 90        # The starting position is randomly generated
 91        self.rect = self.surf.get_rect(
 92            center=(
 93                random.randint(SCREEN_WIDTH + 20, SCREEN_WIDTH + 100),
 94                random.randint(0, SCREEN_HEIGHT),
 95            )
 96        )
 97
 98    # Move the cloud based on a constant speed
 99    # Remove the cloud when it passes the left edge of the screen
100    def update(self):
101        self.rect.move_ip(-5, 0)
102        if self.rect.right < 0:
103            self.kill()

        这看起来应该很熟悉。和 几乎一样Enemy

        要让云以一定的间隔出现,您将使用类似于创建新敌人的事件创建代码。将其放在敌人创建事件的正下方:

Python
116# Create custom events for adding a new enemy and a cloud
117ADDENEMY = pygame.USEREVENT + 1
118pygame.time.set_timer(ADDENEMY, 250)
119ADDCLOUD = pygame.USEREVENT + 2 120pygame.time.set_timer(ADDCLOUD, 1000) 

这表示在创建下一个 .txt 文件之前等待 1000 毫秒或一秒cloud

接下来,创建一个新的Group来保存每个新创建的cloud

Python
125# Create groups to hold enemy sprites, cloud sprites, and all sprites
126# - enemies is used for collision detection and position updates
127# - clouds is used for position updates
128# - all_sprites is used for rendering
129enemies = pygame.sprite.Group()
130clouds = pygame.sprite.Group() 131all_sprites = pygame.sprite.Group()
132all_sprites.add(player)

ADDCLOUD接下来,在事件处理程序中添加新事件的处理程序:

Python
137# Main loop
138while running:
139    # Look at every event in the queue
140    for event in pygame.event.get():
141        # Did the user hit a key?
142        if event.type == KEYDOWN:
143            # Was it the Escape key? If so, then stop the loop.
144            if event.key == K_ESCAPE:
145                running = False
146
147        # Did the user click the window close button? If so, stop the loop.
148        elif event.type == QUIT:
149            running = False
150
151        # Add a new enemy?
152        elif event.type == ADDENEMY:
153            # Create the new enemy and add it to sprite groups
154            new_enemy = Enemy()
155            enemies.add(new_enemy)
156            all_sprites.add(new_enemy)
157
158 # Add a new cloud? 159 elif event.type == ADDCLOUD: 160 # Create the new cloud and add it to sprite groups 161 new_cloud = Cloud() 162 clouds.add(new_cloud) 163 all_sprites.add(new_cloud) 

        最后,确保clouds每帧都更新:

Python
167# Update the position of enemies and clouds
168enemies.update()
169clouds.update() 170
171# Fill the screen with sky blue
172screen.fill((135, 206, 250)) 

        第 172 行更新了原来的内容screen.fill(),使屏幕充满宜人的天蓝色。您可以将此颜色更改为其他颜色。也许你想要一个紫色天空的外星世界,霓虹绿的有毒荒原,或者红色的火星表面!

        请注意,每个新的Cloud和 都Enemy被添加到andall_sprites和 中。这样做是因为每个组都有不同的用途:cloudsenemies

  • 渲染是使用all_sprites.
  • 位置更新是使用clouds和完成的enemies
  • 碰撞检测是使用 完成的enemies

        您可以创建多个组,以便可以更改精灵移动或行为的方式,而不会影响其他精灵的移动或行为。

十一、游戏速度

        在测试游戏时,您可能已经注意到敌人移动得有点快。如果没有,那也没关系,因为此时不同的机器会看到不同的结果。

        原因是游戏循环处理帧的速度与处理器和环境允许的速度一样快。由于所有精灵每帧移动一次,因此它们每秒可以移动数百次。每秒处理的帧数称为帧速率,正确掌握这一点是可玩游戏和容易被遗忘的游戏之间的区别。

        通常,您需要尽可能高的帧速率,但对于此游戏,您需要稍微放慢帧速率才能玩游戏。幸运的是,该模块time包含一个Clock专门为此目的而设计的模块。

        用于Clock建立可播放的帧速率只需要两行代码。第一个Clock在游戏循环开始之前创建一个新的:

Python
106# Setup the clock for a decent framerate
107clock = pygame.time.Clock()

第二个调用.tick()通知pygame程序已到达帧末尾:

Python
188# Flip everything to the display
189pygame.display.flip()
190
191# Ensure program maintains a rate of 30 frames per second 192clock.tick(30) 

        传递的参数.tick()确定所需的帧速率。为此,.tick()根据所需的帧速率计算每帧应花费的毫秒数。.tick()然后,它将该数字与自上次调用以来经过的毫秒数进行比较。如果没有经过足够的时间,则.tick()延迟处理以确保它永远不会超过指定的帧速率。

        传递较小的帧速率将导致每帧有更多的时间进行计算,而较大的帧速率则提供更流畅(并且可能更快)的游戏玩法:

在pygame中设置帧速率

尝试一下这个数字,看看什么最适合您!

11.1 声音特效

        到目前为止,您已经专注于游戏的游戏玩法和视觉效果。现在让我们探索如何为您的游戏添加一些听觉风味。pygame提供mixer处理所有与声音相关的活动。您将使用此模块的类和方法为各种操作提供背景音乐和声音效果。

        该名称mixer指的是该模块将各种声音混合成一个有凝聚力的整体。使用music子模块,您可以流式传输各种格式的单个声音文件,例如MP3、Ogg和Mod。您还可以使用Ogg 或未压缩的 WAVSound格式保存要播放的单个音效。所有播放都在后台进行,因此当您播放 a 时,该方法会在声音播放时立即返回。Sound

注意:文档指出 MP3 支持有限,不支持的格式可能会导致系统崩溃pygame。本文中引用的声音已经过测试,我们建议在发布游戏之前彻底测试所有声音。

        与大多数事情一样pygame,使用mixer从初始化步骤开始。幸运的是,这已经由 处理了pygame.init()pygame.mixer.init()如果您想更改默认值,只需调用:

Python
106# Setup for sounds. Defaults are good. 107pygame.mixer.init() 108
109# Initialize pygame
110pygame.init()
111
112# Set up the clock for a decent framerate
113clock = pygame.time.Clock()

pygame.mixer.init()接受多个参数,但默认值在大多数情况下都可以正常工作。请注意,如果要更改默认值,需要pygame.mixer.init()在调用 之前调用pygame.init(). 否则,无论您如何更改,默认值都将生效。

系统初始化后,您可以进行声音和背景音乐设置:

Python
135# Load and play background music
136# Sound source: http://ccmixter.org/files/Apoxode/59262
137# License: https://creativecommons.org/licenses/by/3.0/
138pygame.mixer.music.load("Apoxode_-_Electric_1.mp3")
139pygame.mixer.music.play(loops=-1)
140
141# Load all sound files
142# Sound sources: Jon Fincher
143move_up_sound = pygame.mixer.Sound("Rising_putter.ogg")
144move_down_sound = pygame.mixer.Sound("Falling_putter.ogg")
145collision_sound = pygame.mixer.Sound("Collision.ogg")

        第 138 和 139 行加载背景声音剪辑并开始播放。您可以通过设置命名参数来让声音剪辑循环播放并且永不结束loops=-1

        第 143 至 145 行加载了三种声音,您将用于各种声音效果。前两个是上升和下降的声音,当玩家向上或向下移动时播放。最后一个是发生碰撞时使用的声音。您还可以添加其他声音,例如创建时的声音Enemy,或游戏结束时的最终声音。

那么,如何使用音效呢?您希望在特定事件发生时播放每种声音。例如,当船向上移动时,你想玩move_up_sound.play()因此,只要处理该事件,您就添加一个调用 。在设计中,这意味着将以下调用添加到.update()for Player

Python
26# Define the Player object by extending pygame.sprite.Sprite
27# Instead of a surface, use an image for a better-looking sprite
28class Player(pygame.sprite.Sprite):
29    def __init__(self):
30        super(Player, self).__init__()
31        self.surf = pygame.image.load("jet.png").convert()
32        self.surf.set_colorkey((255, 255, 255), RLEACCEL)
33        self.rect = self.surf.get_rect()
34
35    # Move the sprite based on keypresses
36    def update(self, pressed_keys):
37        if pressed_keys[K_UP]:
38            self.rect.move_ip(0, -5)
39 move_up_sound.play() 40        if pressed_keys[K_DOWN]:
41            self.rect.move_ip(0, 5)
42 move_down_sound.play() 

对于玩家和敌人之间的碰撞,您可以在检测到碰撞时播放声音:

Python
201# Check if any enemies have collided with the player
202if pygame.sprite.spritecollideany(player, enemies):
203    # If so, then remove the player
204    player.kill()
205
206    # Stop any moving sounds and play the collision sound
207 move_up_sound.stop() 208 move_down_sound.stop() 209 collision_sound.play() 210
211    # Stop the loop
212    running = False

在这里,您首先停止任何其他声音效果,因为在碰撞中玩家不再移动。然后播放碰撞声音并从那里继续执行。

最后,当游戏结束时,所有声音都应该停止。无论游戏因碰撞而结束还是用户手动退出,都是如此。为此,请在循环后的程序末尾添加以下行:

Python
220# All done! Stop and quit the mixer.
221pygame.mixer.music.stop()
222pygame.mixer.quit()

        从技术上讲,最后几行不是必需的,因为程序在此之后立即结束。但是,如果您稍后决定向游戏添加介绍屏幕或退出屏幕,则游戏结束后可能会运行更多代码。

        就是这样!再次测试一下,您应该看到类似这样的内容:

Pygame 窗口

11.2 关于来源的说明

        您可能已经注意到加载背景音乐时第 136-137 行的注释,其中列出了音乐的来源以及知识共享许可证的链接。这样做是因为该声音的创造者需要它。许可证要求规定,为了使用声音,必须提供正确的归属和许可证的链接。

        以下是一些音乐、声音和艺术来源,您可以搜索有用的内容:

  • OpenGameArt.org:声音、音效、精灵和其他艺术作品
  • Kenney.nl:声音、音效、精灵和其他艺术作品
  • 2D 玩家艺术:精灵和其他艺术作品
  • CC Mixter:声音和音效
  • Freesound:声音和音效

        当您制作游戏并使用从其他来源下载的内容(例如艺术、音乐或代码)时,请确保您遵守这些来源的许可条款。

十二、结论

        通过本教程,您已经了解了游戏编程与pygame标准过程编程的不同之处。您还学习了如何:

  • 实施事件循环
  • 在屏幕上绘制项目
  • 播放音效和音乐
  • 处理用户输入

        为此,您使用了pygame模块的子集,包括displaymixermusic、 timeimageeventkey模块。您还使用了多个pygame类,包括RectSurfaceSoundSpritepygame但这些只是我们能做的事情的皮毛!查看官方pygame文档以获取可用模块和类的完整列表。

        您可以通过单击下面的链接找到本文的所有代码、图形和声音文件:

        示例代码: 单击此处下载本教程中使用的 PyGame 示例项目的源代码。

        也请随意在下面发表评论。快乐Python!

参考文章:PyGame: A Primer on Game Programming in Python – Real Python

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JavaEE就业课 V12.5 完整版

简介 众所周知&#xff0c;在IT互联网领域是靠技术吃饭的&#xff0c;更符合企业需求的先进技术才是硬通货。黑马Java学科一直在行动&#xff0c;一直走在行业最前沿! 四项目制用四个不同类型、不同开发深度的项目&#xff0c;去解决企业用人需求与学员具备相应开发能力匹配的…

谷歌AlphaFold模型迎来重大突破!可以预测生物分子、配体

11月1日&#xff0c;谷歌旗下的AI研究机构DeepMind在官网发布了&#xff0c;蛋白质结构预测模型 AlphaFold的最新技术进展&#xff1a;已显著提升了预测准确性&#xff0c;并将覆盖范围从蛋白质扩展至其他生物分子&#xff0c;包括配体&#xff08;小分子&#xff09;。 据悉&…

第五章 I/O管理 十一、减少磁盘延迟时间的方法

目录 一、交替编号 1、定义&#xff1a; 二、磁盘地址结构的设计 三、错位命名 四、总结 一、交替编号 1、定义&#xff1a; 若采用交替编号的策略&#xff0c;即让逻辑上相邻的扇区在物理上有一定的间隔&#xff0c;可以使读取连续的逻辑扇区所需要的延迟时间更小。 二、…

泛微e-office download.php任意文件

0x01 应用介绍 泛微e-office系统是标准、易用、快速部署上线的专业协同OA软件,国内协同OA办公领域领导品牌,致力于为企业用户提供专业OA办公系统、移动OA应用等协同OA整体解决方案 0x02 影响版本及语法特征 泛微e-offcie9 fofa&#xff1a;app”泛微-EOffice” && b…

【深⼊理解指针4】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 1. 回调函数是什么&#xff1f; 2. qsort使用举例 2.1测试qsort函数排序整型数组 2.2测试qsort函数排序结构体数组 3. qsort函数的模拟实现 3.1测试bubble_sort函数…

Android渲染流程

目录 缓冲区的不同生命周期代表当前缓冲区的状态&#xff1a; 多个源 ViewRootImpl&#xff1a; Android4.0&#xff1a; Android5.0&#xff1a; Android应用程序调用SurfaceFliger将测量&#xff0c;布局&#xff0c;绘制好的Surface借助GPU渲染显示到屏幕上。 一个Acti…

什么是AUTOSAR ComStack,AUTOSAR架构中,CAN通信堆栈CAN Communication Stack介绍

AUTOSAR&#xff08;Automotive Open System Architecture&#xff09;ComStack指的是AUTOSAR架构中的通信堆栈。在AUTOSAR体系结构中&#xff0c;ComStack是指用于不同软件组件&#xff08;如应用软件、基础软件等&#xff09;之间进行通信的一组协议和服务。 在AUTOSAR架构中…

MTSC2023|深圳大会,你关心的技术话题都在这里

MTSC中国互联网测试开发大会 (Make Tester Super Cool&#xff0c; 简称MTSC&#xff09; 是由国内知名的测试技术社区TesterHome发起的软件测试行业技术会议&#xff0c;大会以“软件质量保障体系和测试研发技术交流”为主要目的&#xff0c;旨在为行业搭建一个深入探讨和交流…

“深入理解Nginx的负载均衡与动静分离“

目录 引言一、Nginx简介1. Nginx的基本概念2. Nginx的特点3. Nginx的安装配置 二、Nginx搭载负载均衡三、前端项目打包四、Nginx部署前后端分离项目&#xff0c;同时实现负载均衡和动静分离总结 引言 在现代互联网应用中&#xff0c;高性能和可扩展性是至关重要的。Nginx作为一…

DVWA-SQL Injection SQL注入

概念 SQL注入&#xff0c;是指将特殊构造的恶意SQL语句插入Web表单的输入或页面请求的查询字符串中&#xff0c;从而欺骗后端Web服务器以执行该恶意SQL语句。 成功的 SQL 注入漏洞可以从数据库中读取敏感数据、修改数据库数据&#xff08;插入/更新/删除&#xff09;、对数据…

k8s基本操作命令

目录 1、//查看资源对象简写 2、//查看集群信息 3、//配置kubectl自动补全 4、//node节点查看日志 5、//查看 master 节点状态 6、//查看命令空间 7、//查看default命名空间的所有资源 8、//创建命名空间app 9、//删除命名空间app 10、//在命名空间kube-public 创建…

华为OD机考算法题:计算最大乘积

题目部分 题目计算最大乘积难度易题目说明给定一个元素类型为小写字符串的数组&#xff0c;请计算两个没有相同字符的元素长度乘积的最大值。 如果没有符合条件的两个元素&#xff0c;返回 0。输入描述输入为一个半角逗号分隔的小写字符串的数组&#xff0c;2< 数组长度<…

药监局瑞数6 分析 2023版

网站地址 aHR0cHM6Ly93d3cubm1wYS5nb3YuY24veWFvcGluL3lwamdkdC9pbmRleC5odG1s 清除cookie 选中脚本调试 第一次获取的结果ts 第二次获取的结果是一个294cc83.js&#xff0c;可以固定 第三次获取的结果 content和ts属性每次都要换,还有ts属性一定要和content对应,否则你怎么…

比较BFS和DFS

目录 代码框架对比 引出模板 代码框架对比 dfs是栈的递归&#xff0c;bfs是队列的入出。 引出模板 x可以是栈可以是队列&#xff0c;也可以是随机队列、随机容器&#xff0c;一样可以把整张图遍历出来。

golang中的Interface接口 类型断言、接口赋值、空接口的使用、接口嵌套

Interface整理 文章目录 Interface整理接口嵌套接口类型断言类型判断 type-switch使用方法集与接口空接口实例 接口赋值给接口 接口是一种契约&#xff0c;实现类型必须满足它&#xff0c;它描述了类型的行为&#xff0c;规定类型可以做什么。接口彻底将类型能做什么&#xff0…

Reading:Deep dive into the OnPush change detection strategy in Angular

原文连接&#xff1a;IndepthApp 今天深入阅读并总结Angualr中onPush更新策略。 1. 两种策略 & whats Lview&#xff1f; Angular 实现了两种策略来控制各个组件级别的更改检测行为。这些策略定义为Default和OnPush&#xff1a; 被定义为枚举&#xff1a; export enum…

2023最新全国拉新app推广接单平台合集 地推网推项目平台渠道

平台 ”聚量推客“ 服务商直营的拉新平台 数据和结算都有保障 地推平台承上启下&#xff0c;对上承接甲方项目&#xff0c;对下对接渠道&#xff0c;方便甲方放单又方便渠道统一接单 以下是全国国内十大地推拉新app推广接单平台分享&#xff0c;2023最新全国拉新app推广接单平…

【三方登录-Apple】iOS 苹果授权登录(sign in with Apple)之开发者配置一

记录一下sign in with Apple的开发者配置 前言 关于使用 Apple 登录 使用“通过 Apple 登录”可让用户设置帐户并使用其Apple ID登录您的应用程序和关联网站。首先使用“使用 Apple 登录”功能启用应用程序的App ID 。 如果您是首次启用应用程序 ID 或为新应用程序启用应用程序…

生成瑞利信道(Python and Matlab)

channel h k h_k hk​ is modeled as independent Rayleigh fading with average power loss set as 10^−3 Python import numpy as np# Set the parameters average_power_loss 1e-3 # Average power loss (10^(-3)) num_samples 1000 # Number of fading samples to …

《 博弈论教程(罗云峰版) 》——习题一答案

前言 博弈论这门课程&#xff0c;我们主要参考的教材是《博弈论教程&#xff08;罗云峰版&#xff09;》&#xff0c;但是罗老师的课后习题并没有给出完整的答案&#xff0c;秉着学习的态度&#xff0c;本人结合教材和 PPT 在这里给出课后习题的答案。 由于我们只学了完全信息静…