Zh/Particle System Overview

粒子系统粒子系统是使用大量小物体的组合来模拟一些伪3D视觉效果的系统，例如云、射线束。从求生之路起，起源引擎还加入了全屏的后处理特效。
粒子系统通常包含了不止一类的嵌套子系统，每种都是由控制粒子系统行为的各类组件组成。因此，粒子系统得以生成极其简洁或复杂的效果。
粒子是使用制作的。你必须把粒子编辑器创建的所有 PCF 文件添加到 <游戏目录>\particles\ 文件夹中的 清单文件，游戏引擎才能识别到它们。
== 示例 ==
== 剖析 ==
; 内存字段
: 粒子系统的设计是用较少的内存占用换取更多运算资源。为了达到这一目的，粒子系统中的每个粒子只使用了少量字段来描述，例如位置、持续时间、颜色、旋转等。基于这些属性字段，粒子系统的组件得以得以控制粒子系统，计算每个粒子在每一帧的状态。
; 
: 挂点是粒子系统的基础外部输入机制，每个粒子系统最多有64个挂点。挂点包含一个位置、朝向、和一个它们允许引用的实体对象。默认情况下，挂点0表示的是粒子系统的原点和朝向。
: 如果把挂点和一个实体对象关联起来，它就可以被绑定到该对象，或该对象的附着点上。同理，粒子系统中每个额外的挂点都能设置它的位置、朝向和绑定的实体。这样一来，军团要塞中 Medic 的治疗射线束就能够从治疗枪射向被治疗的队友。
:这也让单个的粒子系统能够在多个点或对象间生效，例如制作出蔓延的火焰的效果。
:同时，粒子系统的各种元素都可以获取挂点信息，如此一来，粒子系统可以同时受到多个动态元素的影响。挂点在默认情况下表示空间中的一些位置点，同时，它们还能存储其他附加信息。因此，我们可以通过外部代码或实体来向粒子系统输入一些通用信息，并通过内部的处理转换成系统自身的属性信息。
; 基础属性
: 在基础状态下，一个粒子系统不包含任何组件，只有一些基础属性。这些属性是所有系统的通用属性，包括影响整个系统的属性、每个粒子中不变且无法适配其他接口的属性，这些组成了一个系统。
; 组件
: 除了基础属性之外，粒子系统还可以包含各类可集成的组件。通常来说，每一类组件的添加数量没有上限，即使同时集成了多个相同组件也没有问题，只要能够满足你的需求。这些组件可以在每个粒子的属性字段上起作用，以便设置或修改它们。
:;
:: 渲染器用于指定粒子的绘制方式。包括精灵贴图(Sprite，一种始终面朝玩家相机的贴图/实体类型)、绳索、条带等等。如果你想的话，每个粒子都能以不同的方式被多次绘制。
:;
:: 发射器用于指定在什么时间段内生成多少数量的粒子。如上所述，一个粒子系统可以包含多个发射器，分别指定不同的发射类型，来构成粒子系统的整体图形，
:; 
:: 初始化器用于设置每个已生成粒子的初始状态，初始化它们的属性字段。例如，一个粒子的初始空间位置、颜色、尺寸和透明度。初始化器只会在每个粒子生成时设置它们的初始状态，之后将无法对粒子产生影响。
:; 
:: 一旦粒子生成时的初始状态由初始化器设置完毕，操作器便会接管粒子，并在每一帧根据设定的函数更新粒子的状态，直到粒子消失。
:; 
:: 作用力是操作器的一类变体，可以影响粒子的运动状态。
:;
:: 约束器用于约束粒子的运动，例如使用碰撞约束或是基于控制点的距离约束。
:; 
:: 简而言之，子系统就是嵌套到你的粒子系统中的其他粒子系统。部分数据，例如挂点信息，可以向下传递到子系统中。并且子系统可以相互嵌套很多层。
== 创建我们的第一个粒子系统 ==
通常来说，我们最后还是会直接使用现成的粒子系统，复制几份，简单地修改一下来满足作图的需要。然而，创建一个新的粒子系统并不难，只要点击“创建”按钮，再设置它的名称就完事了。
这样一来，你就创建了一个空空如也的粒子系统。没有包含任何组件的话，它什么事情也做不了。以下将要介绍的是一些必须添加的基础组件。下面我们开工吧。
# 添加一个 Animated Sprite Renderer(动态精灵贴图渲染器) 以实现渲染功能。 
# 我们需要确定粒子创建的位置，因此找到初始化器，并添加一个 Position Within Sphere Initializer(球体初始化器)。将它们的初始最小、最大速度都设置为64。如果没有设置初始位置，粒子将无法正确地生成。
# 添加一个Continuous Emitter(连续发射器)。现在你应该会得到一个白色的球体。实际上，现在你正在发射巨量的拥有初始纹理的粒子。
# 我们会注意到，即使你赋予了它们一个初始速度，这些粒子并没有移动。同时巨量的粒子还在堆叠中。这是因为没有操作器来控制它们。因此，即便我们设置了它们的初始位置和速度，在之后的状态更新中它还是什么也做不了，除了渲染这些粒子。因此我们需要两个操作器：
## 一个是 Movement Basic(基础运动) 操作器。它允许粒子运动。
## 另一个是 Lifespan Decay(结束生命周期) 操作器。它能够在粒子的持续时间结束时移除它们。
# 现在你的粒子应该会运动并在一秒后消失。粒子的持续时间默认条件下为一秒。
# 粒子的持续时间是在生成时初始化的，因此如果要修改它们的持续时间，我们需要添加 Lifetime Random(随机持续时间) 始化器。几乎所有的初始化器都允许设置随机范围的数值。因此如果我们希望每个粒子出现2到4秒时间，分别设置该初始化器的最小、最大值为2和4就行了。
# 接下来修改粒子的纹理，让它不再是白色球体。找到基础属性，点击material(材质)打开材质浏览器，并选择一个合适的粒子材质。
# 最后，添加一个 Fade Out Random 操作器。它的作用是让粒子在其持续时间的某个时间范围内渐变消失，默认是粒子最后25%的持续时间中。需要注意的是，由于每个粒子的持续时间在2到4秒不等，而粒子渐隐的时间是按比例的后25%，因此其时间也是不固定的。（也就是最后的0.5到1秒时间不等）
== 性能 ==
; 最大粒子数量
: 默认情况下，每个粒子系统支持的最大粒子数是1000（尽管它的说明写的是最大1004个）。不论使用与否，游戏都会为它分配这么大的内存。因此在创建粒子系统后，最好检查一下粒子计数器，看看你使用了多少粒子，然后把最大粒子数设置成这个数。这样做可以避免你的粒子系统占用了丧心病狂的内存大小。
; 多线程
: 粒子系统会多线程地处理粒子。因此如果有多个粒子系统，它们会被尽可能多地分配到不同的线程中。因此表面上来看，把一个复杂的粒子系统拆分成多个系统能提高整体性能。尽管如此，每增加一个系统都会增加一份开销，因此，拆分粒子系统所获得的性能提升较为有限。尤其是对于简单的系统而言，拆分一个只有为数不多粒子的粒子系统，基本可以确定是负优化行为。不过，如果你的粒子系统包含了上千个粒子，不妨试试将它拆分成几个，以利用多线程提升性能。对于同一个粒子系统来说，它自身与它嵌套的子系统只能运行在同一个线程上，这样方便它们相互传递数据。
; 单指令多数据流(SIMD)
: 目前，大多数粒子操作器、初始化器等等，都是以单指令多数据流(SIMD)方式工作的。意思是在现有硬件上，它们实际上在同时对一组4个粒子数据进行数学运算。理论上在未来的不同类型的硬件上，可以支持更多数量粒子的同时运算。重点在于，如果你保证粒子数是4的倍数，就能很好地利用这个机制。将粒子系统的数量削减到最近的4的倍数，就能获得少量的性能提升。尽管只有一星半点的提升，但知道这件事没什么坏处。
; 过度绘制
: 过渡绘制是同一个像素点被多次反复绘制造成的，原因是有许多图层，而显卡需要一层一层绘制图像。粒子通常是过度绘制的罪魁祸首，并且很容易就陷入最坏的情况，即大量精灵贴图互相堆叠，占满了整个屏幕。粒子系统自身提供了一些对抗过度绘制的方法。
: 最基础的做法就是遵循通用的解决方法，例如使用数量更少、尺寸更小、不透明的粒子，而不是许多半透明的粒子。然而，考虑到粒子系统的运用场景，这种做法不太现实。
: 下面介绍一些有帮助的 .vmt 材质设置参数。其中一对属性是 $minfadesize 和 $maxfadesize（字面意思是最小消失尺寸和最大消失尺寸）。这些属性会让粒子根据自身屏幕占比的大小逐渐消失。例如某个材质的 $minfadesize 为 0.25，$maxfadesize 为 0.5，它将会在占据 25% 屏幕大小时开始消失，一旦占据 50% 屏幕大小时完全消失（并不再渲染）。在步行穿过粒子特效实现的烟雾时，上述参数的设置与否，足以决定你的机器是上帧率天堂还是下过度绘制地狱。
: 另一个有帮助的参数是 $maxsize（字面意思是最大尺寸）。它决定了单个粒子在屏幕空间上绘制的最大尺寸。粒子最大只能保持在这个尺寸，无法超出。例如，你需要血液来实现命中反馈效果时，显然不希望它过近时消失（使用$maxfadesize参数）。但你可以设置它的最大屏幕占比，以此减少潜在的过度绘制问题。
: 顺带一提，$maxsize 还有一个与之相反的参数 $minsize。它与性能提升关系不大，主要是视觉上的考虑。它可以限制粒子在屏幕上的最小尺寸，不会比该值更小。还是以出血的命中反馈来举例，我们希望它在屏幕上的显示不会小到看不见，这样在命中远距离的敌人时依旧能获得视觉反馈。设置 $minsize 可以达到这一目的
: 上述两个参数也可以用于实现其它有意思的效果。例如，在使用尘埃粒子时我们会将它的尺寸限制在一个非常小的范围。这样一来，在远处你看到的是空气中漂浮的像素级大小闪烁的尘埃颗粒，而凑近看它们也不会变得跟网球一样大，产生糟糕的视觉效果。
: 深度混合(depth blending)可以在粒子表面互相重叠时依旧细致且平滑。然而，它同时也是填充率（和过度绘制正相关的一个性能指标）杀手。因此如果有需要，可以在 .vmt 中设置 $depthblend 0 来提升性能。
: 除此之外，对于动态纹理来说一个有用的 .vmt 参数是 $blendframes 0/1（字面意思是混合帧）。默认情况下动态纹理会将两个动画帧混合插帧来提高动画帧率。然而，这样也会提升场景的绘制次数。禁用帧混合会降低动画的流畅度，但能提高性能。
; 数据共享
: 有多种方式在主系统和子系统之间共享数据。在一些复杂的初始化器和操作器中，我们可以将某些通用参数、运算结果保存到挂点上，子系统就可以直接访问并使用，而不用重复计算。
; 碰撞
: 通常来说，碰撞计算是个开销极大的操作。默认的模式0会在每一帧都检查一遍所有粒子。这样做代价很高，也限制了粒子数量。然而，Collision(碰撞) 约束允许使用其它的碰撞模式，用碰撞精度换取运行速度。Collision Mode 3 对于难以预测粒子位置、运动的粒子系统来说，是精度与性能之间最佳平衡点。
== 制作 ==
制作好的粒子必须被添加到 GAME\particles\particles_manifest.txt 文件中。关于制作 DX8 图形接口的备选粒子系统，文件名中必须附加 "_dx80"，例如 “rockettrail.pcf”就要改成 “rockettrail_dx80.pcf”。在最高仅支持 DirectX 8 的机器上，将会加载该文件作为替代。不过这样做的必要性不大，毕竟绝大多数人的机器都支持 DX9 或更高。
== 另见 ==
*
*