这是这个博客的第二篇博文。简单说一下之前的事情。
这个游戏引擎其实已经经过了三次迭代,就拿比较有代表性的内存管理来说,第一代基本没有。
因为当时我对于项目的设计还比较薄弱,认为 ECS 不过就是管理 E、C 和 S。事实证明事情并没有那么简单。
当然了,引擎还是可以跑的,后来放弃是因为设计实在是太弱了,没有技术话语权。例如整个引擎所有堆内存分配的来源必须都是内存管理器,等等。
到了第二次,引擎的架构就具体的多,不过内存管理器还是没能设计好。
因为我太过于偏向于 TLSF 这种分配速度较快的分配方式,硬编码太多东西,导致无法挽回,或者重构。还是项目管理的问题。
不过在第二次迭代中,我也做了资源管理器,渲染器(具体的实现是用 Diligent 和 Skia 做了统合的 ECS 组件,以及渲染路径,并且通过 DIM 编译时标签,其实就是模板元,来路由不同的组件。)
在第二次,有一个非常重要的节点,我用这个游戏引擎的雏形参加了 GameJam。成功做出来两款游戏。
这个对我来说很重要,而且也让我一直确定了之前怀疑的东西:就是性能好像并不是最最重要的。
第三版也就是现在的设计。还是拿内存管理器来说,这一次的设计要更好一些。我采取了三层路由架构,因为内存管理还是要强调性能,所以都是编译时路由。
分层设计为:
Consumer // 消费者标签,根据不同的 consumer 特性,路由到不同的 adaptor
Adaptor // 内存适配器,举个例子,PMR 适配器,可增长内存适配器(Vector)
Provider // 底层内存提供者,比如 Mimalloc,GPU(特殊提供者)
这样就好得多。
我对现在的游戏引擎很满意,而且也很清楚设计过程中,我成长了多少。
然后,接下来,我想简单说一下现在在做的事情。
目前在做的
这其实是很遗憾的一件事情,因为我还是没能做出让自己满意的渲染模块,不过得益于 ECS 的解耦,我认为先做出可用的原型,再去替代是完全可行的,因为整条执行路径为:
ECS
Render 基础设施
而一切和渲染有关的操作,都只能操作 ECS 层的内容,这就导致引擎的资产管理,运行时,都只是系统在操作组件而已,所以我修改组件和系统即可,这也就为重构减轻了很多压力,也就让我们的 MVP 开发路线得以实现。
当然了,整个引擎还是增量开发的。模块化的思想帮助了我很多。
我现在正在尝试用 GitHub Submodule,不过这东西有一个致命缺陷:没法处理树形依赖。没法去重。 下一步我还是打算回退到 CMake,或者自己搭建一个依赖管理体系。这件事的难度应该十分简单。这是题外话。
说回渲染项目,目前做出来的就是很普通的渲染项目,所以我打算把讨论的重点放在我理想中的渲染项目——这就不得不提到 HTML/CSS/JS。
我认为这是我见过最棒的设计模式。
首先,它本身就有 ECS 的潜质。
我们经常开玩笑,说 HTML 其实可以全用 div,这其实是个事实,我想说真的是这样,不过我的出发点不同:我认为 HTML 标签本身就是一个实体,而 CSS 样式其实是组件,而 JS 就是系统。
这么说当然很武断,因为其实在 HTML 里,它们互相还是有一些耦合的地方,不能一刀切,我只是表明我的意思。
那么,我觉得一个好的,解耦的,符合 ECS 思想的渲染系统,应该模仿这种设计,系统去操控实体和组件,而组件,比如有“位置”,“混合方式”,“骨骼”等等,或者我用更加自然的语言表述:在某个位置,放着某个模型。
而这就是一个实体,系统就是要去处理它。
不过目前,因为我精力有限,还是做了传统的设计。一定程度上遵从了 ECS 的思想,同时增强了技术话语权,所有的 GPU 资源都要从 MemoryCenter 取得,以及其它的约束条件。
尝试的新的思想
我在尝试统合的设计思想。
举两个特别通俗的例子,2D 和 3D 的“人物”统一为 Character 组件,根据维度路由,2D 就是精灵图,而 3D 为模型(实际不存在 Character 组件,只是个例子),而在物理系统中也差不多。
“流体”的概念在 2D 和 3D 都有不同表现。
ECS 很好的减轻了这项设计的难度。
AI
我在 GitHub 中提到了这一点。
我正在借鉴 AI CLI 的一些思想来完善引擎,我认为 MCP 和 Agent 尤其比较有意思。
所以我将会从两个层面做一些改动:
第一,我会为引擎开发模块化的 Tool(这是我以前一直在做的,只不过,一直不是很完善,Tool 当时是作为处理某一个小问题的工具产生的,而不是处理某一类问题。举个例子,之前我只是为了“读取预编译的引擎二进制资产文件”而做了一个 FileReader,而现在,我想做一个完善的文件管理工具,包含加密,读取,流式,多线程等。我认为这才是正确的做法。难点在于,控制功能和其本身轻量的平衡,避免其作为一个 Tool,甚至膨胀到 Center 级别。)
第二点,也就是最重要的一点。
我将开发完整的 MCP 工具链。
作为一个独立的,新的游戏引擎,没有庞大的代码存量,也没有什么名气。绝对不能指望 AI 的头部玩家们(A, O, G)主动来为我的引擎训练。
而 AI 现在对于随机应变,根据即时的信息解决问题的能力也很弱,只是在预训练的基础上才很强。所以,AI 对于这个游戏引擎必然是没有多少支持的。
然而,开发者的需求正在改变。
首先,我们可以关注一下国内的前阵子的外卖巨头之间的战争,以及方便面口味的不断增加,调味的不断灵活,以及最近游戏社区中不断传出的“独立游戏开发”的声音,其实答案比较明确,“单干”正在成为趋势。
那么“单干”,也就意味着,开发者更注重的是,更低的成本,以及尽可能高的质量,当然,还有随之而来的,由独立游戏本身决定的,“能否可以给我更多的时间来想象,来发挥创造力,而不是去写代码做调试收集美术音乐资产”。
因为独立游戏就是强在创造,或者某一点特别突出,比如独特的美术风格带来的独特体验,等等。
那么现在的矛盾也很简单了:开发者需要的是高效率,而引擎初创,会有学习成本。要解决学习成本,最好就是使用 AI。而使用 AI,AI 却并未针对这个引擎进行训练。
矛盾已经明确,答案其实也很简单。
在做数学题的时候,我们通常会被老师告知,要去做一个桥梁,将没碰到过的问题,向碰到过的问题转换,将难做的题目,向“通用的方法”的方向去化归。
好了,那么,如何“化归”?
MCP。
大模型再怎么多样,也离不开对逻辑的训练。比如你要问它,如果我要吃一袋饼干要怎么做,任何一个模型都有足够的能力,告诉你,先拿到一袋饼干,然后再打开,然后吃。这就是“通法”——逻辑。
我要做的就是“桥梁”——MCP。
我直接编写代码,开发很多的 MCP,比如,创建组件,创建系统,等等,请注意,这是 ECS 独有的优势,如果是 OOP 引擎,那么还是会有复杂的逻辑,继承等等,我没法给出通用的 MCP,所以,这也是 ECS 的优势——而且我比较确定,从未有人发掘出它的这种优势。
那么,大模型只需要调用 MCP,就可以将复杂的开发问题,转换为简单的逻辑问题:
“为了完成两个方块碰撞的游戏,我需要先创建一个组件,然后创建一个碰撞系统,一旦二者相撞,就会发出弹窗”
从而调用 MCP,根本无需编码。
这是我接下来主要要做的事情。