欢迎光临S11竞猜有限公司官网!
S11竞猜10年专注高精度恒温恒湿设备定制生产厂家
全国咨询热线:0826-69313480
联系我们
S11竞猜有限公司
全国免费服务热线:0826-69313480
手机:12487442559
邮箱:admin@ytqczz.com
地址 :湖南省衡阳市南岳区奥标大楼92号
联系人:陈先生
您的位置: 主页 > S11竞猜新闻 > 公司新闻 >
公司新闻

十年职业法式员教你如何编写C++游戏引擎:S11竞猜

时间:2021-10-24 05:58:01 来源:LOL全球总决赛下注 点击:

本文摘要:本人从事在线教育c++十年事情履历现在经心整理了一套从小白到项目实践开发种种学习资料如果你想学想加入我们请关注我在私信回复“编程”就可以领取学习资料!!!最近我在用 C++ 写游戏引擎,再用这个引擎做了一个移动端小游戏跳一跳(Hop Out)。下面是截自我的 iPhone6 的一个小片段。跳一跳是我想玩的游戏类型:3D卡通外观的复古街机游戏。 目的是改变每个填充块的颜色,就像Q * Bert一样。

S11竞猜

本人从事在线教育c++十年事情履历现在经心整理了一套从小白到项目实践开发种种学习资料如果你想学想加入我们请关注我在私信回复“编程”就可以领取学习资料!!!最近我在用 C++ 写游戏引擎,再用这个引擎做了一个移动端小游戏跳一跳(Hop Out)。下面是截自我的 iPhone6 的一个小片段。跳一跳是我想玩的游戏类型:3D卡通外观的复古街机游戏。

目的是改变每个填充块的颜色,就像Q * Bert一样。Hop Out仍在开发中,但引擎的功效已经很完善了,所以我想在这里分享一些关于引擎开发的技巧。

你为什么想要写一个游戏引擎?可能有许多原因:你是个修理工,喜欢重新开始建设系统,直到系统完成。关于游戏开发你想相识更多。我在游戏行业事情了14年,现在我仍然在不停的琢磨。我甚至不确定我是否可以重新开始编写一个引擎,因为它与大型事情室的编程事情的日常职责大不相同。

我想知道谜底。你喜欢控制。

对完全根据你想要的方式组织代码,知道一切都在那里,感应满足。你可以从AGI(1984),id Tech 1(1993),Build(1995)等经典游戏引擎以及Unity和Unreal等行业巨头那里获得灵感。你相信我们这个游戏工业应该试着去揭开引擎生长的序幕。

我们并没有掌握制作游戏的艺术。还离得很远!我们对这个历程的研究越多,革新的时机就越大。2017年的游戏平台 – 手机,游戏机和电脑 – 很是强大,而且在许多方面都很是相似。游戏引擎的开发并不是像已往一样,在懦弱和怪异的硬件上挣扎。

在我看来,更多是关于自己制造出来的庞大性的斗争。缔造一个怪物很容易!这就是为什么本文建议围绕着保持事情可控的原因。我把它分成三部门:使用迭代方法在统一事物前要三思请注意,序列化是一个很大的课题这个建议适用于任何类型的游戏引擎。

我不会告诉你如何编写着色器,八叉树是什么,或者如何添加物体。这些事儿,都是我假设你已经知道而且应该知道 – 这很大水平上取决于你想要制作的游戏类型。相反,我居心选择了一些似乎没有被广泛认可或提及的看法 – 这些是我在试图揭开一个主题神秘面纱时最感兴趣的一些看法。

使用迭代方法我的第一条建议是使一些工具(任何工具),快速运行起来,然后迭代。如果可能的话,从一个示例应用法式开始,初始化设备并在屏幕上绘制一些工具。

就我而言,我下载了SDL,打开了Xcode-iOS / Test / TestiPhoneOS.xcodeproj,然后在我的iPhone上运行了testgles2示例。瞧!我使用OpenGL ES 2.0,生成了一个可爱的旋转立方体。下一步,是下载一个其他人制作的马里奥3D 模型。

我写了一个快速和粗拙的OBJ文件加载器 – 文件花样并不太庞大 – 而且修改了例程,来出现Mario,而不是一个立方体。我还集成了SDL_Image来资助加载纹理。然后我实现了一个双摇杆控制器用来操控马里奥(我原来想要建立的是一个双摇杆设计游戏,并不是马里奥。

)接下来,我想探索骨骼动画,所以我打开了Blender,做了一个触手模型,而且用一个前后摆动的双骨架来利用它。此时,我放弃了OBJ文件花样,编写了一个Python剧本来从Blender导出自界说的JSON文件。这些JSON文件形貌了皮肤网格,骨架和动画数据。

在C ++ JSON库的资助下将这些文件加载到游戏中。一旦这个完成,我回到了Blender,并做了更详细的角色设计。

(这是我缔造的第一个被利用的3D人,我为他感应自满。)在接下来的几个月里,我接纳了以下几个步骤:开始将向量和矩阵函数剖析成我自己的3D数学库。

用CMake项目替换.xcodeproj。在Windows和iOS上运行引擎,因为我喜欢在Visual Studio下事情。

开始将代码移动到单独的“引擎”和“游戏”库中。随着时间的推移,我把它们分成更细粒度的库。

写了一个单独的应用法式将我的JSON文件转换为游戏可以直接加载的二进制数据。最终从iOS版本中删除所有SDL库。(Windows版本仍然使用SDL。

)重点是:在开始编程之前,我没有对引擎架构举行设计。这是一个经由深思熟虑的选择。

相反,我只是写了实现下一个特性的最简朴的代码,然后我会检察代码,看看会泛起什么自然生成的架构。我说的“引擎架构”是指组成游戏引擎的模块集,这些模块之间的依赖关系,以及用于与每个模块交互的 API。这是一个迭代的方法,因为它关注于较小的可交付结果。

它在编写游戏引擎时效果很是好,因为在每个步骤中,你都有一个正在运行的法式。如果在将代码合成到新模块中时泛起问题,可以随时将做的更改与以前事情的代码举行比力。显然,我假设你在使用某种源代码治理工具。

你可能会认为这种方法浪费了许多时间,因为总是在编写糟糕的代码,之后需要清理。可是大部门的清理操作都是将代码从一个.cpp文件移动到另一个,将函数声明提取到.h文件中,或者直接举行简朴的修改。决议事情应该去哪是难点,可是这在已经有代码的时候会更容易决议。

我认为用相反的方法:试图设计出一个能够提前完成所有需求的架构,会浪费更多的时间。我最喜欢的两篇关于系统过分设计风险的文章是 Tomasz Dąbrowski 的《泛化的恶性循环》和 Joel Spolsky 的《不要让架构太空人吓到你》。我并不是说在用代码处置惩罚问题之前,不应该在纸上举行设计。

我也不是说你不应该事先决议你想要的功效。好比,我从一开始就知道我想让我的引擎在后台线程中加载所有资源。

我只是没有实验设计或实现该功效,直到我的引擎首先加载一些资源。迭代的方法给了我一个比我以前盯着一张白纸冥思苦想更优雅的架构。

我的引擎的iOS版本现在是 100% 原始代码,包罗自界说数学库,容器模板,反射/序列化系统,渲染框架,物理模块和音频混淆器。我可以编写每一个模块,可是你可能没有须要自己写所有这些工具。你可能会发现适合自己引擎的许多优秀的开源代码库。GLM、Bullet Physics 和 STB 头文件只是一些有趣的例子。

在整合事物太多之前要三思作为法式员,我们只管制止代码重复,喜欢代码遵循统一的气势派头。不外,我认为不要让这些本能凌驾于每一个决议之上。

偶然要抵制一下 DRY 原则举个例子,我的引擎包罗了几个“智能指针”模板类,与 std :: shared_ptr 类似。每一个指针作为一个原始指针的包装,有助于防止内存泄漏。<> 是用于具有单个所有者的动态分配的工具。

Reference<> 使用引用计数来允许一个工具拥有多个所有者。audio :: AppOwned <> 被音频混音器以外的代码挪用,允许游戏系统拥有音频混音器使用的工具,例如当前播放的语音。

audio :: AudioHandle <> 使用音频混音器内部的引用计数系统。这样可能看起来像其中一些类复制了其它的功效,违反 DRY(不要重复自己)的原则。

事实上,在开发早期,我尽可能地重用现有的Reference <>类。可是,我发现音频工具的生命周期是由特殊规则来治理的:如果一个音频语音已经完成了一个样本的播放,而且游戏没有指向该语音的指针,那么该语音会被立刻到删除排队等候。如果游戏持有指针,则不应删除这个语音工具。

如果游戏持有一个指针,但指针的所有者在语音竣事之前被销毁,这段语音应该被取消,而不是增加Reference <>的庞大性,我决议引入单独的模板类,这样更为实用。95% 的时间都在重用现有的代码。可是,如果你开始感应麻木,或者发现自己增加了一件简朴的事情的庞大性,那就问自己,代码库中的工具是否应该是两件事。可以使用差别的挪用规则我不喜欢Java的一件事是,它强迫你在一个类中界说每个函数。

在我看来,这是无稽之谈。这可能会使你的代码看起来越发一致,可是它也勉励过分工程,而且不适合我前面形貌的迭代方法。在我的 C++ 引擎中,一些函数属于类,有些则不属于类。

例如,游戏中的每个敌人都是一个类,可能就像你预料的那样,大部门敌人的行为都是在这个类内部实现的。另一方面,在我的引擎中投射的球体是通过挪用 sphereCast() 函数来执行的,这是物理命名空间中的一个函数。

sphereCast() 不属于任何类 – 它只是物理模块的一部门。我构建了一个系统来治理模块之间的依赖关系,这使得我的代码组织得很好。

将这个函数包装在一个任意的类中不会以任何有意义的方式改善代码的组织。然后是动态调理,这是一种多态的形式。

我们经常需要为一个工具挪用一个函数,而不知道该工具简直切类型。C ++法式员的第一本能是用虚函数界说抽象基类,然后在派生类中重写这些函数。这是有效的,但这只是一种技术。

另有其他动态调理技术,不会引入分外的代码,或带来其他利益:C ++ 11引入了std :: function,这是存储回调函数的一个轻便方法。也可以编写自己的std :: function版本,这样在调试中不会那么痛苦。许多回调函数可以用一对指针来实现:一个函数指针和一个类型不确定的参数。它只需要在回调函数中举行明确的转换。

你在纯C语言库中经常看到。有时候,底层类型实际上是在编译时已知的,你可以绑定这个函数挪用而不用分外的运行开销。

S11竞猜

Turf是我在游戏引擎中使用的一个库,它很是依赖这种技术。例如看到turf:: Mutex,这只是针对特定平台类的界说。有时,最直接的方法是自己构建和维护一个原始函数指针表。

我在我的音频混音器和序列化系统中使用了这种方法。Python解释器也大量使用这种技术,如下所述。你甚至可以将函数指针存储在散列表中,使用函数名称作为关键字。

我使用这种技术来调理输入事件,如多点触控事件。这是记载游戏输入并用重放系统回放的计谋的一部门。

动态调理是一个很大的课题。我只是想讲明,有许多方法来实现它。你编写的可扩展底层代码越多(这在游戏引擎中很常见),越会发现替代方法越多。

如果你不习惯这种编程,C语言编写的Python解释器是一个很好的学习资源。它实现了一个强大的工具模型:每个PyObject都指向一个PyTypeObject,每个PyTypeObject都包罗一个用于动态分配的函数指针表。

如果你想直接跳转到其中的话,界说新类型的文档是一个很好的起点。注意序列化是一个大问题序列化是将运行时工具转换为字节序列的操作。换句话说,就是生存和加载数据。对于许多游戏引擎来说,游戏内容以种种可编辑的花样建立,例如.png,.json,.blend或专有花样,然后最终转换为特定于平台的可以快速加载到引擎的游戏花样。

流水线中的最后一个应用通常被称为“炊具”。炊具可能被集成到另一个工具,甚至漫衍在几台机械上。通常,炊具和一些工具是与游戏引擎自己一起开发和维护的。

在建设这样的流水线时,每个阶段的文件花样的选择取决于你。你可以界说自己的一些文件花样,这些花样可能会随着添加引擎功效而变化。徐徐地可能会发现有须要保持某些法式与以前生存的文件兼容。

不管什么花样,你最终都需要用C++来序列化它。用C ++实现序列化有无数种方法。一个相当显着的方式是将加载和生存函数添加到要序列化的C ++类。可以通过在文件头中存储版本号来实现向后兼容,然后将这个数字通报给每个加载函数。

这是可行的,只管这样代码可能维护起来比力繁琐。12345678910void load(InStream& in, u32 fileVersion) {// 加载预期的成员变量in >> m_position;in >> m_direction;// 仅当正在加载的文件版本是2或更大时才加载新的变量if (fileVersion >= 2) {in >> m_velocity;}}通过反射(特别是通过建立形貌C ++类型结构的运行时数据),可以编写更灵活,不容易堕落的序列化代码。想要快速相识反射如何举行序列化,请看一下开源项目Blender是如何实现的。

从源代码构建Blender时,有许多步骤。首先,编译并运行一个名为makesdna的自界说实用法式。该实用法式剖析Blender源代码树中的一组C语言头文件,然后以SDNA的自界说花样输出所有C界说类型的汇总。

这个SDNA数据作为反射数据,链接到Blender自己,并生存在Blender写入的每个.blend文件中。从这一刻开始,每当Blender加载一个.blend文件,就会将.blend文件的SDNA与链接到当前版本的SDNA举行比力,并使用通用序列化代码来处置惩罚差异。这个计谋使Blender具有令人印象深刻的向前和向后兼容性。

你仍然可以在最新版本的Blender中加载1.0版本的文件,也可以在旧版本中加载新的.blend文件。像Blender一样,许多游戏引擎及其相关工具都市生成并使用自己的反射数据。有许多方法可以做到这一点:可以像Blender一样剖析自己的C / C ++源代码来提取类型信息。你可以建立一个单独的数据形貌语言,并编写一个工具来从该语言生成C ++类型界说和反射数据。

可以使用预处置惩罚器宏和C ++模板在运行时生成反射数据。一旦你有反射数据可用,有无数的方法来编写一个通用的序列化器。显然,我省略了许多细节。

在这篇文章中,我只想讲明有许多差别的方法来序列化数据,其中一些很是庞大。法式员不会像其他引擎系统那样讨论序列化,只管大多数其他系统依赖于它。例如,在GDC 2017给出的96个法式设计讲座中,我数了一下,共有31次关于图形,11次关于在线,10次关于工具,4次关于AI,3关于物理模块,2关于音频的 – 但只有一个直接涉及到序列化。

至少,试着想一想你的需求会有多庞大。如果你正在制作一个像Flappy Bird这样的小游戏,只有少数资源.,那么你可能不需要想太多的序列化。

你可以直接从PNG加载纹理,这样很利益理。如果你需要一个向后兼容的紧凑的二进制花样,但不想自己开发,可以看看第三方库,好比Cereal或者Boost.Serialization。我不认为Google协议缓冲区是序列化游戏资产的理想选择,可是值得研究。编写一个游戏引擎,纵然是一个小游戏引擎,也是一个很大的任务。

关于这个我可以说的另有许多,可是对于这个长度的帖子来说,这真的是我认为最有用的建议:迭代地事情,抵制统一代码的激动,而且知道序列化是一个大问题,你需要选择一个合适的计谋。凭据我的履历,如果忽视这些事情,每一件事情都可能成为一个绊脚石。

本人从事在线教育c++十年事情履历现在经心整理了一套从小白到项目实践开发种种学习资料如果你想学想加入我们请关注我在私信回复“编程”就可以领取学习资料!!!。


本文关键词:S11竞猜,十年,职业,法式,员教,你,如何,编写,C++,游戏

本文来源:S11竞猜-www.ytqczz.com

在线客服
联系方式

热线电话

12487442559

上班时间

周一到周五

公司电话

0826-69313480

二维码
线