Skip to content

Latest commit

 

History

History
1120 lines (804 loc) · 84.4 KB

File metadata and controls

1120 lines (804 loc) · 84.4 KB

十一、使用 Pygame 超越turtle——贪食蛇游戏 UI

Python 游戏开发以某种方式与pygame模块相关。到目前为止,我们已经学习了许多关于 Python 的主题和技术,因为我们必须在进入pygame模块之前了解它们。在使用 Pygame 构建游戏时,所有这些概念都将用作分配技术。我们现在可以开始使用面向对象的原则,事件处理的矢量运动,旋转技术来旋转游戏中使用的图像或精灵,甚至使用我们在turtle模块中学习的东西。在 turtle 模块中,我们学习了如何创建对象(请参阅第 6 章面向对象编程),这些对象可用于调试游戏初级阶段的不同功能,我们可以使用 Pygame 构建这些功能。因此,我们到目前为止所学到的知识将与 Pygame 模块的附加功能一起使用,这些功能可以帮助我们制作更具吸引力的游戏。

在本章中,我们将涵盖多个方面,首先学习 Pygame 的基本安装、构建块和不同功能。之后,我们将学习 Pygame 的不同对象。这些模块可用于多种功能,如在屏幕上绘制形状、处理鼠标和键盘事件、将图像加载到 Pygame 项目中等等。在本章末尾,我们将尝试通过添加多个功能使我们的贪食蛇游戏在视觉上具有吸引力,例如定制的蛇图像、苹果作为食物以及游戏的菜单屏幕。最后,我们将把我们的 snake 游戏转换成可执行文件,这样你就可以把你的游戏分发给你的朋友和家人,并从他们那里得到回复。本章将介绍以下主题:

  • Pygame 基础知识
  • Pygame 对象
  • 初始化显示和处理事件
  • 制作 snake 游戏的对象渲染
  • 游戏菜单
  • 转换为可执行文件
  • 游戏测试和可能的修改

技术要求

完成本章需要满足以下要求:

  • Python-3.5 或更高版本
  • PyCharm IDE 下载过程请参考第 1 章了解 Python–设置 Python 和编辑器

本章文件可在找到 https://github.com/PacktPublishing/Learning-Python-by-building-games/tree/master/Chapter11

请查看以下视频以查看代码的运行情况:

http://bit.ly/2o2GngQ

理解 pygame

使用pygame模块编写游戏需要在您的机器上安装 pygame。您可以通过访问网站(www.Pygame.org)从 Pygame 官方库手动下载,也可以使用带有pip install pygame命令的终端进行安装。

pygame 模块可以从前面提到的网站免费下载,因此我们可以按照与其他 Python 模块类似的过程进行下载。然而,我们可以通过使用视觉上更吸引人、更有效的替代 IDEPyCharm来手动下载 pygame,这是我们在第 1 章中下载的了解 Python–设置 Python 和编辑器。在这一章中,我们熟悉了在 PyCharm 中下载和安装第三方软件包的技术。

一旦您将 pygame 软件包下载到 PyCharm 中,请给它一些加载时间。现在,我们可以通过编写以下代码来测试它。以下两行代码检查pygame模块是否下载,如果下载,则打印其版本:

import pygame
print(pygame.version.ver) #this command will check pygame version installed
print(pygame.version.vernum) #alternate command

如果 pygame 成功安装到您的机器上,您将观察以下输出。版本可能有所不同,但在撰写本书时,版本为 1.9.6(2019 年的最新版本)。本书内容适用于pygame的任何版本,因为其向后兼容性。确保您的 pygame 版本高于 1.9+:

pygame 1.9.6
Hello from the pygame community. https://www.pygame.org/contribute.html
1.9.6

Pygame 是许多 Python 游戏开发者的乌托邦;它包含大量的模块,从制作界面到处理用户事件。pygame 中定义的所有这些模块都可以根据我们的需要独立使用。最重要的是,您还可以使用 pygame 制作游戏,这可能是特定于平台的,也可能不是特定于平台的。调用 pygame 的模块类似于调用类的方法。您始终可以使用 pygame 名称空间访问这些类,然后是要使用的类。例如,pygame.key将读取键盘上按下的键。因此,key类负责处理键盘操作。类似地,pygame.mouse模块用于管理鼠标事件。这些模块以及 pygame 的许多其他模块可以相互独立地调用,这使得我们的代码更易于管理和可读。您可以从 pygame 模块的官方文档页面中搜索可用的模块列表,但几乎 80%的游戏只需要四到六个模块。如果您想了解更多关于它们的信息,最好浏览它的官方文档页面。其中,我们在每一个游戏中主要使用两个类,即显示模块,以访问和操作游戏显示;以及鼠标和按键或操纵杆模块,以处理游戏的输入事件。我不会说其他模块不那么重要,但这些模块是游戏的基石。下表摘自 Python pygame 官方文档;它向我们简要介绍了pygame模块及其用法:

| 模块名称 | 说明 | | pygame.draw | 绘制形状、线和点。 | | pygame.event | 处理外部事件。 | | pygame.font | 处理系统字体。 | | pygame.image | 将图像加载到项目中。 | | pygame.joystick | 处理操纵杆移动/事件。 | | pygame.key | 从键盘读取按键。 | | pygame.mixer | 混合、加载和播放声音。 | | pygame.mouse | 读取鼠标事件。 | | pygame.movie | 播放/运行电影文件。 | | pygame.music | 播放流式音频文件。 | | pygame | 捆绑为高级 pygame 函数/方法。 | | pygame.rect | 处理矩形区域,可以创建长方体结构。 |

还有一些,如曲面、时间和变换。我们将在本章和接下来的章节中探讨每一个问题。前面的所有模块都是独立于平台的,这意味着它们可以被调用,而与机器使用的操作系统无关。但也存在一些特定于操作系统的错误,以及由于硬件不兼容或设备驱动程序不正确而导致的错误。如果任何模块与任何机器不兼容,Python 解析器将其返回为None,这意味着我们可以事先检查以确保游戏正常运行。下面的代码行将检查是否存在任何指定的模块(pygame.module_name),如果不存在,它将在 print 语句中返回一条自定义消息,在本例中,没有这样的模块!试试另一个

if pygame.overlay is None:
    print("No such module! Try other one")
    print("https://www.pygame.org/contribute.html")
    exit()

为了完全掌握pygame的概念,我们必须养成观察其他 pygame 开发人员编写的代码的习惯。通过这样做,您将学习使用pygame构建游戏的模式。如果,像我一样,你只有在陷入僵局时才检查文档,那么我们可以制作一个简单的程序来帮助我们理解pygame的概念以及我们可以调用其不同模块的方式。我们将编写一个简单的代码来说明这一点:

import pygame as p #abbreviating pygame with p

p.init()
screen = p.display.set_mode((400, 350)) #size of screen
finish = False

while not finish:
    for each_event in p.event.get():
        if each_event.type == p.QUIT:
            finish = True
    p.draw.rect(screen, (0, 128, 0), p.Rect(35, 35, 65, 65))
    p.display.flip()

在讨论前面的代码之前,让我们运行它并观察输出。您将得到一个几何形状——一个绿色的矩形框,它将在屏幕内呈现一定的高度和宽度。现在,是时候快速记下pygame模块的构建模块了。为了使事情更简单,我在以下几点中列出了它们:

  • import pygame:我们从本书开始就熟悉的进口声明。这一次,我们将 pygame 框架导入到 Python 文件中。

  • pygame.init():此方法将初始化 pygame 中嵌入的模块/类包。这意味着我们可以使用 pygame 的名称空间调用它的其他模块。

  • pygame.display.set_mode((width, height)):作为元组传递的大小(宽度、高度)是所需的屏幕大小。这个尺寸代表我们的游戏机。返回的对象将是一个窗口屏幕或曲面,我们将在其上执行不同的图形计算。

  • pygame.event.get():此语句将处理事件队列。正如我们在前面章节中讨论的,队列将存储用户的不同事件。如果不显式调用此语句,游戏将因大量 Windows 消息而受阻,最终将变得无响应。

  • pygame.draw.rect():我们可以使用 draw 模块进入屏幕。使用此模块可以绘制不同的形状。下一节将介绍更多相关内容-Pygame 对象rect()方法将屏幕对象、颜色和位置作为参数,绘制一个矩形。第一个参数表示 screen 对象,它是 display 类的返回对象;第二种是颜色代码,以 RGB(红、绿、蓝)代码的形式作为元组传递;第三个是矩形的尺寸。为了操作和存储矩形区域,pygame 使用Rect对象。Rect()可以通过组合四个不同的值来创建高度、宽度、左侧和顶部。

  • pygame.QUIT:当您显式关闭 pygame 屏幕时,会调用此事件,这是通过按下游戏控制台右上角的close(X)按钮完成的。

  • pygame.display.flip():这与update()功能相同,可以在屏幕上看到任何新的更新。在制作或闪电显示形状或角色时,必须在游戏结束时调用此方法,以确保所有对象都正确渲染。这将交换 pygame 缓冲区,因为 pygame 是一个双缓冲框架。

上述代码在执行时呈现绿色矩形。如前所述,rect()方法负责创建矩形区域,颜色代码(0、128、0)表示绿色。

不要被这些行话弄得不知所措;在接下来的章节中,您将详细了解它。阅读本章时,请确保养成在代码之间建立逻辑连接的习惯:类似于将游戏从一个位置映射到另一个位置(即显示屏、渲染角色和处理事件)的蓝图。

If you get into a situation where you are unable to close the pygame Terminal, it's surely because you haven't handled the event queue properly. In such cases, you can always stop Python from the Terminal by pressing Ctrl + C.

在跳到下一节之前,我想讨论一下由pygame.init()语句完成的 pygame 初始化的相当简单但深不可测的工作。这只是一条命令行,但它执行的任务比我们想象的要多。顾名思义,这是 pygame 的初始化。因此,它必须初始化pygame包的每个子模块,即displayrectkey等。不仅如此,它还将加载硬件组件的所有基本驱动程序和查询,以便进行通信。

如果希望更快地加载任何子模块,可以显式初始化特定的子模块,并避免所有不必要的子模块。例如,pygame.music.init()只会从 Pygame 维护的子模块桶中初始化音乐子模块。对于我们将在本书中介绍的大多数游戏,pygame模块需要三个子模块以上。因此,我们可以使用通用的pygame.init()方法进行初始化。完成上述呼叫后,我们将具备使用pygame模块的所有指定子模块的能力。

初始化过程结束后,最好开始创建显示屏。显示屏的尺寸取决于游戏的需求。有时,您可能需要为游戏提供全屏分辨率,以使其具有充分的互动性和吸引力。屏幕大小的操作可以通过 pygame surface 对象完成。display 类上的set_mode方法调用返回表示整个窗口屏幕的对象。如果需要,还可以将标题设置为显示屏幕;标题将添加到顶部导航栏中,该导航栏与关闭按钮一起。以下代码表示将字幕或游戏名称添加到游戏屏幕的方式:

pygame.display.set_caption("My First Game")

现在,让我们来讨论通过set_mode方法传递的参数。第一个也是最重要的参数是屏幕表面的尺寸。大小应该作为元组传递,即宽度和高度,这是必需的。其他的是可选的(在上一个程序中,我们甚至都没有使用它们);它们被称为旗帜。我们需要它们,因为与宽度和高度相关的信息有时不足以进行适当的显示。

我们可能需要一个全屏可调整大小的显示器,在这种情况下,标志更适合于显示创建。说到标志,它是一种可以根据情况打开和关闭的功能,相对而言,有时使用它可能会节省时间。让我们观察下表中的一些标志,尽管我们不会很快使用它们,但在这里介绍它们可以避免在接下来的部分中进行不必要的介绍:

| 标志 | 目的 | | FULLSCREEN | 创建覆盖整个屏幕的显示。建议使用窗口屏幕进行调试。 | | DOUBLEBUF | 用于创建双缓冲显示器。强烈建议用于模拟 3D 显示的HWSURFACEOPENGL。 | | HWSURFACE | 用于创建硬件加速显示,即使用显卡存储器而不是主存储器(必须与FULLSCREEN标志结合使用)。 | | RESIZABLE | 创建可调整大小的显示。 | | NOFRAME | 显示时不带边框或边框,也不带标题栏。 | | OPENGL | 创建 OpenGL 可渲染显示。 |

您可以使用按位 OR 运算符将多个标志组合在一起,这有助于在屏幕表面方面获得更好的体验。为了创建双缓冲 OpenGL 渲染显示,您可以在此处将可选标志参数设置为DOUBLEBUF|OPENGL;|是按位OR运算符。即使 pygame 无法呈现我们要求的完美显示,这可能是由于缺少合适的图形卡,pygame 也会为我们做出选择,选择与我们的硬件兼容的显示。

游戏开发最重要的一个方面是处理用户事件,这通常是在游戏循环中完成的。在主游戏循环中,我们通常有另一个循环来处理用户事件-一个事件循环。事件是通知 pygame 在代码外围之外会发生什么的一系列消息。事件可能会有所不同,从用户按键事件到通过第三方库(例如互联网)传输的任何信息。

创建为块的事件存储在队列中,并一直保持在队列中,直到我们显式地解决它们为止。虽然 pygame 的事件模块中有不同的功能,提供了捕获事件的方法,get()是最可靠的,也很容易使用。在获得动作的范围之后,我们可以使用 pygame 事件处理程序,使用诸如pumpget之类的函数来处理它们。请记住,如果您只处理特定的操作,那么事件队列可能会包含您可能不感兴趣的其他表面事件。因此,必须使用事件属性显式地处理事件,类似于我们在前面的示例中使用QUIT事件属性所做的。您还可以通过eventType.__dict__属性完全访问事件对象的属性。我们将在即将到来的事件处理部分中全面了解它们。

在学习如何使用 pygame 升级我们之前制作的snake游戏之前,我们必须了解 pygame 的几个重要概念—pygame 对象进入屏幕处理用户事件。我们将逐一详细了解这些概念。我们将从Pygame 对象开始,在这里我们将学习曲面对象、创建曲面和矩形对象。我们还将学习如何使用 pygame 绘制形状。

Pygame 对象

pygame模块是通过内部使用类创建的,它允许我们创建对象并使用它们的属性,从而使代码可读性和可重用性。如前所述,pygame模块中定义了几个类,可以独立调用这些类来执行独立任务。例如,draw类可用于绘制不同的形状,如矩形、多边形、圆等;event类可以调用 get 或 pump 等函数来处理用户事件。这些调用可以使用对象完成,方法是首先为每个操作创建对象。在本节中,您将探索一些概念,这些概念将帮助您了解如何访问曲面对象、矩形对象和在屏幕上绘图。

创建自定义维度的空白曲面的最基本方法是从 pygame 名称空间调用Surface构造函数。创建Surface类的对象时,必须传递包含宽度和高度信息的元组。以下代码行创建 200 x 200 像素的空白表面:

screen_surface = pygame.Surface((200,200))

我们可以指定一些最终会影响屏幕视觉效果的可选参数。可以将标志参数设置为以下一个或多个参数:

  • HWSURFACE:创建硬件表面。这在游戏环境中不是很重要,因为它是由 pygame 在内部完成的。
  • SRCALPHA:使用alpha 信息进行背景转换,是指使屏幕背景透明的过程。它使用 alpha 转换创建曲面。alpha 信息将使曲面的一部分透明。如果将此用作可选标志,则必须指定一个或多个必需参数,包括深度,并将其值指定为 32,这是 alpha 信息的标准值。

此外,如果要创建包含图像作为背景的曲面,可以从pygame模块调用image类。image 类包含load方法,可以使用需要渲染的背景图像文件名的参数调用该方法。传递的文件名应为全名及其原始扩展名:

background_surface = pygame.image.load(image_file_name.extension).convert()

image类调用的 load 函数从您的机器读取图像文件,然后返回包含图像的曲面。这里,屏幕尺寸将由图像大小决定。Surface对象的convert()成员函数将指定的图像转换为您的显示屏支持的格式。

现在,让我们学习如何在一个曲面(通常称为次曲面)内创建多个曲面。

次曲面

顾名思义,子曲面是单个主曲面内部的嵌套曲面列表。主曲面可以作为父曲面引用。可以使用Surface构造函数、set_mode或图像使用上述任何方法创建父曲面。当您绘制到次曲面上时,它也将绘制到父曲面上,因为次曲面也是父曲面的一部分。创建一个次表层是很容易的;您只需要从Surface对象调用subsurface方法,传递的参数应该指示要覆盖的parent类的位置。通常,传递的坐标应在父屏幕内创建一个小矩形。以下代码显示了如何创建次表面:

screen = Pygame.load("image.png")
screen.subsurface((0,0),(20,20))
screen.subsurface((20,0),(20,20))

您可以将这些子曲面存储到数据结构(如字典)中,以便轻松引用它们。你可以观察到在地下方法中传递的位置,它们是异教徒。点(0,0)始终表示次表面从父屏幕的左上角开始。

子曲面有几种可用的方法,所有这些方法都可以从其官方文档中窥探。最有用的方法之一是get_parent(),它返回次表层的父曲面。如果未使用任何子面调用get_parent方法,则返回None

现在,我们将了解下一种使用 pygame 制作任何游戏时经常使用的有关曲面对象的方法,即blit,它代表位块传输

快闪你的物体

虽然牛津字典中可能没有定义术语blitting,但它在使用 pygame 制作游戏时具有更大的意义。通常称为位边界块传输,或块信息传输,blit是一种将图像从一个表面复制到另一个表面的方法,通常通过剪切或移位。让我们假设你有Surfaceb(你的屏幕),你想在屏幕上画一个形状,比如说,一个矩形。所以,你要做的是画一个矩形,然后把一个矩形块的缓冲区转移到屏幕缓冲区。这个过程被称为布点。当我们介绍使用 pygame 的游戏时,您会发现它被用于绘制背景、字体、字符以及您可以想象的一切。

为了blit曲面,您可以从生成的曲面对象调用blit方法,该曲面对象通常是显示对象。您必须将源曲面(如角色、动画和图像)以及坐标作为参数传递给blit。与理论上听起来的情况相比,blit方法的调用相当简单。以下代码行显示了如何blit在指定位置(0,0)显示背景图像,该位置是屏幕的最上角:

screen.blit(image_file_name.png, (0,0))

假设您有一组需要根据不同帧速率进行渲染的图像。我们也可以使用blit方法来实现这一点。我们可以在生成的屏幕的不同区域更改帧数和blit的值,以制作图像的动画。这通常是在静态图像的情况下完成的。例如,我们将在下一章中使用 Pygame 创建 flappy bird 游戏的克隆。

在那个游戏中,我们必须blit将管道和鸟(flappy 游戏中的角色)放在不同的位置,脱离静态图像,我们通常称之为精灵。这些精灵只不过是可以直接从互联网上使用的图像,或者我们可以根据自己的需要制作一个。下面的代码显示了一种基于不同帧速率的blit图像的简单方法:

screen.blit(list_of_images, (400, 300), (frame_number*10, 0, 100, 100))

在 flappy bird 游戏中,图像列表包含鸟在两个位置的图像:飞行和坠落。根据用户事件,我们将使用blit方法渲染每个事件。

在进入下一节之前,让我们先了解一下帧速率这个可能微不足道但必须了解的主题。这个术语经常被用作衡量游戏性能的基准。视频游戏中的帧速率可以推断出最终的模拟运动,或者你在屏幕上观察到的图像被刷新或获取多少次的运动。帧速率是以每秒进行的测量(不要将其与术语第一人称射击手混淆)。

决定游戏帧速率的因素有很多,但当代游戏玩家不希望游戏滞后,也不希望游戏呆滞。因此,比率越高越好。低帧速率可能在不合适的时候造成不幸的情况。例如,在游戏中,用户可以从某个高度跳跃或被砍;低 FPS 会导致系统延迟,经常会导致屏幕冻结,用户无法与游戏互动。许多现代游戏,例如,第一人称射击游戏,如 Pubg 和 Fortnite,都是为了接近每秒 60 帧的帧速率而开发的。但在 Pygame 开发的简单游戏中,15 到 30 FPS 的速度都是可以接受的。一些评论家认为,任何低于 30 帧/秒的动画都会产生起伏的动画和不切实际的动作,但正如我们所知,pygame 允许我们主要制作迷你游戏。因此,15 到 30 FPS 的速度对我们来说就足够了。

让我们跳到下一节,我们将学习如何使用pygame绘图模块绘制不同的形状。

使用 pygame 绘图模块绘图

最常用的模块之一是draw,它声明了大量的方法,可用于在游戏屏幕上绘制形状。使用此模块的目的是绘制直线、圆和多边形,实际上是任何几何形状。你可能想知道使用它的重要性,因为它有着广泛的用途。我们可能必须创建形状以便执行裁剪,或者将精灵或图像快速显示在屏幕上。有时,您可能希望将这些形状用作游戏中的角色;像俄罗斯方块这样最受欢迎的游戏就是一个很好的例子。尽管在创建游戏时您可能觉得它不是很有用,并且您可能会使用精灵,但在测试游戏动画时它可能会有所帮助。你不必到任何地方去了解这些形状在游戏开发中的重要性;你可以观察我们迄今为止创造的游戏。到目前为止,在贪食蛇游戏中,我们一直使用简单的矩形来表示蛇的身体和头部。虽然它可能不是很吸引人,但在游戏的初期,我们总是可以使用这种形状制作游戏。

使用 pygame 创建这样的形状比使用任何其他模块都要容易。我们可以调用 draw 模块以及函数名。函数名将是要绘制的形状的名称。例如,对于圆,我们将使用pygame.draw.circle(),对于矩形,我们将使用:pygame.draw.rect()pygame.draw中函数的前两个参数是要绘制的曲面,然后是要绘制的颜色。绘制函数的第一个参数是Surface对象,它表示要在其中绘制的屏幕。下一个参数表示要在其上绘制形状的屏幕位置。

这三个参数对于每个几何形状都是必需的,但最后一个参数取决于形状。该方法的最后一个参数表示绘制此类形状时使用的数学量,例如圆的半径或直径。通常,传递的第三个参数应以xy坐标的形式表示坐标位置,其中点(0,0)表示屏幕最左上方的区域。下表列出了“绘制”模块中可用的方法数量,这些方法可用于绘制任何几何形状:

| 功能 | 说明 | | rect | 画一个矩形 | | polygon | 绘制正多边形(具有三条或更多封闭边的几何形状) | | line | 划一条线 | | lines | 画几条线 | | circle | 画圈 | | ellipse | 画一个椭圆 |

作为一个例子,让我们使用circle方法,观察pygame绘图模块的工作情况。为了画一个圆,我们需要知道半径的值。半径是从圆心到圆边缘的距离,即圆的圆弧。调用圆函数时应传递的参数是 screen,它表示曲面对象;圆圈的颜色;应绘制圆的位置;最后是圆的半径。由于我们使用随机模块为圆生成半径的随机值,而不是给定特定值,下面的代码创建了多个圆,在随机位置具有随机宽度,并且明显具有随机颜色。如果为每个参数键入特定值,将绘制一个形状:

import pygame as game
from pygame.locals import *
from random import *
import sys

game.init()
display_screen = game.display.set_mode((650, 470), 0, 32)
while True:
    for eachEvent in game.event.get():
        if eachEvent.type == QUIT:
            sys.exit()
    circle_generate_color = (randint(0,255), randint(0,255), 
                            randint(0,255))
 circle_position_arbitary = (randint(0,649), randint(0,469))
 circle_radius_arbitary = randint(1,230)
    game.draw.circle(display_screen, circle_generate_color, 
    circle_position_arbitary, circle_radius_arbitary)
    game.display.update()

The code, which will be written from this chapter onward, is in the PyCharm Community IDE, which was downloaded in Chapter 1Getting to Know Python - Setting Up Python and the Editor. Make sure that pygame is installed on the interpreter main directory so that pygame can be used universally on any newly created Python file.

使用 PyCharm IDE 时可以注意到的一个重要特性是,它可以为我们提供安装pygame模块时附带的所有模块的信息。要确定draw模块中包含哪些功能,请从代码中选择circledraw键盘,然后按键盘上的Ctrl+B,这将反过来将您重定向到draw模块的声明文件。

当谈到代码时,它很容易理解。主要的三行代码高亮显示,以便您可以直接观察它们的重要性。大多数情况下,第三行调用circle方法,在draw模块中声明,它获取参数、屏幕对象、颜色、位置和半径,以便绘制一个圆。前面程序的输出将不间断地打印具有随机半径和随机颜色的圆,直到和除非用户手动关闭屏幕,这是由于事件处理程序,并通过pygame.event.get方法完成。

类似地,您可以绘制多种形状和大小的多边形,范围从三边三角形到 9999 边多边形。就像我们使用pygame.draw.circle函数创建圆一样,我们可以使用pygame.draw.polygon绘制任何类型的多边形。对 polygon 函数的调用采用点列表形式的参数,并将使用这些点绘制多边形形状。我们可以用相似的方式用特定的名称画出不同的几何形状。

在下一节中,我们将学习使用pygame模块初始化显示屏和处理键盘和鼠标事件的不同方法。

初始化显示和处理事件

首先,游戏开发者将关注如何通过让玩家感觉自己在玩游戏,从而使游戏更具互动性。在这种情况下,两件事必须结合在一起,一是视觉上有吸引力的展示,二是处理玩家的事件。我们不希望我们的玩家被一个糟糕的显示屏和一个动作迟缓的游戏弄得不知所措。在本节中,我们将讨论开发人员在制作游戏时必须考虑的两个主要问题:通过调整可用的可选参数和处理用户操作事件(如按下键盘键或鼠标按钮)来初始化显示的不同方式。要创建的显示类型取决于您计划开发的游戏类型。

在使用pygame模块制作游戏时,您必须记住的一点是,在游戏中添加更多的动作会影响游戏的流畅性,这意味着如果您在游戏中添加多个功能,游戏的交互性就会越差。因此,我们将主要关注使用pygame模块制作迷你游戏。市场上有更多高级 Python 模块可用于制作高功能游戏,我们将在接下来的章节中探讨它们。现在,我们将看到如何初始化显示,这是通过选择一个较低的分辨率来完成的,因为我们不希望我们的游戏以任何方式落后。

从现在开始制作的任何游戏都将具有固定的低分辨率,但您可以通过让用户选择自己定制的显示器来自己进行实验。以下代码是创建 pygame 窗口的简单方法,我们在前面编写的代码中也看到了这一点:

displayScreen = pygame.display.set_mode((640, 480), 0, 32) #standard size

set_mode()的第一个参数是屏幕的尺寸。元组(640480)中的值表示屏幕的高度和宽度。此维度值将创建一个与大多数桌面屏幕兼容的小窗口框。然而,我们可能会遇到这样一种情况:游戏必须有一个FULLSCREEN,而不是一个小屏幕。在这种情况下,我们可以使用一个可选参数,给出FULLSCREEN的值。显示全屏的代码如下所示:

displayScreen = pygame.display.set_mode((640, 480), FULLSCREEN, 32)

然而,我们可能会观察到使用全屏模式与定制显示之间的性能差异。在全屏模式下打开游戏会运行得更快,因为它不会与其他后台桌面屏幕交互,而另一个带有自定义显示的屏幕可能会与您机器上其他正在运行的显示屏幕合并。除此之外,调试小屏幕游戏比全屏游戏更容易,因为你应该解决全屏模式下关闭游戏的替代方法,因为关闭按钮将不可见。要检查电脑支持的不同显示分辨率,可以调用list_modes()方法,该方法将返回包含分辨率列表的元组,如下所示:

>>> import pygame as p
>>> p.init()
>>> print(p.display.list_modes())
[(1366, 768), (1360, 768), (1280, 768), (1280, 720), (1280, 600), (1024, 768), (800, 600), (640, 480), (640, 400), (512, 384), (400, 300), (320, 240), (320, 200)]

有时,您可能会觉得屏幕上显示的图像质量略有下降。这主要是由于图形卡功能较少,无法提供所需图像的颜色。这由pygame进行补偿,他将图像转换为适合您设备的图像。

在某些游戏中,您可能希望用户决定选择显示屏的大小。取舍关系到玩家是选择高质量的视觉效果还是让游戏顺利运行。我们的主要目标是处理该事件,它可以在可调整大小的屏幕和全屏之间切换屏幕。下面的代码演示了在窗口屏幕和全屏屏幕之间的切换,反之亦然。当用户按键盘上的F时,会在屏幕之间切换。

运行程序时,窗口屏幕和全屏之间的切换过程不是自发的。这是因为pygame需要一些时间来检查图形卡的功能,如果图形卡功能不足,则需要处理图像本身的质量:

import pygame as p #abbreviating pygame module as p
from pygame.locals import *
import sys
p.init()
displayScreen = p.display.set_mode((640, 480), 0, 32)

displayFullscreen = False
while True:
    for Each_event in p.event.get():
        if Each_event.type == QUIT:
            sys.exit()
        if Each_event.type == KEYDOWN:
            if Each_event.key == K_f:
                    displayFullscreen = not displayFullscreen
                    if displayFullscreen:
                        displayScreen = p.display.set_mode((640, 480), 
                                        FULLSCREEN, 32)
                    else:
                        displayScreen = p.display.set_mode((640, 480), 0,
                                        32)

    p.display.update()

让我们逐行了解显示切换过程:

  1. 您必须从导入pygame模块开始。第二条 import 语句将导入 Pygame 使用的常量。但是,它的内容会自动放在pygame模块名称空间中,我们可以使用pygame.locals只包含pygame常量。常数的示例包括:KEYDOWN、键盘k_constants等。

  2. 您将在游戏开始时设置默认显示模式。每当您第一次运行程序时,此显示将是默认显示;将呈现当前自定义显示。默认情况下,我们有一个通过的显示屏幕(640480)。

  3. 要切换显示屏幕,您必须创建一个布尔变量Fullscreen,它将是TrueFalse,并基于此,我们将设置屏幕的模式。

  4. 在主循环中,必须处理键盘键操作的事件。每当用户在键盘上按F时,我们都会更改布尔变量的值,如果FULLSCREEN变量的值为True,我们必须将显示更改为全屏。额外的标志FULLSCREEN作为第二个参数添加到add_mode()函数中,深度为 32。

  5. 在 else 部分,如果全屏的值为False,则必须以窗口版本显示屏幕。相同的键F用于在窗口和全屏之间切换屏幕。

现在我们已经了解了如何使用不同的可用标志修改窗口视觉效果,让我们进入下一节,在这里我们将讨论接受用户输入和控制游戏,这通常被称为h**和处理用户事件

处理用户事件

在传统的 PC 游戏中,我们通常只看到玩家使用键盘玩游戏。即使在今天,大多数游戏都完全依赖键盘操作。随着游戏行业的发展,我们可以接受来自多种输入设备的用户输入,如鼠标和操纵杆。通常,鼠标用于处理动作,从而提供游戏视觉效果的全景视图。如果您曾经玩过《反恐精英》或任何第一人称射击游戏,则鼠标允许玩家以多个角度旋转视图,而键盘操作则控制玩家的移动,例如向左、向右、跳跃等。键盘通常用于触发诸如射击和闪避之类的动作,因为它的动作很像一个开关。开关只有两种可能:打开或关闭;键盘键也可以按下,也可以不按下,这概括了处理键盘操作的技术。在典型的 19 世纪游戏中,我们通常通过检查键盘的动作来生成游戏敌人。当用户不停地按键盘键时,我们通常会大量生成敌人。

这两种输入设备的组合,即鼠标和键盘,非常适合这些游戏,因为鼠标能够处理方向运动,并且动作平稳。例如,在玩第一人称射击游戏时,使用键盘和鼠标旋转玩家。当任何敌人在你身后时,你通常使用鼠标快速旋转到该位置,而不是使用键盘旋转。

为了检测和收听(捕获)所有键盘键,您必须使用pygame.key模块。该模块能够检测是否按下任何键,甚至支持方向移动。该模块还能够处理任何键盘操作。基本上,pygame 中有两种处理按键的方法:

  • 通过处理按键事件,按下键盘上的键时会触发按键事件。
  • 通过处理键盘上的键释放时触发或发出的按键事件。

虽然这些事件处理程序是检查按键的好方法,但处理键盘输入以进行移动并不适合它们。为了绘制下一帧,我们需要事先知道是否按下了键盘键。因此,直接使用pygame.key模块将赋予我们有效处理键盘键的能力。键盘的键(a-z、0-9 和 F1-F12)具有 pygame 预定义的键常量。这些键常量可以称为 keycode,用于唯一地标识它们。Keycode 始终以K_开头。对于每个可能的键,keycode 看起来像(K_aK_z)(K_0K_9),并包含其他常量,如K_SPACEK_LEFTK_RETURN。由于硬件不兼容,pygame 无法处理某些键盘键。一些在线开发者在《键盘是邪恶的》一书中讨论了这种异常现象。您可能希望参考他们来更详细地了解这一点。

处理任何键盘操作的最基本方法是使用pygame.key get_pressed功能。此方法非常强大,因为它将布尔值分配给所有键盘常量;无论是True还是False。我们可以使用if条件来检查这一点:键盘的值是常量True还是False?如果是True,则很明显按下了一个键。get_pressed方法调用返回键常量的字典,其中字典的键是键盘的键常量,字典的值是布尔值,dictionary_name[K_a] = True。假设您正在制作一个程序,该程序将使用向上移动作为跳转按钮。您必须编写以下代码:

import pygame as p
any_key_pressed = p.key.get_pressed()
if any_key_pressed[K_UP]:
    #UP key has been pressed
    jump()

让我们更详细地了解pygame.key模块。以下每个功能都将处理键盘键,但方法不同:

  • pygame.key.get_pressed():正如我们在前面的代码中看到的,该方法返回一个字典,其中包含键盘每个键的布尔值。您必须检查按键的值,以确定是否按下了按键。换句话说,如果键盘键的任何值被设置为True,则表示该索引的键被按下。
  • pygame.key.name():顾名思义,此方法调用将返回被按下键的名称。例如,如果我得到一个值为 115 的键的KEY_UP事件,您可以使用key.name打印出键的名称,在本例中是一个字符串s
  • pygame.key.get_mods():这将确定按下了哪个修改器键。修改键是与ShiftAltCtrl组合的普通键。为了检查是否有任何修改键被按下,您必须先调用get_mods方法,然后调用K_MOD。方法调用和常量由按位 and 运算符分隔,例如,event.key == pygame.K_RIGHTpygame.key.get_mods() & pygameKMOD_LSHIFT方法可用于检查左
  • pygame.key.set_mods():您也可以临时自动设置修改键,观察修改键被按下的效果。要设置多个修改器关键点,我们通常使用按位 OR 运算符(|)组合它们。例如,pygame.key.set_mods(KMOD_SHIFT | KMOD_LSHIFT)将设置 SHIFT 和 LEFTSHIFT修改键。
  • pygame.key.get_focused():要从键盘上抓取每个按下的键,显示器必须聚焦于键盘动作。此方法调用将通过检查显示器是否从系统接收键盘输入来返回布尔值。在游戏的情况下,可能有一个定制的屏幕,游戏屏幕不集中,因为你可能正在使用其他应用程序;这将返回False,这意味着显示器未激活或未聚焦以收听键盘操作。但在全屏显示模式下,您将完全聚焦在单屏上,在这种情况下,此方法将始终返回True

pygame 还有两个以上的按键功能,例如get_repeatset_repeat,它们在您希望在连续按住键盘上的任意键时重复操作的情况下非常有用。例如,打开记事本,连续按s键。您将看到字符s将被打印多次。可使用pygame.key set_repeat功能嵌入此功能。此函数将接受两个参数:延迟和间隔(以毫秒为单位)。

第一个延迟值用于重复按键之前的初始延迟,而下一个间隔值用于重复按键之间的延迟。您可以使用不带参数的calling set_repeat方法禁用这些关键重复功能。默认情况下,pygame 初始化时,密钥重复功能被禁用。因此,您不必手动禁用它。请访问以下网站获取 pygame 官方文档,以了解有关 pygame 关键功能的更多信息:https://www.pygame.org/docs/ref/key.html

通过指定上、下、左或右键,可以使用键盘设置游戏屏幕上精灵/图像/对象的移动。到目前为止,我们一直在使用不同的模块(如 Python turtle 和 curses)来实现这一点。但是,我们无法处理静态精灵或图像的移动。我们只处理几何对象的上、下、左、右和关键事件,但现在 pygame 允许我们使用更复杂的图形并相应地处理它们。

我们可以分配任何键盘键来执行方向移动,但按照传统方法,我们可以适当地使用光标键或箭头键,因为它们完全放置在键盘上,这使得玩家可以轻松地玩。但在一些复杂的多人游戏中,例如第一人称射击游戏,AWSD键用于定向移动。现在,您可能想知道,为了使任何箭头键的行为能够用于定向运动,您必须做些什么。只要回忆一下向量的力量:无论你使用何种语言或模块,这一数学概念对游戏开发都很有用。移动任何几何形状和图像的技术是相同的;我们需要创建一个向量,该向量指向我们可能想要的方向。表示游戏角色的位置非常简单:可以使用(xy位置在 2D 中表示,也可以使用(xyz位置在 3D 中表示。但是,方向向量是必须添加到当前矢量位置才能更改到下一帧的单位数量。例如,按下键盘上的向下键,我们必须向下移动,而不改变x位置,但在y坐标中增加一个单位。下表说明了四个方向的方向运动:

| 位置 | 方向向量 | | 向上的 | (0, -1) | | 向下 | (0, 1) | | 左边 | (-1, 0) | | 正当 | (1, 0) |

我们可能还希望玩家允许对角移动,如下图所示:

上图表示向上和向右键盘键的矢量运动。假设在游戏开始时,玩家处于(0,0)位置,这意味着他们处于中间。现在,当用户按下向上(箭头键)键盘键时,将向上方向向量(0,-1)添加(0,0),得到的向量将是玩家的新位置。对角线移动(在本例中,两个键的组合,向上和向右)将为播放器的当前矢量位置增加(0.707,-0.707)。我们可以使用这种矢量运动技术为任何游戏对象提供定向运动,无论是精灵/静态图像还是几何形状。以下代码表示使用 pygame 事件处理技术的矢量运动:

import pygame as p
import sys
while True:
    for anyEvent in p.event.get():
        if anyEvent.type == QUIT:
            sys.exit()
        any_keys_pressed = p.key.get_pressed()
        movement_keys = Vector2(0, 0) #Vector2 imported from gameobjects
        #movement keys are diectional (arrow) keys
        if any_keys_pressed[K_LEFT]:
            movement_keys.x =1
        elif any_keys_pressed[K_RIGHT]:
            movement_keys.x = +1
        if any_keys_pressed[K_UP]:
            movement_keys.y = -1
        elif any_keys_pressed[K_DOWN]:
            movement_keys.y = +1
        movement_keys.normalize() #creates list comprehension 
                                   [refer chapter 7]

虽然知道如何使事物朝八个方向运动(四个基本方向和四个对角线运动)是值得的,但使用这八个方向并不能使游戏更流畅。假设,让事情朝八个方向发展有点人为。然而,如今的游戏允许游戏玩家通过 360 度设备观察风景。因此,为了制作具有这些功能的游戏,我们可以使用按键进行旋转运动,而不是使用八个键盘动作。为了计算旋转的合成矢量,我们必须使用数学模块计算角度的正弦和余弦。角度的正弦负责x分量中的运动,而余弦负责y分量中的运动。这两个函数都以弧度表示角度;如果旋转角度以度为单位,则必须使用(degree*pi/180将其转换为弧度):

resultant_x = sin(angle_of_rotational_sprite*pi/180.0) 
#sin(theta) represents base rotation about x-axix
resultant_y = cos(angle_of_rotational_sprite*pi/180.0)
#cos(theta) represents height rotation about y-axis
new_heading_movement = Vector2(resultant_x, resultant_y)
new_heading_movement *= movement_direction

现在,让我们学习如何实现鼠标控制,并观察如何在游戏开发中使用鼠标控制。

鼠标控制

如果你想让游戏更具交互性,鼠标控制和键盘控制都会派上用场。有时,处理八个方向键是不够的,在这种情况下,还必须处理鼠标事件。例如,在 flappy bird 等游戏中,用户基本上必须能够玩鼠标,尽管它在手机游戏中使用屏幕点击,但在 PC 上,您必须能够提供鼠标操作。在显示屏上画一个鼠标光标非常简单;您需要从MOUSEMOTION事件中获取鼠标的坐标。与键盘get_pressed功能类似,您可以调用pygame.mouse.get_pos()功能来获取鼠标的位置。如果你想让游戏中的角色旋转,或者做一个屏幕点击游戏,或者即使你想上下查看游戏屏幕,鼠标移动在游戏中是非常有用的。

为了了解处理鼠标事件的方法,让我们看一个简单的示例:

import pygame as game #now instead of using pygame, you can use game

game.init()
windowScreen = game.display.set_mode((300, 300))
done = False

# Draw Rect as place where mouse pointer can be clicked
RectangularPlace = game.draw.rect(windowScreen, (255, 0, 0),(150, 150, 150, 150))
game.display.update()
# Main Loop
while not done:
    # Mouse position and button clicking.
    position = game.mouse.get_pos()
    leftPressed, rightPressed, centerPressed = game.mouse.get_pressed()
    #checking if left mouse button is collided with rect place or not
 if RectangularPlace.collidepoint(position) and leftPressed:
        print("You have clicked on a rectangle")
    # Quit pygame.
    for anyEvent in game.event.get():
        if anyEvent.type == game.QUIT:
            done = True

我强调了代码的一些重要部分。重点主要放在那些帮助我们理解鼠标事件实现的部分。让我们逐行查看代码:

  1. 首先,您必须定义一个对象,一个将设置鼠标事件侦听器来捕获它的区域。在这种情况下,您必须使用pygame.draw.rect方法调用将区域声明为矩形。
  2. 在主循环中,您必须获得鼠标的位置,它将使用pygame.mouse.get_pos()函数表示当前光标坐标。
  3. 然后,您必须从pygame.mouse模块调用get_pressed()方法。将返回布尔值列表。左、右或中的布尔True值表示在特定实例中,按下了特定的鼠标按钮,而其余两个则没有按下。这里,我们为三个鼠标按钮捕获了三个布尔值。
  4. 现在,要检查用户是否按下了矩形,您必须调用collidepoint方法并将位置值传递给它。该位置表示当前光标位置。如果在当前位置单击鼠标,pressed1将成为True
  5. 当这两条语句都是True时,您可以相应地执行任何操作。请记住,此程序不会打印消息,即使您在窗口屏幕中单击,窗口屏幕不是矩形的一部分。

pygame.key模块类似,让我们详细了解pygame.mouse模块。此模块包含八个功能:

  • pygame.mouse.get_rel():以元组的形式返回鼠标的相对运动,xy相对运动。
  • pygame.mouse.get_pressed():返回三个布尔值,表示鼠标按键,如果其中一个是True,则假设对应的按键被按下。
  • pygame.mouse.set_cursor():设置标准光标图像。这是很少需要的,因为通过在鼠标坐标上点选图像可以获得更好的结果。
  • pygame.mouse.get_cursor():执行两个不同的任务:一是设置光标标准图像,二是获取系统光标的确定性数据。
  • pygame.mouse.set_visible():改变标准鼠标光标的可见性。如果False,光标将不可见。
  • pygame.mouse.get_pos():返回一个元组,包含画布中鼠标点击位置的xy值。
  • pygame.mouse.set_pos():设置鼠标位置。它采用一个元组形式的参数,该元组包含画布中的xy坐标。
  • pygame.mouse.get_focused():此布尔函数结果基于窗口屏幕是否从鼠标获取输入的条件。类似于key.get_focused功能。当 pygame 在当前窗口屏幕中运行时,窗口将从鼠标获取输入,但仅当 pygame 窗口被选中并在显示器前面运行时。如果另一个程序正在后台运行并且被选中,那么 pygame 窗口将不会从鼠标中获得输入,并且此方法调用的输出将是False

你可能玩过驾驶飞机或摧毁坦克的游戏,鼠标用作瞄准装置,键盘用于移动和射击动作。这些游戏互动性很强。因此,您必须尝试制作一个能够尽可能多地结合这两个事件的游戏。这两种类型的事件非常有用,对于任何游戏开发都很重要。我建议您花点时间来体验这些事件。如果可能的话,试着只用几何物体来制作你自己的游戏。现在,我们将学习如何使用 pygame 和我们自己的精灵制作游戏。

这个游戏将是一个修改版本的贪食蛇游戏,是由turtle模块在上一章。所有的概念都是一样的,但我们将制作视觉上吸引人的角色,而不是单调乏味的游戏角色,我们将使用 pygame 处理事件。

对象渲染

计算机以颜色网格的形式存储图像。大多数情况下,RGB(红色、绿色和蓝色)足以提供像素的信息。但除了 RGB 值之外,还有一个图像组件在处理 pygame 游戏开发时非常有用,即 alpha 信息(通常称为属性组件)。alpha 信息表示图像透明度。这些额外的信息非常有用;在 pygame 中通常发生的情况是,我们通常在激活 alpha 属性的情况下绘制或放置一个图像在另一个图像上。通过这样做,我们可以通过它看到部分背景。我们通常使用第三方软件,如 GIMP,以使图像的背景透明。

除了知道如何使图像的背景透明外,我们还必须知道如何将它们导入到我们的项目中,以便我们可以使用它们。将任何静态图像或精灵导入 Python 项目都很容易,pygame 使之更容易。我们有一个图像模块,它提供了导入图像的加载方法。调用 load 方法时,必须传递一个包含完整文件名(包括扩展名)的图像。以下代码表示将图像导入 Python 项目的方法:

gameBackground = pygame.image.load(image_filename_for_background).convert()
Image_Cursor = pygame.image.load(image_filename_mouseCursor).convert_alpha()

The image that you want to import into the game project should be in the same directory where the game project resides. For example, if the Python file is saved in the snake directory, the image that is loaded by the Python file should also be saved inside the snake directory.

在图像模块中,加载功能将从硬盘加载文件,并返回新生成的曲面,其中包含要加载的图像。第一次调用pygame.image.load将读取图像文件,然后立即调用convert方法,该方法将图像转换为与显示相同的格式。由于图像和显示屏的转换处于相同的深度级别,因此绘制到屏幕中的速度相对较快。

第二条语句是加载鼠标光标。有时,您可能希望将定制的鼠标光标加载到游戏中,第二行代码就是实现这一点的方法。在加载mouse_cursor的情况下,使用convert_alpha代替转换功能。这是因为鼠标光标的图像包含关于透明度的特殊信息,称为α信息,并使部分图像不可见,无法检测。通过禁用 alpha 信息,我们的鼠标光标将被矩形或方形包围,从而使光标看起来不好看。基本上,alpha 信息用于表示具有透明背景的图像。

现在我们已经学习了如何将图像导入 Python 项目,让我们学习如何旋转这些图像。这是一项非常有用的技术,因为在制作游戏时,我们可能需要旋转一定程度的图像,以使游戏吸引人。例如,假设我们正在制作一个贪食蛇游戏,我们正在使用一个蛇的头部图像。现在,当用户按下键盘上的向上键时,蛇头应该旋转,并且必须平稳地向上移动。这是由pygame.transform模块完成的。为了便于旋转,可以从转换模块调用Rotate方法。旋转方法采用从image.load()函数加载的图像表面,并指定必须进行旋转的度数。通常,变换操作会调整像素大小或移动部分像素,以使表面看起来与显示屏兼容:

pygame.transform.rotate(img, 270) #rotation of image by 270 degree

在我们开始开发我们自己的视觉吸引力贪食蛇游戏之前,您必须了解 Pygametime模块。点击此链接了解更多信息:https://www.pygame.org/docs/ref/time.html#pygame.time.ClockPygame.time模块用于监控时间。时钟还提供了几个功能来帮助控制游戏的帧速率。术语帧速率是连续图像在显示屏上显示的速率或频率。无论何时调用时间模块的Clock()构造函数,它都会创建一个对象,用于跟踪时间。Pygame 时间模块中有多种函数由 Pygame 开发人员内部定义。然而,我们将只使用tick方法,它将更新时钟

每帧调用一次Pygame.time.Clock.tick()。在函数的两次连续调用之间,tick()方法以毫秒为单位跟踪每次调用之间的时间。通过每帧调用Clock.tick(60)一次,程序只能在 60 FPS 的范围内运行,即使处理能力更高,也不能超过 60 FPS。因此,它可以用来限制游戏的运行速度。对于 Pygame 开发的游戏来说,这一点很重要,因为我们希望顺利运行游戏,而不是用 CPU 资源进行补偿。在 Pygame 开发的游戏中,每秒帧数(帧速率)的值可以是 15 到 40。

现在,我们有足够的信息使用 Pygame 制作我们自己的游戏,它将为游戏角色提供精灵和平滑的动作。我们将在下一节中初始化显示。我们将使用 Pygame 模块更新我们的 snake 游戏。

初始化显示

初始化显示非常基本;您可以从导入基本模块开始,并向set_mode()方法提供特定的显示尺寸,以创建窗口屏幕。除此之外,我们还要声明一个主循环。请参阅以下代码以遵守主循环的声明:

import pygame as game
from sys import exit
game.init()

DisplayScreen = game.display.set_mode((850,650))
game.display.set_caption('The Snake Game') #game title

game.display.update()

gameOver = False

while not gameOver:
    for anyEvent in game.event.get():
        print(event)
        exit()

game.quit()
quit()

初始化后,您可以运行程序检查是否一切正常。如果您在说No pygame module时出错,请确保按照上述步骤在 PyCharm IDE 上安装 Pygame。现在,我们将学习如何使用颜色。

使用颜色

与计算机颜色一起工作的基本原理是颜色添加,这是一种将三种原色添加到一起以创建新原色的技术。三种原色是红色、绿色和蓝色,通常称为 RGB 值。每当 Pygame 需要将任何颜色添加到游戏中时,您都必须以三个整数的元组传递它,每个整数对应一个表示红色、绿色或蓝色的组件。

将整数值传递给元组的顺序很重要,在 integer 中做一些小的更改会产生不同的颜色。颜色的每个分量的值必须介于 0 到 255 之间,其中 255 表示具有绝对强度的颜色,0 表示完全没有强度的颜色。例如,(255,0,0)表示红色。下表显示了不同颜色的颜色代码:

| 颜色名称 | Hex code

#RRGGBB

| Decimal code

(R、G、B)

| | --- | --- | --- | | 黑色 | #000000 | (0,0,0) | | 白色 | #FFFFFF | (255,255,255) | | 红色 | #FF0000 | (255,0,0) | | 石灰 | #00FF00 | (0,255,0) | | 蓝色 | #0000FF | (0,0,255) | | 黄的 | #FFFF00 | (255,255,0) | | 青色/水 | #00FFFF | (0,255,255) | | 洋红/紫红色 | #FF00FF | (255,0,255) |

现在,让我们为我们的贪食蛇游戏项目添加一些颜色:

white = (255,255,255)
color_black = (0,0,0)
green = (0,255,0)
color_red = (255,0,0)

while not gameOver:
    #1 EVENT GET
    DisplayScreen.fill(white) #BACKGROUND WHITE
    game.display.update()

现在,在下一节中,我们将学习如何使用pygame模块创建游戏对象。

制作游戏物品

为了开始创建游戏对象,我们不会直接使用蛇精灵或图像。取而代之的是,我们将首先使用一个小矩形框,然后将其替换为蛇图像。这需要在大多数游戏中完成,因为我们必须在游戏开发之初测试多个方面,比如帧速率、碰撞、旋转等等。处理完所有这些之后,将图像添加到 pygame 项目就很容易了。因此,在本节中,我们将制作类似矩形框的游戏对象。我们将制作蛇头和蛇身,这将是一个小长方形的盒子。我们将首先制作一个盒子装蛇头,另一个盒子装食物,然后为其添加颜色:

while not gameOver:
    DisplayScreen.fill(white) #background of game 
    game.draw.rect(DisplayScreen, color_black, [450,300,10,10]) #1\. snake
    #two ways of defining rect objects
    DisplayScreen.fill(color_red, rect=[200,200,50,50]) #2\. food

现在我们将向game对象添加运动。在前面的章节中,我们已经讨论了很多,例如在使用向量处理方向运动时:

change_x = 300
change_y = 300
while not gameOver:
    for anyEvent in game.event.get():
        if anyEvent.type == game.QUIT:
            gameOver = True
        if anyEvent.type == game.KEYDOWN:
            if anyEvent.key == game.K_LEFT:
                change_x -= 10
            if anyEvent.key == game.K_RIGHT:
                change_x += 10

    DisplayScreen.fill(white)
    game.draw.rect(DisplayScreen, black, [change_x,change_y,10,10])
    game.display.update()

在前面的代码中,change_xchange_y表示蛇的初始位置。无论何时开始玩我们的游戏,蛇的默认位置都是(change_xchange_y。通过按左键或右键,我们可以改变它的位置。

当您此时运行游戏时,您可能会注意到游戏只移动一步,当您按下然后立即释放键盘键时,游戏最终会停止。这种异常可以通过处理多次移动来纠正。在这种情况下,我们将创建lead_x_change,这将根据主change_x变量进行更改。请记住,我们不是在处理上下关键事件;因此,不需要lead_y_change

lead_x_change = 0

while not gameOver:
    for anyEvent in game.event.get():
        if anyEent.type == game.QUIT:
            gameOver = True
        if anyEvent.type == game.KEYDOWN:
            if anyEvent.key == game.K_LEFT:
                lead_x_change = -10
            if anyEvent.key == game.K_RIGHT:
                lead_x_change = 10

    change_x += lead_x_change
    DisplayScreen.fill(white)
    game.draw.rect(DisplayScreen, black, [change_x,change_y,10,10])
    game.display.update()

因为,在新的代码行中,我们添加了额外的信息,lead_x_change,它将被称为x坐标的变化,并且每当用户点击左右键盘键时,蛇将自动移动。代码中突出显示的部分(change_x += lead_x_change负责让蛇持续移动,即使用户没有按任何键(贪食蛇游戏规则)。

现在,当你按下一个键时,你可能会看到游戏中另一个不寻常的行为。在我的例子中,我运行我的游戏,当我开始按左键时,蛇开始快速地、连续地从左向右移动。这是由于在帧速率上的宽大;我们现在必须明确指出游戏的帧速率,以便限制游戏的运行速度。我们将在下一节介绍这一点。

使用帧速率概念

这个话题对我们来说并不陌生;我已经尽了最大努力尽早介绍这个话题。在讨论时钟模块时,我们也了解了帧速率的概念。在本节中,我们将了解实际使用的帧速率的概念。到目前为止,我们已经制作了一个可以运行的游戏,但它的动作没有限制。它不断地朝着一个或另一个方向高速移动,我们当然不希望这样。我们真正想要的是让蛇在一定的帧速率内连续移动。我们将使用pygame.time.Clock创建一个对象,它将跟踪我们游戏中的时间。我们将使用tick功能更新时钟。每帧应调用一次 tick 方法。通过每帧调用Clock.tick(15)一次,游戏将不会以超过 15 FPS 的速度运行:

clock = game.time.Clock()
while not gameOver:
    #event handling
    #code from preceding topic
    clock.tick(30) #FPS

重要的是要了解 FPS 与游戏中精灵的速度不同。开发者制作游戏的方式可以在高端和低端设备上玩。你会发现游戏在一台功能较低的机器上有点迟钝和急促,但两台设备中的精灵或角色都会以平均速度移动。我们不否认,使用基于时间的运动游戏且帧速率较低的机器,其视觉体验会较不吸引人,但不会降低动作速度。

因此,要使游戏在视觉上吸引人,甚至在普及设备中兼容,通常最好提供 20 到 40 FPS 的帧速率。

在接下来的部分中,我们将处理剩余的方向运动。处理这些动作也没什么不同;它们可以通过矢量运动来处理。

处理定向运动

我们已经处理了x轴变化的运动。现在,让我们添加一些代码来处理y轴中的移动。要使蛇连续移动,我们必须制作lead_y_change,它表示持续添加到当前位置的方向量,即使用户没有按任何键盘键:

lead_y_change = 0
while not gameOver:
        if anyEvent.type == game.KEYDOWN:
            if anyEvent.key == game.K_LEFT:
                lead_x_change = -10
                lead_y_change = 0
            elif anyEvent.key == game.K_RIGHT:
                lead_x_change = 10
                lead_y_change = 0
            elif anyEvent.key == game.K_UP:
                lead_y_change = -10
                lead_x_change = 0
            elif anyEvent.key == game.K_DOWN:
                lead_y_change = 10
                lead_x_change = 0  

    change_x += lead_x_change
    change_y += lead_y_change

现在,我们已经处理了蛇的所有可能移动,让我们为贪食蛇游戏定义边界。change_xchange_y的值代表头部的当前位置。如果头部击中边界,游戏将终止:

while not gameOver:
    if change_x >= 800 or change_x < 0 or change_y >= 600 or change_y < 0:
            gameOver = True

现在,我们将学习编程的另一个概念,这将使我们的代码看起来更干净。到目前为止,我们一直在为许多组件使用数值,例如高度、宽度、FPS 等。但是如果你必须改变其中一个值,会发生什么呢?在搜索代码和再次调试代码时会有很多开销。现在,我们不用直接使用这些数值,而是可以创建常量变量,在其中存储值并在需要时检索它们。此过程称为去除硬编码。让我们为每个数值创建一个具有适当名称的变量。代码应该如下所示:

#variable initialization step
import pygame as game

game.init()

color_white = (255,255,255)
color_black = (0,0,0)
color_red = (255,0,0)

#display size
display_width = 800 
display_height = 600

DisplayScreen = game.display.set_mode((display_width,display_height))
game.display.set_caption('') #game title

gameOver = False

change_x = display_width/2
change_y = display_height/2

lead_x_change = 0
lead_y_change = 0

objectClock = game.time.Clock()

pixel_size = 10 #box size 
FPS = 30 #frame rate

在移除变量初始化步骤中的硬编码后,我们将进入主游戏循环。以下代码表示主游戏循环(在初始化步骤后添加):

#main loop
while not gameOver:
    for anyEvent in game.event.get():
        if anyEvent.type == game.QUIT:
            gameOver = True
        if anyEvent.type == game.KEYDOWN:
            if anyEvent.key == game.K_LEFT:
                lead_x_change = -pixel_size
                lead_y_change = 0
            elif anyEvent.key == game.K_RIGHT:
                lead_x_change = pixel_size
                lead_y_change = 0
            elif anyEvent.key == game.K_UP:
                lead_y_change = -pixel_size
                lead_x_change = 0
            elif anyEvent.key == game.K_DOWN:
                lead_y_change = pixel_size
                lead_x_change = 0

       #step 3: adding logic which will check if snake hit boundary or not

现在我们已经添加了在主循环中处理用户事件的方法,让我们折射表示逻辑的代码,例如当蛇到达游戏边界时,或者当蛇改变速度时会发生什么。处理用户事件后,应在主循环内添加以下代码:

 if change_x >= display_width or change_x < 0 or change_y >= display_height 
                or change_y < 0:
        gameOver = True

    change_x += lead_x_change
    change_y += lead_y_change
    DisplayScreen.fill(color_white)
    game.draw.rect(DisplayScreen, color_black, 
      [change_x,change_y,pixel_size,pixel_size])
    game.display.update()

    objectClock.tick(FPS)

前面的所有代码都已经简要描述过了,我们在前面的三段代码中实际做的是将变量折射为一些有意义的名称,以便删除硬编码;例如,添加变量名以显示宽度,将变量名添加到颜色代码,等等。

在下一节中,我们将在屏幕上添加一个食物角色,并创建一些逻辑来检查蛇是否吃了苹果。

为游戏添加食物

在屏幕上添加一个字符非常简单。首先,为角色创建一个位置,最后,blit将角色移动到该位置。在蛇类游戏中,食物必须呈现在任意位置。因此,我们将使用随机模块创建随机位置。我创建了一个新函数gameLoop(),它将使用上一节中的代码。我用了apple作为食物。稍后,我将添加一个苹果图像到它。以下代码行定义了游戏的主循环:

def MainLoopForGame():
    global arrow_key #to track which arrow key user pressed

    gameOver = False
    gameFinish = False
    #initial change_x and change_y represent center of screen
    #initial position for snake
    change_x = display_width/2
    change_y = display_height/2

    lead_x_change = 0
    lead_y_change = 0

在为游戏显示和角色定义了一些首字母之后,让我们添加一些逻辑来为贪食蛇游戏添加苹果(食物)(这应该在MainLoopForGame函数中):

 XpositionApple = round(random.randrange(0, display_width-pixel_size))
 YpositionApple = round(random.randrange(0, display_height-pixel_size))

这两行代码将为xy创建随机位置。确保导入了随机模块。

接下来,我们需要在MainLoopForGame函数中定义主游戏循环。添加到主循环中的代码将处理多种事情,例如处理用户事件、绘制游戏角色等等。让我们从以下代码中获取用户事件开始:

 while not gameOver:

        while gameFinish == True:
            DisplayScreen.fill(color_white)
            game.display.update()

            #game is object of pygame
            for anyEvent in game.event.get():
                if anyEvent.type == pygame.KEYDOWN:
                    if anyEvent.key == pygame.K_q:
                        gameOver = True
                        gameFinish = False
                    if anyEvent.key == pygame.K_c:
                        MainLoopForGame()

前面的代码将很容易掌握,正如我们在本章前面所做的那样。我们首先用白色填充游戏的背景屏幕,然后使用pygame模块的事件类获取事件。我们检查用户是否输入了q键,如果输入了,我们就退出游戏。类似地,现在我们有了来自用户的事件,让我们来处理使贪食蛇游戏的动作使用箭头键(如左键和右键)的事件。获取用户事件后,必须添加以下代码:

 #event to make movement for snake based on arrow keys
        for anyEvent in game.event.get():
            if anyEvent.type == game.QUIT:
                gameOver = True
            if anyEvent.type == game.KEYDOWN:
                if anyEvent.key == game.K_LEFT:
                    arrow_key = 'left'
                    lead_x_change = -pixel_size
                    lead_y_change = 0
                elif anyEvent.key == game.K_RIGHT:
                    arrow_key = 'right'
                    lead_x_change = pixel_size
                    lead_y_change = 0
                elif anyEvent.key == game.K_UP:
                    arrow_key = 'up'
                    lead_y_change = -pixel_size
                    lead_x_change = 0
                elif anyEvent.key == game.K_DOWN:
                    arrow_key = 'down'
                    lead_y_change = pixel_size
                    lead_x_change = 0

前面的代码已经编写好了,所以请确保遵循程序的顺序。参考中提供的代码资产 https://github.com/PacktPublishing/Learning-Python-by-building-games/tree/master/Chapter11 。让我们在主循环中添加剩余的代码,主循环处理呈现蛇食的逻辑。处理用户事件后应添加以下代码:

         if change_x >= display_width or change_x < 0 or change_y >= 
                        display_height or change_y < 0:
            gameFinish = True

        change_x += lead_x_change
        change_y += lead_y_change
        DisplayScreen.fill(color_white)
        Width_Apple = 30
        game.draw.rect(DisplayScreen, color_red, [XpositionApple, 
            YpositionApple, Width_Apple, Width_Apple])
        game.draw.rect(DisplayScreen, color_black, 
            [change_x,change_y,pixel_size, pixel_size])
        game.display.update()

        objectClock.tick(FPS)

    game.quit()
    quit()

MainLoopForGame()

在代码的突出显示部分,我们将绘制一个红色的矩形,并将其渲染到由高度和宽度为pixel_size= 10的随机模块定义的位置。

现在我们已经为蛇添加了食物,让我们做一个函数,使蛇的身体。到目前为止,我们只研究蛇头;现在,是时候做一个功能,将增加蛇的身体单位块。记住,只有蛇吃了食物时才会调用此函数:

def drawSnake(pixel_size, snakeArray):
    for eachSegment in snakeArray:
        game.draw.rect(DisplayScreen, color_green  [eachSegment[0],eachSegment[1],pixel_size, pixel_size])

在主游戏循环中,我们必须声明多个内容。首先,我们将声明snakeArray,其中包含蛇的尸体。比赛开始时,蛇的长度将是 1。只要蛇吃了食物,我们就会增加:

def MainLoopForGame():
 snakeArray = []
 snakeLength = 1

    while not gameOver:
        head_of_Snake = []
 #at the beginning, snake will have only head
 head_of_Snake.append(change_x)
 head_of_Snake.append(change_y)

        snakeArray.append(head_of_Snake)

        if len(snakeArray) > snakeLength:
            del snakeArray[0] #deleting overflow of elements

        for eachPart in snakeArray[:-1]:
            if eachPart == head_of_Snake:
                gameFinish = True #when snake collides with own body

        drawSnake(pixel_size, snakeArray)  
        game.display.update()

变量的名称告诉您需要知道的一切。我们以前做过很多次,也就是说,列出蛇头的清单,检查它是否与蛇的身体相撞。snake 方法调用采用pixel_size,这是 snake 维度,以及 snake 的列表,其中包含与蛇的身体相关的位置列表。根据这些列表,通过在snake函数中定义的语句,snake 将成为blit

接下来,我们需要定义让蛇吃食物的逻辑。这种逻辑已经被反复使用,在 pygame 中也没有什么不同。每当蛇的头部位置与食物位置相同时,我们会将蛇的长度增加 1,并在新的随机位置生成食物。更新显示后,确保在主游戏循环中添加以下代码:

#condition where snake rect is at the top of apple rect  
if change_x > XpositionApple and change_x < XpositionApple + Width_Apple or change_x + pixel_size > XpositionApple and change_x + pixel_size < XpositionApple + Width_Apple:

      if change_y > YpositionApple and change_y < YpositionApple + 
        Width_Apple:
                #generate apple to new position
                XpositionApple = round(random.randrange(0, 
                                 display_width-pixel_size))
                YpositionApple = round(random.randrange(0, 
                                 display_height-pixel_size))
                snakeLength += 1

      elif change_y + pixel_size > YpositionApple and change_y + pixel_size 
            < YpositionApple + Width_Apple:

                XpositionApple = round(random.randrange(0, display_width-
                                 pixel_size))
                YpositionApple = round(random.randrange(0, display_height-
                                 pixel_size))
                snakeLength += 1

既然我们能够添加一些逻辑来检查蛇是否吃了食物,并做出相应的反应,那么是时候给角色添加一个精灵或图像了。如前所述,我们将添加自己的蛇头,而不是使用单调的矩形。让我们开始创建一个。

添加蛇精灵

最后,我们可以开始使我们的游戏更具吸引力,我们将制作蛇头。我们不需要任何额外的知识来为游戏角色创建图像。您也可以从 internet 下载图像并使用它们。然而,在这里,我将向您展示如何为自己创建一个,以及如何在我们的贪食蛇游戏中使用它。

逐行执行以下步骤:

  1. 打开任何油漆应用程序,或在搜索选项卡中搜索油漆,然后打开应用程序。
  2. Ctrl+W可调整所选图片的大小并使其倾斜,或者只需使用上部菜单栏上的“调整大小”按钮即可。它将打开一个新的调整大小窗口。可以按百分比和像素调整大小。使用百分比调整大小并将纵横比保持为 20 x 20,即水平:20 和垂直:20。
  3. 之后,您将得到一个绘图屏幕。选择要制作的蛇头的颜色。在制作游戏时,我们创造了一个绿色的蛇身;因此,我也会选择绿色作为蛇头。我会用钢笔画一些像下图的东西。如果你愿意,你可以慢慢来,创造一个更好的。完成后,保存文件:

  1. 现在,您必须使图像的背景透明。您也可以使用几种在线工具,但我将使用 GIMP 软件,我们之前已经讨论过。你必须从它的官方网站下载。它是开源的,可以免费使用。访问该网站并下载 GIMP:https://www.gimp.org/downloads/

  2. 使用 GIMP 软件打开以前制作的蛇头。从最上面的菜单转到“层”选项卡,选择“透明度”,然后单击“添加 alpha 通道”。这将添加一个通道,可用于使图像背景透明。

  3. 单击菜单屏幕上的颜色选项卡。将出现一个下拉菜单。单击“颜色到 Alpha”以使背景透明。将该文件导出到存储 Python 文件的目录中。

现在我们有了蛇头的精灵,让我们使用它并使用 Python 文件中的blit命令渲染它。正如您所知,在使用任何图像之前,您必须导入它。由于我已将蛇头图像保存在保存 Python 文件的同一目录中,因此我可以使用pygame.image.load命令:

image = game.image.load('snakehead.png')

drawSnake方法的主体内,必须对图像进行 blit;大概是这样的:

DisplayScreen.blit(image, (snakeArray[-1][0], snakeArray[-1][1]))

现在,当你运行游戏时,你会观察到一件奇怪的事情。当我们按下任何一个箭头键时,头部不会相应地旋转。它将保持其默认位置。因此,为了使精灵旋转,根据方向运动,我们必须使用transform.rotate功能。观察 snake 方法,因为它可以在不旋转的情况下生成blit图像。现在,我们将添加两行代码,使精灵旋转:

def drawSnake(pixel_size, snakeArray):

 if arrow_key == "right":
 head_of_Snake = game.transform.rotate(image, 270) #making rotation of 270 

 if arrow_key== "left":
 head_of_Snake = game.transform.rotate(image, 90)

 if arrow_key== "up":
 head_of_Snake = image #default

 if arrow_key== "down":
 head_of_Snake = game.transform.rotate(image, 180)

 DisplayScreen.blit(head_of_Snake, (snakeArray[-1][0], snakeArray[-1][1]))
 for eachSegment in snakeArray[:-1]:
 game.draw.rect(DisplayScreen, color_green,[eachSegment[0],eachSegment[1], 
 pixel_size, pixel_size])

现在,让我从互联网上下载一个苹果的样本,以 PNG(透明背景)的形式,而不是用一个矩形框来表示苹果,还有blit

appleimg = game.image.load('apple.png') 
#add apple.png file in same directory of python file
while not gameOver:
    #code must be added before checking if user eats apple or not
    DisplayScreen.blit(appleimg, (XpositionApple, YpositionApple))

让我们运行游戏并观察输出。虽然蛇头看起来更大,但我们始终可以调整其大小:

在下一节中,我们将学习如何在游戏中添加菜单。菜单是一个屏幕,每当你打开一个游戏,它通常是一个欢迎屏幕。

在游戏中添加菜单

在任何游戏中添加介绍屏幕都需要我们具备使用pygame模块处理字体的知识。pygame 提供了一个功能,以便我们可以使用不同类型的字体,包括一个更改字体大小的功能。pygame.font模块用于为游戏添加字体。字体用于向游戏屏幕添加文本。由于介绍或欢迎屏幕需要播放器显示包含字体的屏幕,因此我们必须使用此模块。调用SysFont方法向屏幕添加字体。SysFont方法有两个参数:第一个是字体名称,第二个是字体大小。以下代码行初始化相同字体的三种不同大小:

font_small = game.font.SysFont("comicsansms", 25)
font_medium = game.font.SysFont("comicsansms", 50)
font_large = game.font.SysFont("comicsansms", 80)

我们将首先使用text_object功能,以便为小、中、大字体创建曲面。文本对象函数将使用文本创建矩形曲面。传递给此方法的文本将添加到长方体对象并从中返回,如下所示:

def objects_text(sample_text, sample_color, sample_size):
 if sample_size == "small":
 surface_for_text = font_small.render(sample_text, True, sample_color)
 elif sample_size == "medium":
 surface_for_text= font_medium.render(sample_text, True, sample_color)
 elif sample_size == "large":
 surface_for_text = font_large.render(sample_text, True, sample_color)

 return surface_for_text, surface_for_text.get_rect()

让我们在 Python 文件中创建一个新函数,它将使用上述字体向屏幕添加一条消息:

def display_ScreenMessage(message, font_color, yDisplace=0, font_size="small"):
 textSurface, textRectShape = objects_text(message, font_color, font_size)
 textRectShape.center = (display_width/ 2), (display_height/ 2) + yDisplace
 DisplaySurface.blit(textSurface, textRectShape)

发送给screen方法的消息将为blit文本创建一个矩形表面,该文本作为msg传递给它。默认字体大小较小,文本在矩形表面的中心对齐。现在,让我们为游戏创建一个游戏介绍方法:

def intro_for_game(): #function for adding game intro
 intro_screen = True

 while intro_screen:

 for eachEvent in game.event.get():
 if eachEvent.type == game.QUIT:
 game.quit()
 quit()

 if eachEvent.type == game.KEYDOWN:
 if eachEvent.key == game.K_c:
 intro_screen = False
 if eachEvent.key == game.K_q:
 game.quit()
 quit()

 DisplayScreen.fill(color_white)
 display_ScreenMessage("Welcome to Snake",
 color_green,
 -99,
 "large")

 display_ScreenMessage("Made by Python Programmers",
 color_black,
 50)

 display_ScreenMessage("Press C to play or Q to quit.",
 color_red,
 180)

 game.display.update()
 objectClock.tick(12)

此游戏intro方法在游戏loop方法调用之前调用。例如,请查看以下代码:

intro_for_game()
MainLoopForGame()

最后,欢迎菜单的输出应如下所示:

最后,我们的游戏已经准备好发布了。您可能会看到,我们的游戏是一个扩展名为.py的 Python 文件,无法在未安装 Python 的机器上执行。因此,在下一节中,我们将学习如何将 Python 文件转换为可执行文件,以便在 Windows 机器上全局分发游戏。

转换为可执行文件

如果你已经到了用 pygame 制作自己游戏的地步,很明显你想和你的朋友和家人分享。在互联网世界中,共享文件非常容易,但当另一端的用户没有预装 Python 时,问题就会出现。不可能每个人都只为了测试游戏而安装 Python。一个更好的办法是制作可执行文件,这些文件可以在许多这样的机器上执行。在本节中,我们将学习如何转换为.exe,接下来的章节将介绍其他版本(Linux 和 Mac)。

如果使用 Python 提供的模块,将 Python 文件转换为可执行文件会更容易。它们有两个-them—py2execx_Freeze。我们将在本节中使用第一个。

使用 py2exe

要将 Python 文件转换为可执行文件,我们可以使用另一个 Python 模块,名为py2exepy2exe模块未预装在 pygame 中,它不是标准库,但可以使用以下命令下载:

pip install py2exe 
OR
py -3.7 -m pip install py2exe

下载py2exe模块后,导航到包含 Python 文件的文件夹。在该位置打开命令提示符或终端,然后运行代码。。它会将您的 Python 文件打包成一个.exe文件,或者打包成可执行文件。以下命令将搜索脚本使用的所有文件并将其复制到名为dist的文件夹中。dist内将有一个snake.exe文件;该文件将是 Python 代码的输出模拟,可以在机器上不安装 Python 的情况下执行。例如,您的朋友可能没有在他们的机器上安装 Python,但他或她仍然可以运行此文件。要将游戏分发到任何其他 Windows 机器,您只需发送dist文件夹或snake.exe文件的内容即可。只需运行以下命令:

python snake.py py2exe #conversion command

这将创建名为snake并扩展为.exe的游戏。您可以跨 Windows 平台分发这些文件,并从中获得响应。祝贺你终于成功了。现在,让我们学习使用 pygame 进行游戏测试。

游戏测试和可能的修改

有时,您的机器可能存在内存不足的情况。如果内存不足,即使 pygame 尽了最大努力,也试图将更多图像加载到游戏中,此过程将中止。pygame.image.load必须有一些记忆才能正确执行任务。在内存不足的情况下,您可以预测您肯定会触发某种异常。即使有足够的内存,如果您试图加载一个不在硬盘中的图像,或者说,您在写入文件名时输入了一个错误,您也可能会遇到异常。因此,最好事先处理它们,这样我们就不会有事后调试的麻烦。

其次,让我们检查一下当我们向set_mode方法提供屏幕的异常尺寸时会发生什么。回想一下,set_mode是我们用来创建Surface对象的方法。比如说,我们忘了给set_mode加两个值,只加了一个。在这种情况下,我们也会触发错误:

screen = pygame.display.set_mode((640))
TypeError: 2 argument expected

比方说,不要忘记为高度和宽度添加适当的尺寸,如果我们添加高度值 0 会发生什么?对于 PyCharm IDE,这个问题不会产生任何异常。相反,该程序将无限运行,导致您的机器故障。但是,这些程序通常会抛出异常pygame.error: cannot set 0 sized display。既然您知道了pygame可能出错的领域,您就可以捕获这些异常并相应地处理它们:

try:
    display = pygame.display.set_mode((640,0))
except pygame.error:
    print("Not possible to create display")
    exit()

因此,最好明智地选择显示屏,以消除任何不必要的异常。但是,更可能的是,如果您尝试加载不在硬盘驱动器中的映像,则会出现异常的pygame错误。因此,处理异常是一种很好的做法,这样游戏中的精灵或图像就能被正确加载。

总结

在本章中,我们查看了pygame模块,并发现了在游戏开发中使用它的原因。从下一章开始,我们将介绍的大多数游戏都将以pygame模块为基础。因此,在继续之前,请确保您自己使用 pygame 制作了一个简单的游戏。

我们开始学习如何使用 pygame 对象制作游戏。我们学到了很多东西,包括处理涉及鼠标和键盘等输入设备的用户键事件;我们制作了一个精灵动画;我们学习了颜色特性;我们使用矢量运动处理不同的对角线和方向运动。我们使用简单的绘画应用程序创建了自己的精灵,并使用 GIMP 应用程序添加了 alpha 属性。我们试图通过加入一个交互式游戏屏幕,即菜单屏幕,使游戏更具交互性。最后,我们学习了如何使用py2exe模块将 Python 文件转换为可执行文件。

本章的主要目的是让您熟悉精灵的使用,以便您可以构建 2D 游戏。您还学习了如何处理用户事件和不同的移动,包括对角线移动。您还学习了如何使用外部软件创建自定义精灵和图像,以及在游戏中使用它们的方法。不仅如此,您还熟悉了颜色和rect对象的概念,并学会了如何使用它们,通过部署菜单和记分屏幕,让游戏更具用户互动性。

在下一章中,我们将使用在本章中学习的概念来制作我们自己的 flappy bird 克隆。除了本章所学内容之外,我们还将学习游戏动画、角色动画、碰撞原理、随机对象生成、添加分数以及更多概念。