Skip to content
云风 edited this page Jan 24, 2024 · 1 revision

特效

特效可以大大增强游戏的画面感染力。特效最重要的组成部分是粒子系统。由于粒子数量非常巨大,处于性能原因,通常游戏引擎不会把每一片粒子都当成一个 Scene 场景对象对待。几乎在所有的游戏引擎中,粒子系统都是单独实现部分。游戏引擎管理的是粒子发射器,它是一个场景对象;当发射器发射出粒子后,发射物就按一定规则自生自灭。

粒子系统非常重要,性能敏感,Ant 引擎在开发时曾经实现过一套例子系统。随着游戏开发,我们发现,实现一个功能齐备、性能高效的粒子系统并不难,但制作一个好用的编辑器有巨大的工作量。而美术创作人员制作出来的效果好坏,受编辑器的影响更大。

特效也不仅仅只有粒子系统。

effekseer

经过一番调研,我们决定集成 Effekseer 。虽然有一些看起来更好的中间件,但多半是商业化的。作为一个采用 MIT 开源许可证的引擎项目,同样采用 MIT 许可证的 Effekseer 是目前的最佳选择。

我们为 effekseer 开发了 bgfx 的 binding ,让它可以用于我们的引擎。除了自定义材质这个特性外,其它特性全部支持了。在我们的游戏开发过程中,完全满足了游戏的需要。

多线程

在对我们的游戏做性能测试时,我们发现过多的特效会占用大量 cpu ,这会降低游戏的帧率。幸运的是,对游戏引擎来说,管理的是粒子发射器的空间状态,粒子本身是引擎其它部分不必关心的。只要我们合理的安排 pipeline ,尽早计算出粒子发射器的位置,接下来的计算都可以和其它业务并行。

而 bgfx 提供了良好的多线程支持,可以在不同线程平行提交图形指令。所以,我们非常轻松的把特效模块整个移入了独立的 ltask 服务中。改为独立线程处理特效后,提高了游戏帧率。但需要小心,这只是降低了每个渲染帧的延迟,对手机设备的电能消耗并没有减少。所以在实际使用时,还是需要避免使用太多的粒子发射器。

独立服务还可以帮助我们将特效和其它部分隔离开,所以,一旦帧率不够或是能耗太大,可以通过这个服务直接关闭特效模块。一般都不会对游戏逻辑造成影响,只是画面效果受损。

使用特效并不关心特效服务的存在。导入 ECS ant.efk 这个特性即可。

world:create_entity {
	policy = {
		"ant.scene|scene_object",
		"ant.efk|efk",
	},
	data = {
		scene = {},
		efk = {
			path = "/pkg/ant.test.features/assets/efk/miner_efk/miner_dust.efk",
		},
		visible_state = "main_queue",
	}
}

创建一个带有 "ant.efk|efk" policy 的 entity 就可以得到一个特效对象,它同时必须是一个 Scene 场景对象,即需要 ant.scene|scene_object 这个 prolicy 。特效对象必须引用 .efk 文件。.efk 文件是用 Effekseer 编辑器制作出来的。

一般我们不会直接在代码中使用 create_entity 创建特效对象,而是使用引擎编辑器制作 Prefab 在编辑器中,可以给预制件添加特效,并为特效加入时间线(参见 Animation

如果不想通过时间线触发特效,使用 :

local iefk = ecs.require "ant.efk|efk"

这个 iefk 中的接口也能独立控制特效的播放过程。

挂钩上的特效

在介绍 挂钩 时我们提到过:如果多个挂钩应用了同一组 entity ,那么,这组 entity 会被绘制多次。引用相同 entity 的 hitch 画面表现应该是完全一致的,仅仅位置不同。这同样也适用于特效。

例如,在我们开发的游戏中,场景中有大量冒着烟的相同建筑。建筑是相同的模型,而烟雾使用特效制作。我们就使用了 hitch 来优化这种场景。引擎可以通过 hitch 了解到哪些对象在渲染过程能够合并。

注:在实现时,因为 effekseer 并不支持将一个发射器渲染多次,所以 Ant 引擎实际上创建了多个特效示例。由于一些实现上的限制,它们并不会保证严格的一致。好在大多数情况下,我们也不需要严格一致性。我们还可以通过参数,将特效对象配置为即使被多个 hitch 引用,它们也是分别独立的粒子发射器。

Clone this wiki locally