图书前言

本书贡献者

关于作者

John Horton是英国的一位程序及游戏发烧友。

谨以此书献给Ray与Barry两位兄弟,感谢他们的指引、示范与支持。

——John Horton

关于审阅者

Yoan Rock虽然只有26岁,但已拥有4年游戏行业的从业经验。Yoan具有C++软件工程方面的背景,对C++游戏产业有独到的见解,尤其是在Unreal Engine的使用以及通过设计图(blueprint)创建沉浸式体验方面。

Yoan在Limbic Studio工作期间主要参与Park Beyond的研发工作,这是一款AAA级游戏,玩家可以在其中创建并管理自己的主题公园。他专精于玩法开发与缺陷修复,以及提升团队成员间的交流体验。

Yoan随后与Chillchat工作室就Primorden达成合作,后者是基于Unreal Engine 5和Gameplay Ability System的一个多玩家项目。在此项目中,Yoan在实现游戏机制、怪兽能力及AI行为树等方面做出了重要贡献。

Yoan在Game Atelier的一个内部项目中领导UI开发,这充分体现出他在使用Unreal Engine 5.3、Common UI与UMG等工具创建沉浸式玩家体验的丰富经验。

目前,Yoan在Blacksheep参与开发一个令人激动的大型项目。他勇于创新,时刻把握着产业的趋势,并探索利用Unreal Engine 5.3开发个人项目的方法。

前    言

你是否一直梦想着去创建自己的游戏?在第3版《C++游戏编程入门》的帮助下,你便能够实现这个梦想!这本初学者教程经过了修订,展示最新的VS 2022、SFML以及现代C++20编程技术,其中我们将构建Timber!!!、Pong、Zombie Arena与Run这4个复杂度递增的游戏,从而引导你踏上妙趣横生的游戏编程入门之旅。

本书始于对编程基础的讨论,你将学习C++的若干重点主题,如OOP(Object-Oriented Programming,面向对象编程)与C++指针等,并逐渐熟悉STL(Standard Template Library,标准模板库)的用法。本书随后将通过构建Pong游戏来介绍碰撞检测技巧等游戏物理学知识。在构建游戏的过程中,你同样会学到顶点数组、方向性(空间化)音效、OpenGL可编程着色器、对象创建等技术,这些都是非常有用的游戏编程概念。同时,你还将能够深入挖掘游戏机制,并实现输入处理、角色升级等过程与简单的敌方AI。最后,你将探索一些游戏设计模式来强化游戏编程技巧。

读罢本书,你将能掌握自主创建炫酷游戏所需要的全部知识。

本书读者对象

如果你没有C++编程经验,并因此需要一本针对初学者的启蒙教程,或者希望学习如何构建游戏,或者仅仅将游戏编程视为一种学习C++的手段,那么本书就非常适合你。

无论是有意发布一款游戏(例如,在Steam上),还是仅仅希望用自己偷偷努力的成果惊艳友人,你都能从本书中获益。

本书各章的主要内容

第1章,“欢迎阅读《C++游戏编程入门》(第3版)”:本章概述我们将要踏上的、使用OpenGL驱动的SFML库和C++为PC编写精彩游戏的旅程。本书已全面升级至第3版,在深度和广度上进行了显著提升与扩展。新版内容丰富,涵盖了从C++基础(如变量、循环)到面向对象编程、标准模板库、SFML特性,乃至C++的新功能等知识。完成这段学习之旅后,你不仅将掌握4款可玩性高的游戏的制作技巧,还将奠定深厚而坚实的C++基础。

第2章,“变量、运算符与决策——让精灵动起来”:本章将完成许多绘制任务,为背景图上的云朵以及前景中的蜜蜂分别赋予随机移动的能力(包括高度随机与速度随机),而这需要用到更多C++知识。我们将学习如何利用变量来存储数据,也会学习如何通过运算符来操作这些变量,以及如何根据变量的值来制定决策,有选择地执行若干分支路径中的一种。所有这些知识与本章所介绍的SFML Sprite与Texture这两个类的信息相结合,便能让我们实现云朵和蜜蜂的动画效果。

第3章,“C++字符串、SFML时间、玩家输入与HUD”:本章将用一半的篇幅来介绍文本操作以及在屏幕上显示文本的方法,另一半篇幅则针对计时功能,学习通过使用更形象的时间棒来向玩家提醒剩余时间并制造紧迫感。

第4章,“循环、数组、switch、枚举与函数——实现游戏机制”:与本书其他各章相比,本章所含的C++信息应该是最多的。这些信息被组织为一些基础的概念,并进而大大拓展我们对这门语言的理解。此外,本章同样将详述函数、游戏循环以及循环结构等之前被有意略去的一些模糊内容。

第5章,“碰撞、音效及终止条件:让游戏能玩起来”:这是我们首个游戏项目的最后一章,结束后便将得到自己的第一个完整游戏Timber!!!。而真正玩起来之后,别忘了阅读本章最后一节,该节提供了对游戏的一些改进意见。具体而言,本章涵盖以下内容:添加剩余精灵(即Sprite对象)、处理玩家输入、让木料飞起来、处理角色之死、增加音效与其他功能、改进Timber!!!。

第6章,“面向对象编程——开启Pong游戏”:本章将简要介绍一些有关OOP(即面向对象编程)的理论知识,这些理论是我们开始应用OOP的基石。OOP有助于我们将代码组织成人类可识别的结构,并有效控制代码的复杂性。我们不会让理论束之高阁,而是会立即将其应用于实践,通过开发一个Pong游戏来展示OOP的作用。我们将深入探索如何在C++中创建可用作对象的新类型,并通过编写我们的第一个类来实现这一过程。本章首先将介绍一个简化的Pong游戏场景,以学习类的基础知识,随后将运用所学的知识,从头开始编写一个真正的Pong游戏,将理论转化为实践。

第7章,“AABB碰撞检测与物理学——完成Pong游戏”:本章将编写第二个类。在这个过程中,我们体会到,虽然球明显异于球拍,但可以使用相同的技术将球的外形以及功能封装在Ball类中,这正是球与Bat类之间的关系。随后我们为碰撞检测与记分功能编程,从而完成Pong游戏的收尾工作。虽然这两种功能听起来有些复杂,但按照之前的趋势,使用SFML将大大简化其实现过程。

第8章,“SFML View类 ——开启僵尸射手游戏”:这个项目会令我们更频繁地践行OOP思想,初步体会其强大效果。我们也将探索SFML中的一个多用途的View类,该类允许我们将游戏按照不同的视角分层。在Zombie Arena项目中,我们会把HUD与主游戏各划为一层,而之所以这样,是因为玩家每清空一批僵尸后都会拓展游戏世界,最终会让游戏世界远大于屏幕,玩家因而需要滚动摄像头。借助于View类,我们能让HUD文本不与背景一同滚动。

第9章,“C++引用、精灵表单与顶点数组”:我们曾在第4章介绍过作用域的概念,如果变量定义在函数中或某内层区块中,则此变量的作用域仅限于该函数或区块内部(换句话说,仅在该函数或区块内可见/使用)。在目前所学的C++知识范围内,这可能带来问题,例如,我们可能无法处理main函数所需要的复杂对象,毕竟强行实现意味着全部代码均应位于main函数中。

本章将探索的C++引用允许在变量或对象的作用域之外对其进行操作。此外,引用同样有助于避免在函数间直接传递大对象,由于这种传递每次均需要创建变量或对象的副本,因此非常缓慢。

掌握了引用这项新技能之后,我们会学习SFML中的VertexArray类,该类允许使用图像文件内的多个图片单元来高效地构建大型图像。本章结束时,通过引用机制以及一个VertexArray对象便可构建可缩放、可滚动的随机背景图片。

第10章,“指针、标准模板库与纹理管理初探”:本章将介绍许多知识,并完成游戏中的大量内容。首先,我们会学习指针这一基本C++主题,这是保存内存地址的一种变量,且通常会保存另一变量的内存地址。虽然这听起来与引用类似,但后文将说明指针的功能更加强大,并会实际使用指针来处理规模持续扩张的僵尸群。

我们还将学习标准模板库,其中整合了许多类,能够用来轻松实现一些常见的数据管理技术。

第11章,“编写TextureHolder类并构建僵尸群”:至此,我们所理解的STL基础知识足以管理游戏所需的一切纹理资源,毕竟不必为上千僵尸而反复给GPU加载图片。

随后,我们将进一步钻研OOP思想并使用静态函数。静态函数虽然也属于某个类,但在调用时不需要借助于该类的具体实例。同时,我们还会学习如何设计类,以令其仅存在一个实例,这种技巧非常适合确保某实例在程序不同位置上使用相同的内部数据。

第12章,“碰撞检测、拾取包与子弹”:现在,我们已经实现了游戏的主要视觉内容,让玩家能够控制角色在竞技场中跑动,其中充斥着正在追逐他的僵尸。但现在的问题是,其中没有任何交互,玩家能够直接穿越僵尸而毫发无损。为此,我们需要在僵尸与玩家之间进行碰撞检测。

另一方面,如果僵尸能够伤害并最终杀死玩家,那么为保持公平性,我们需要为玩家手中的枪械提供子弹,并保证子弹能够击中并杀死僵尸。

此外,由于本章需要实现子弹、僵尸与玩家三者之间的碰撞检测,因此同样需要将医疗包与弹药包抽象为类。

以上都是本章的任务,具体而言,包括子弹射击、增加准星、隐藏鼠标指针、创建拾取包以及碰撞检测。

第13章,“借助分层视图实现HUD”:本章将揭示SFML View类的实际效果。我们会增加一组SFML Text对象,并参照Timber!!!与Pong这两个项目来操作它们。此外,本章还将引入第二个View实例来绘制HUD,这样,无论背景、玩家、僵尸或其他游戏对象如何行止,视角如何移动,HUD均将作为所有游戏动作的最顶层而出现。

第14章,“音效、文件I/O操作与完成游戏”:行文至此,本游戏项目即将完成。当前这个短章将演示如何使用C++标准库来简单地操作硬盘上的文件,也会介绍为游戏添加音效的方法。当然,我们知道如何添加音效,但本章将详细介绍play函数在代码中的具体位置。随后在为游戏完成一些辅助性功能后,本游戏便大功告成。具体而言,本章将介绍通过文件输入与文件输出操作来加载并保存高分纪录、添加音效、允许玩家升级、创建下一波僵尸等操作。

第15章,“Run!”:欢迎来到最终的项目。Run是一个无限跑酷游戏,其中玩家脚下的平台会从后向前逐一消失,玩家需要持续向前跑动以避免被追上。我们将学习更多游戏编程技术,而这需要我们进一步学习更多C++知识才能逐一实现。相比于之前三个项目,也许这个游戏最显著的特点在于其大大强化了面向对象理念。该游戏将使用的类远多于之前的游戏,只是其中大多数类并不复杂,代码也不长。此外,我们将把游戏内部所有对象的功能与外观封装为类,从而在改动对象时维持游戏循环本体不发生变化。我们很快便能意识到这种设计的强大之处:只需设计出描述所需游戏实体的行为与外观的独立组件(类),便能创建出迥然不同的游戏。这也意味着你在自主设计游戏时完全可以采用这种代码结构。即便如此,这也不是这种设计思路的全部优势,还有更多的细节有待探索。

第16章,“声音、游戏逻辑、对象间通信与玩家”:本章将快速实现本游戏的声音效果。之前已经做过类似的工作,所以这不算难,而且通过仅仅几行代码便能为项目添加音乐背景。本项目后面将添加方向性(空间化)音效。

本章负责把与声音有关的全部代码封装为SoundEngine类。我们在实现声音效果后便转而实现玩家,而且只需要分别扩展Update类与Graphics类得到两个新的类结构,便能实现整个玩家角色的功能。我们之后为完成本游戏的全部工作,也基本上是通过扩展既有类来创建新的游戏对象。此外,我们还将介绍通过指针来进行对象间通信的一种简单方法。

第17章,“图像、摄像机与动作”:我们有必要深入讨论本项目的图像机制。本章将编写负责绘制工作的摄像机类,所以同样适合讨论图像。打开graphics文件夹便可发现,其中只有一个图片文件。此外,我们目前完全没有调用过window.draw函数。这里我们将讨论为什么需要尽量避免调用它,并转而实现代替我们完成这项工作的Camera结构。本章结束时,我们将能运行游戏并亲身体会摄像机的效果,其中包括主视图、雷达视图与计时器文本。

第18章,“编写平台、玩家动画与控制机制”:本章将编写平台、玩家角色动画及其控制操作。在我看来,我们早已完成了其中的困难部分,所以本章大部分工作的投入产出比很高。而且,本章的趣味性很强,将介绍平台如何支撑玩家角色并令其能够跑动,还将演示如何通过循环播放动画帧来实现玩家角色平滑跑动的效果。具体而言,本章将完成编写平台结构、为玩家角色结构添加新功能、实现Animator类、实现动画效果、添加玩家角色平滑跑动的动画等工作。

第19章,“创建菜单与实现下雨效果”:本章将实现两大重要功能:其一是能够为玩家提供开始、暂停、重新开始与退出游戏等功能选项的游戏菜单界面,其二是营造出简单的下雨效果。可能你会认为下雨效果没有必要,甚至可能不适合Run游戏,但这个技巧简单又有趣,很适合学习并掌握。这里更值得期待的是我们如何通过再次编写Graphics与Update的派生类,并将其组合为GameObject实例来完成这两个目标,同时需要保证这两个派生类能与游戏中的其他实体协同工作。

第20章,“火球与空间化”:本章将添加所有的音效与HUD。虽然前几个项目也实现了音效,但这一次稍有不同,因为我们将探索声音空间化(spatialization)这个复杂的概念,并学习SFML让它变得简单而优雅的方法。

第21章,“视差背景与着色器”:本章是Run游戏编写过程的最后一章,在添加所有功能后,它便能完整地玩起来了。在整款游戏的收尾过程中,我们将初步学习OpenGL、着色器与图形库着色语言(Graphics Library Shading Language,GLSL),并实现可滚动的背景与着色器,以最终完成CameraGraphics类,还将使用他人的代码在游戏中使用着色器。最后,我们会运行整个游戏。

如何最大化本书的阅读效果

阅读本书没有任何前置知识要求,不需要知晓任何编程知识,因为本书将带领你从零学起,并最终得到4个可玩的游戏。此外,拥有几种电脑游戏的体验并有学下去的决心对阅读本书会有所帮助。

下载示例代码文件与彩图

读者可通过扫描本书封底的二维码下载本书的源代码。我们还提供了一个PDF文件,其中含有本书所用的全部屏幕截图与图表素材,读者可以通过网址https://packt.link/gbp/9781835081747下载,也可以通过扫描本书封底的二维码下载。