所以你想制作一个游戏引擎 (2023)

Hacker News Top 工具

摘要

一篇博客文章,讨论了构建自定义游戏引擎的原因和考量,由Unity的定价争议引发。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/07/28 18:28

# 那么,你想造一个游戏引擎 来源:https://lisyarus.github.io/blog/posts/so-you-want-to-make-a-game-engine.html 那么,你想造一个游戏引擎 2023年9月15日 Unity 近期的争议(https://blog.unity.com/news/plan-pricing-and-packaging-updates) 引发了关于游戏引擎的热烈讨论。有人说每个人都应该立即换用新引擎,而另一些人则回应说换引擎可能需要数月甚至数年。有人说最好的办法是自己造一个游戏引擎,而另一些人则指出这可不是件简单的事。而且,你知道吗,这里每个人都是对的:没有唯一的答案,一切都取决于具体情况。但也许自己写引擎并不像听起来那么难?为什么有人要这么做?*造一个游戏引擎* 到底意味着什么? 我实际上从未使用过除自己业余引擎(https://bitbucket.org/lisyarus/psemek/src/master)之外的任何游戏引擎,这个引擎我慢慢扩展了三年(目前已有大约 10 万行代码(https://en.wikipedia.org/wiki/Source_lines_of_code)),并且成功用它制作了十几个 Game Jam 游戏(https://lisyarus.itch.io/)以及一款已发行的商业游戏(https://store.steampowered.com/app/2403100/Costa_Verde_Transport_Department)。不过,我见过别人使用其他引擎;也见过别人抱怨其他引擎;还见过别人谈论他们自己的引擎;我研究过其他(开源)引擎的源代码。所以,我认为我在这件事上有些话可以说,不过,不可否认,我的经验可能有些偏向或非常规。 ## 目录 ## 第一部分:难题 ## 为什么**要**造你自己的游戏引擎? 先从房间里的大象说起。这个话题已经讨论过无数次,但再多讨论一次也无妨。以下是你会自己造游戏引擎的几个原因: - 你将**摆脱**那些可疑的公司决策的负担,正如近期事件所示,这很重要 - 自制游戏引擎是**免费**的(当然,忽略了开发成本——没有什么是真正免费的) - 你可以**针对自己的需求**(或特定项目的需求)优化引擎,包括性能、迭代时间(大家都讨厌等着着色器重新编译或光照图烘焙)、引擎架构(对于拥有数百万飞行物体的游戏,你可能会使用 ECS 而非传统的场景图)以及许多其他方面 - 你**完全掌控**内部一切的工作方式以及使用的技术 - 你可以使用你最喜欢的**编程语言**(尽管地球上的每种语言可能都有一些游戏引擎) - 你可以拥有**小得多的**分发体积(我的引擎不到 2MB,其中几乎全都是 `SDL2.dll` 占用的空间) - 你可以将引擎**开源**,并看到它被其他人使用(但要注意,维护 FOSS 项目基本上就是地狱) - 如果你的引擎质量足够,你甚至可以**出售**它(但要注意,推广你的作品基本上就是地狱) - 你不会因为某些愚蠢的开发者没有实现你需要的功能而**感到沮丧** - 互联网上的陌生人会认为你**酷毙了**,因为你正在制作自己的引擎 - 这**非常有趣**,我保证 - 在这个过程中你会**学到超多的东西**,这能让你更好地理解其他引擎,并因此帮助你在未来制作游戏——即使你最终把自己引擎扔进垃圾桶也是如此 对我个人来说,最后两点最重要。首先,学习很有趣,如果你不这么认为,你最好开始这么想:如果你想做游戏,你*必须*学习很多东西,所以最好开始享受这个过程! 其次,我就是喜欢编程。我喜欢实现算法,喜欢设计库和系统,喜欢挖掘底层的东西。去掉这些,游戏开发对我来说就变得痛苦了。 我还想详细说说“不会感到沮丧”这一点,因为对我来说这算是一个令人大开眼界的发现。我看到人们总是在抱怨引擎:这个坏了,那个搞坏了这个,这个新东西不支持那个旧东西,这个功能十年前就有人要求了却还没实现。当然,事情之所以如此总有原因;如果你曾在足够大的公司工作过,你大概能理解。然而,责备别人总比责备自己容易。*那些*愚蠢的开发者没修好这个!如果你需要它,为什么*你*不去修呢?嗯,这又不是*你的*引擎,对吧?——是*他们的*。但*你的*沮丧可是你自己的。 但现在不同了,现在你在造自己的引擎。突然之间,没有沮丧了:你确切地知道为什么引擎缺少你现在需要的某个功能。——*你*没有实现它,就这么简单。为什么你没实现?嗯,显然你忙着做其他事情了!你一个人或在一个小团队里制作整个游戏引擎,引擎缺少一些功能是可以理解的。甚至可以说是意料之中的。当真正需要的时候,你会实现这个功能的。当然你会。只是,可能不是现在。还有其他事情。 我的意思是,责怪自己的作品比责怪别人的作品更难。 ## 为什么**不**造你自己的游戏引擎? 无论如何,这项工程当然有一些缺点: - 它**难**且**耗时**。意思是,当然这取决于你的用例,但别指望在上完三个月 C++ 课程后一年内写一个 Unreal 克隆。 - 你**永远无法胜过**那些巨大的工业级引擎(比如老牌的 Unreal)。当然,除非你是一个足够大且有钱的工作室,那样的话这篇文章可能根本不适合你。 - 你的引擎会**缺少功能**。需要多年的辛勤工作才能达到大型引擎功能集和易用性的一小部分。 - 你可能会发现自己深陷引擎开发之中,**而不是在制作实际游戏**,这很容易导致**倦怠**。 - 互联网上的陌生人会认为你是个白痴(https://www.gamespot.com/articles/unity-ceo-says-youre-a-fing-idiot-if-you-dont-think-about-monetization-during-creative-process-apologizes/1100-6505516),因为你在造自己的引擎。 注意,如果你规划得当,自己造引擎的成本可以分散到多个项目中。 那么,你该不该造一个游戏引擎呢?你自己决定!如果你想尽快做出一个游戏,那就选一个现成的引擎并开心地用它。如果你想作为游戏开发者成长,保障你的未来项目,并且你有足够的时间,那就造一个游戏引擎!在其他情况下,你得自己决定,而不是依赖互联网上的陌生人 :) ## 什么**是**游戏引擎? 好问题!SDL2(https://www.libsdl.org/)是游戏引擎吗?我认为不是,它实际上是一个*平台抽象库*。OpenGL 是游戏引擎吗?当然不是,它是一个*底层图形 API*。stb_image(https://github.com/nothings/stb/blob/master/stb_image.h)是游戏引擎吗?肯定不是,它是一个*图像加载库*。 现在想象一个将 SDL2、OpenGL 和 stb_image 组合起来让你在屏幕上绘制移动精灵的小项目:这是一个游戏引擎吗?听起来确实像! 那么,*什么才是*游戏引擎,它与其他库或 API 的区别是什么?是图形、动画和用户输入的组合吗?嗯,我的一位好朋友成功地在终端中制作了基于文本的游戏,这显然没有任何图形或动画,但它们却是完全有效的游戏。所以,任何具有用户输入的东西都是游戏引擎?例如,计算器是游戏吗?呃,嗯,大概不是,除非你无聊得要死。 我提出以下定义:**游戏引擎**是*任何旨在帮助你制作游戏的工具、库和其他东西的集合*。如果你觉得这个定义模糊到几乎毫无用处,那它就是。是的,但*这正是关键所在*。 你看,如果你正在制作一款复古风格的 2D 像素平台游戏,你真的需要复杂的材质图、连续几何细节层次、全局光照、高级 UI、集成了反向运动学的全方位 3D 物理、多人支持等等等等吗?你不需要。你唯一需要的是处理用户输入、在屏幕上显示图像,以及可能一些音频。根据你的经验和固执程度,这大约需要 2 到 30 天的工作。无论如何,这只会是你工作的 1%;另外 99% 是*真正制作那个该死的游戏*。 *顺便看看才华横溢的 @Eniko(https://mastodon.gamedev.place/@[email protected])对此事的看法(https://cohost.org/eniko/post/2852222-what-does-make-your)。* 但如果你想制作下一款 AAA 大作,拥有顶级画质、物理和诸如此类的东西呢?嗯,那么你就需要所有这些众多复杂的功能。你还需要*至少 5 年的空闲时间*全职投入你的引擎来实现所有这些东西。 我这里想说的是: - 游戏引擎可以(而且在大多数情况下应该)**很小**。小的游戏引擎仍然是游戏引擎。 - 游戏引擎可以**千差万别**,从只有 100 行代码的非常专门的文件,到 100 Gb 的工业级巨兽。没有引擎适合所有需求,每个游戏都有自己独特的功能和限制。 ## 人们对游戏引擎有**什么期望**? 好吧,我对游戏引擎的定义主要是为了说服你引擎可以很小。不过,现实一点说:大多数*足够大*的游戏引擎都有一些*期望*的功能,除非它们设计得极为简约或专门化(这*完全正常*!)。例如: - 能够**编程**游戏逻辑(废话) - 创建一个**窗口**来显示内容 - 运行处理各种事件的**游戏循环** - 处理**用户输入**,如鼠标、键盘、摇杆或触摸条 - 显示某种**图形**,无论是 2D 精灵还是 3D 内容 - 产生**音频**,如背景音乐和音效 - 管理**资源**,如图片、模型、音频、预制件或完整的游戏关卡 - 模拟**物理**,无论是 2D 还是 3D,基于位置、基于冲量或基于力,刚体或软体 - 支持**脚本**以便快速原型设计或轻松 mod - 支持**网络**用于多人游戏、自动下载游戏更新或补丁,或允许用户生成内容下载 - 实现**AI**用于游戏中的敌人或 NPC - 实现**UI**用于游戏的辉煌界面 - 施加一种**架构**,以统一的方式连接所有这些部分 - 提供**工具**来帮助使用引擎 - 负责用该引擎制作的游戏的**分发**,最好能在多个平台上,最好只需一键点击 请注意,你的特定引擎可能只包含这些功能的一小部分,但仍然可以被称为游戏引擎。并非每个游戏都需要所有功能。 现在我们来逐一讨论这些功能。 ## 第二部分:游戏引擎功能 ## 编程 严格来说,传统编程并不*必须*用于游戏逻辑。可视化脚本(https://en.wikipedia.org/wiki/Visual_programming_language)已经存在了几十年,可以在不写任何代码的情况下实现复杂逻辑。 *Unreal 中的可视化脚本 一些人曾与我争论,认为可视化脚本也是一种编程。而且,嗯,当然是了!同样,画黑白方格(https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life#Undecidability)以及在大数字上做简单算术(https://en.wikipedia.org/wiki/G%C3%B6del_numbering)也是一种编程。但这其实不是重点。* 然而,在你的引擎中支持可视化脚本需要编写专门的工具,这显著增加了引擎开发的复杂性。而且,在我看来,代码仍然是实现任何类型逻辑最通用、最灵活的方式。 你也可以只支持用非引擎编写语言进行脚本(例如,一个 C++ 引擎用 Lua 脚本而不提供直接的 C++ 接口),但如果你已经在编写引擎,可能只需提供一个原生 API(即引擎本身的语言)会更简单。 不过,*写引擎*仍然需要编程,这里没有捷径。那么,哪种语言最适合写引擎?老实说,这不太重要,选你喜欢的语言就行。我使用 C++,因为我用了它十五年,这是我最了解的语言,也是我热爱且感到舒适的语言。如果你对 C++ 过敏,选 Rust、C#、Python、Haskell、Java 或随便什么。真的,没关系。 在某些时候你可能需要性能关键的代码,大多数语言都有实现这一点的方法。更接近底层的语言(如 C++ 或 Rust)通常在性能方面更透明,但同样,你可以用任何语言做到。 根据我(可能不太受欢迎)的观点,呈现引擎的最佳方式是作为一个*编程库*:你将其作为依赖项包含在游戏项目中,并随意使用它。这可能意味着你需要一些*构建系统*支持;不过,根据引擎的大小和复杂性,你可能只需一些 makefile(https://makefiletutorial.com/)就能搞定。 假设你以 C++ 库的形式做了一个存根(空)引擎,在头文件中提供引擎的 API,并有一些代码实现该 API。你将其打包为一个 CMake 项目,或者提供一些 makefile,或者干脆提供全部源代码,期望引擎的用户只需将所有源文件添加到游戏项目中。接下来呢? ## 窗口 大多数游戏需要一个*窗口*来运行。即使是网页游戏也不例外:窗口可能是整个网页,或者游戏运行在其上的某个画布元素等。可能唯一的例外是基于终端的游戏,它使用终端进行所有输入和输出(例如 C++ 中的 `std::cin/cout`)。 创建窗口通常通过平台特定的方式完成,比如使用 WinAPI(https://learn.microsoft.com/en-us/windows/win32/learnwin32/creating-a-window)中的 `CreateWindowEx`(https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexa)函数。这些平台特定的接口通常相当丑陋且对新手不友好,而且你必须为每个计划支持的平台分别重写窗口创建代码。不过,在大多数情况下,你还需要从窗口获得一些额外的东西,比如图形 API 上下文,这也是通过丑陋的平台特定接口创建的。 值得庆幸的是,有些库可以为你完成所有这些工作。在 C++ 中,你可以使用 SDL2(https://www.libsdl.org/)、glfw(https://www.glfw.org/)、sfml(https://www.sfml-dev.org/)或成千上万个类似库中的一个。只需将它们添加到你的项目中,调用相应的 `createWindow()` 函数就完成了。其他任何语言也有类似的库甚至内置工具来做到这一点。 如何将窗口暴露在你的引擎 API 中完全取决于你以及你的编程品味。也许你有一个 `engine::init()` 函数,它创建一个窗口并将其存储在一个全局变量中。也许你有一个显式的 `engine::createWindow()` 函数,返回代表窗口的对象并允许对其进一步操作。也许你有一个基类 `engine::window`,引擎的用户必须继承它。可能性是无限的。

相似文章

godotengine/godot

GitHub Trending (daily)

Godot Engine 是一个免费、开源、跨平台的游戏引擎,用于创建2D和3D游戏,采用社区驱动的开发模式,基于MIT许可。

从像素到状态:重新思考作为游戏引擎的交互式世界模型

Hugging Face Daily Papers

本文通过考察四个关键维度——动作控制、状态动态、状态-观测持久性和实时生成——重新思考了交互式世界模型作为游戏引擎的角色,并介绍了为《黑神话:悟空》构建的可扩展数据引擎,包含超过90小时的游戏数据,以推进状态感知的游戏世界建模。