从Unity 5.3开始,你可以通过脚本访问粒子系统中所有的模块。我们注意到这些新的脚本特性可能有一些让人迷惑。为什么我们用一个非常规的方式使用结构体?
在本博文中,我们将解答你的疑惑。

访问模块一个范例,这是一个典型的修改发射模块中的速率的范例。
[C#] 纯文本查看 复制代码using UnityEngine; public class AccessingAParticleSystemModule : MonoBehaviour { // Use this for initialization void Start () { // Get the emission module. var forceModule = GetComponent<ParticleSystem>().forceOverLifetime; // Enable it and set a value forceModule.enabled = true; forceModule.x = new ParticleSystem.MinMaxCurve(15.0f); } }
如果你熟悉.NET,你会注意到我们抓取了结构体并且设置了它的值但是没有赋值回粒子系统中。那么粒子系统是怎么知道这个改变呢?背后有什么魔法么?

这只是一个接口,Unity中的粒子系统模块完全包含在引擎的C++部分里面。当调用粒子系统或者其中的一个模块时,会直接调用到C++端。

在其内部,粒子系统模块是粒子系统中的属性。他们不是独立的,并且我们永远不会在系统间交换模块所有权和共享模块。因此,虽然可以在脚本中抓取一个模块并且在脚本中传递,但是模块永远只会属于同一个系统。

为了有助于理解,让我们详细看一下前面的例子。当系统接收到一个对发射模块的请求时,引擎创建一个新的EmissionModule结构体并且传递拥有它的粒子系统作为其唯一的参数。我们这么做的原因是粒子系统是访问模块属性必需的。
[C++] 纯文本查看 复制代码public sealed class ParticleSystem : Component { ..... public EmissionModule emission { get { return new EmissionModule(this); } } .... } public partial struct EmissionModule { private ParticleSystem m_ParticleSystem; // Direct access to the particle system that owns this module. EmissionModule(ParticleSystem particleSystem) { m_ParticleSystem = particleSystem; } public MinMaxCurve rate { set { // In here we call down to the c++ side and perform what amounts to this: m_ParticleSystem->GetEmissionModule()->SetRate(value); } } ...... }

当我们设置速率时,变量m_ParticleSystem用于访问这个模块并且直接设置其速率值。因此我们完全不需要重新把模块赋值到粒子系统中,因为它永远是粒子系统的一部分,任何改变会立即生效。所以模块做的只是调用到拥有它的粒子系统内部,模块结构体本身只是一个进入粒子系统内部的接口。

在其内部,模块存储他们各自的属性并且他们同样保留基于状态的信息,这就是为什么不能在不同的粒子系统享模块或者赋值模块。

如果你想将属性从一个系统拷贝到另外一个,有一个建议就是你只拷贝相应的值而不要拷贝整个类,这样就可以在C++端和C#端之间拷贝的数据尽可能的少。

MinMaxCurve
有很多模块的属性都是由MinMaxCurve类来驱动的。它用于描述值随着时间的变化。有4种支持的模式;这里是如何在脚本中使用它们的指南。

常量模式
这是最简单的模式,常量模式只使用一个单一的常量值。这个值不会随着时间改变而改变。如果你想要通过脚本改变一个值,设置这个量是其中一个实现途径。

184523zvnihh66pie6ynh2.png
在脚本中,我们可以像这样访问这个常量:
[C#] 纯文本查看 复制代码using UnityEngine; public class MinMaxCurveConstantMode : MonoBehaviour { ParticleSystem myParticleSystem; ParticleSystem.EmissionModule emissionModule; void Start() { // Get the system and the emission module. myParticleSystem = GetComponent<ParticleSystem>(); emissionModule = myParticleSystem.emission; GetValue(); SetValue(); } void GetValue() { print(The constant value is + emissionModule.rate.constantMax); } void SetValue() { emissionModule.rate = new ParticleSystem.MinMaxCurve(10.0f); } }

双常量间随机模式
这个模式在两个常量之间产生一个随机数。在其内部,我们将这两个值作为键分别存储到最小和最大曲线中。我们通过执行一个在两个值之间的线性插值计算来获取这个值,它使用一个归一化的随机数作为插值量。这就意味它的工作量和两个曲线模式的工作量是相同的。

184712uq7co5o8e97odz97.png
这里是我们如何访问模块的两个常量的范例:
[C#] 纯文本查看 复制代码using UnityEngine; public class ParticleModuleExamples : MonoBehaviour { ParticleSystem myParticleSystem; ParticleSystem.EmissionModule emissionModule; void Start() { // Get the system and the emission module. myParticleSystem = GetComponent<ParticleSystem>(); emissionModule = myParticleSystem.emission; GetRandomConstantValues(); SetRandomConstantValues(); } void GetRandomConstantValues() { print(string.Format(The constant values are: min {0} max {1}., emissionModule.rate.constantMin, emissionModule.rate.constantMax)); } void SetRandomConstantValues() { // Assign the curve to the emission module emissionModule.rate =new ParticleSystem.MinMaxCurve(0.0f, 1.0f); } }

曲线模式
在这个模式中,属性值将会通过对一个曲线的查询来改变,当使用脚本中MinMaxCurve的曲线时有一些注意事项。

首先,如果你尝试在某一个曲线模式下读取曲线值的时候会得到如下的错误消息:: “Reading particle curves from script is unsupported unless they are in constant mode”。

由于曲线在引擎中的压缩方式,是不可能获取到MinMaxCurve的,除非使用了两个常量模式中的一个。我们知道这个不怎么好,并且计划对此改进。其原因是在系统内部,我们没有实际保存一个AnimationCurve,而是从两个路径中选择一个。如果曲线很简单(不超过三个键并且在每个端点都有一个)。那么我们使用一个优化的多项式曲线,这样可以提供更好的性能。如果很复杂我们会回退到使用未优化曲线。在检视窗口中,一个未优化的曲线会在其右下显示一个小图标可以让你优化这个曲线。

优化的曲线
185208pnh16wkxdz5tzabs.png

未优化的曲线
185315auc88hy2cmzpf32y.png

虽然不能在脚本中从一个模块中获得曲线,但是可以先储存你自己的一个曲线然后在需要的时候应用到模块中,就像这样:
[C#] 纯文本查看 复制代码using UnityEngine; public class MinMaxCurveCurveMode : MonoBehaviour { ParticleSystem myParticleSystem; ParticleSystem.EmissionModule emissionModule; // We can scale the curve with this value. It gets multiplied by the curve. public float scalar = 1.0f; AnimationCurve ourCurve; void Start() { // Get the system and the emission module. myParticleSystem = GetComponent<ParticleSystem>(); emissionModule = myParticleSystem.emission; // A simple linear curve. ourCurve = new AnimationCurve(); ourCurve.AddKey(0.0f, 0.0f); ourCurve.AddKey(1.0f, 1.0f); // Apply the curve emissionModule.rate = new ParticleSystem.MinMaxCurve(scalar, ourCurve); // In 5 seconds we will modify the curve. Invoke(ModifyCurve, 5.0f); } void ModifyCurve() { // Add a key to the current curve. ourCurve.AddKey(0.5f, 0.0f); // Apply the changed curve emissionModule.rate = new ParticleSystem.MinMaxCurve(scalar, ourCurve); } }

两个曲线间随机模式
这个模式从最小和最大曲线之间生成一个随机数,它使用时间来决定在X轴上采样的位置。阴影区域表示可能的值。这个模式同曲线模式类似,不能在脚本中访问曲线并且使用了优化多项式曲线(在可能的情况下)。为了从此模式获益,两条曲线都必须被优化,就是说每条曲线包含不超过三个键并且在每个端点有一个。与曲线模式一样,可以通过检查编辑窗口的右下角来确定曲线是否被优化的。
185507v0c9hjhihsjzsici.png

下面的范例同曲线模式很相似,但是,我们现在还设置了最小曲线。
[C#] 纯文本查看 复制代码using UnityEngine; public class MinMaxCurveRandom2CurvesMode : MonoBehaviour { ParticleSystem myParticleSystem; ParticleSystem.EmissionModule emissionModule; AnimationCurve ourCurveMin; AnimationCurve ourCurveMax; // We can scale the curves with this value. It gets multiplied by the curves. public float scalar = 1.0f; void Start() { // Get the system and the emission module. myParticleSystem = GetComponent<ParticleSystem>(); emissionModule = myParticleSystem.emission; // A horizontal straight line at value 1 ourCurveMin = new AnimationCurve(); ourCurveMin.AddKey(0.0f, 1.0f); ourCurveMin.AddKey(1.0f, 1.0f); // A horizontal straight line at value 0.5 ourCurveMax = new AnimationCurve(); ourCurveMax.AddKey(0.0f, 0.5f); ourCurveMax.AddKey(1.0f, 0.5f); // Apply the curves emissionModule.rate = new ParticleSystem.MinMaxCurve(scalar, ourCurveMin, ourCurveMax); // In 5 seconds we will modify the curve. Invoke(ModifyCurve, 5.0f); } void ModifyCurve() { // Create a pinch point. ourCurveMin.AddKey(0.5f, 0.7f); ourCurveMax.AddKey(0.5f, 0.6f); // Apply the changed curve emissionModule.rate = new ParticleSystem.MinMaxCurve(scalar, ourCurveMin, ourCurveMax); } }
性能
我们做了一些简单的性能比对来看看这些模式之间的差异。这些样本都是在我们的SIMD优化前获取的,此优化能获得明显的性能提升。在我们的特定测试场景下,我们获得了如下结果:
185713mt9zporio96n8zir.png

缓解痛苦

从MinMaxCurve中读取曲线
我们知道你很可能需要能够从脚本中读取粒子系统曲线,不管他们是工作在什么模式下面。我们正在致力于移除这个限制,这样你就可以在脚本中读取/修改这些可爱的曲线了。还有现在不能查询曲线来得知当前模式是否在使用曲线而不抛出错误。这个也会做修补!

将模块从结构体转换为类
我们现在正在做将所有的结构体转换到类的原型化工作。从功能上说他们的行为是一样的,但是类是引用类型,这就会更加明确模块从属于一个系统。同样也可以在不保持一个临时变量的情况下设置/获取值。但是这就意味着在构造时就会分配内存,这将会产生GC内存分配,当然只是在初始化的时候。
[C++] 纯文本查看 复制代码var em = ps.emission; em.enabled = true; // Could be written as ps.emission.enabled = true;
结尾
我们希望本博文对你有用,请移步这里来获取范例以及参与评论。
我们也会将此信息添加到我们的文档中。

粒子, Particle锐亚教育

锐亚教育 锐亚科技 unity unity教程