查看原文
其他

粒子系统模块答疑

2016-04-21 Unity官方 Unity官方平台
从Unity 5.3开始,就可以通过脚本来访问粒子系统中所有的模块了。我们发现这些新的脚本特性可能让大家感到有些迷惑。为什么我们要用非常规的方式来使用结构体?在本文中,我们将解答你的疑惑。
访问模块

一个范例
这是一个典型的修改发射模块中的速率的范例。



如果你熟悉.NET,你会注意到我们抓取了结构体并且设置了它的值,但没有将该值赋回粒子系统中。那么粒子系统是如何知道这个改变呢?背后有什么魔法么?


这只是一个接口
Unity中的粒子系统模块完全包含在引擎的C++部分中。当调用粒子系统或者其中的一个模块时,会直接调用到C++端。
在其内部,粒子系统模块是粒子系统中的属性。他们不是独立的,并且我们永远不会在系统间交换模块所有权和共享模块。因此,虽然可以在脚本中抓取一个模块并且在脚本中传递,但是模块永远只会属于同一个系统。
为了便于理解,让我们详细看一下前面的例子。

当系统接收到一个对发射模块的请求时,引擎会新建一个EmissionModule结构体并且传递拥有它的粒子系统作为其唯一的参数。我们这么做的原因是访问模块属性必需要有粒子系统。


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


在其内部,模块存储他们各自的属性并且他们同样保留基于状态的信息,这就是为什么不能在不同的粒子系统中共享模块或者赋值模块。
如果你想将属性从一个系统拷贝到另外一个,一个建议就是只拷贝相应的值而不要拷贝整个类,这样就可以让C++端和C#端之间拷贝的数据尽可能的少。


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

常量模式

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



在脚本中访问该常量的代码如下:




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




访问模块两个常量的范例如下:




曲线模式
在这个模式中,属性值会通过对一个曲线的查询来改变,当使用脚本中MinMaxCurve的曲线时有一些注意事项。
首先,如果试着在某一个曲线模式下读取曲线值的时候会得到如下的错误消息:“Reading particle curves from script is unsupported unless they are in constant mode”。
由于曲线在引擎中的压缩方式,是不可能获取到MinMaxCurve的,除非使用了两个常量模式中的一个。我们知道这不太好,并且计划对此改进。其原因是在系统内部,我们没有实际保存一个AnimationCurve,而是从两个路径中选择一个。如果曲线很简单(不超过三个键并且在每个端点都有一个)。那么我们使用一个优化的多项式曲线,这样性能更好。如果很复杂我们会回退到使用未优化曲线。在检视窗口中,一个未优化的曲线会在其右下显示一个小图标可以让你优化这个曲线。
优化的曲线


未优化的曲线



虽然不能在脚本中从一个模块中获得曲线,但是可以先储存一个自己的曲线然后在需要的时候应用到模块中,如下:




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




下面的范例同曲线模式很相似,但是,我们现在还设置了最小曲线。




性能
我们做了一些简单的性能比对来看看这些模式之间的差异。这些样本都是在我们的SIMD优化前获取的,此优化能达到明显的性能提升。在我们的特定测试场景下,结果如下:

其它需求
从MinMaxCurve中读取曲线

你可能需要能够从脚本中读取粒子系统曲线,不管他们是以何种模式工作。我们正致力于移除这个限制,这样你就可以在脚本中读取并修改这些可爱的曲线了。还有现在不能查询曲线来得知当前模式是否在使用曲线而不抛出错误。这个也会做修补!
将模块从结构体转换为类
我们现在正着手将所有的结构体转换到类的原型化工作。从功能上说他们的行为是一样的,但是类是引用类型,这就会更加明确模块从属于一个系统。同样也可以在不保持一个临时变量的情况下设置/获取值。但是这就意味着在构造时就会分配内存,这将会产生GC内存分配,当然只是在初始化的时候。
例如:



结尾
希望本文的讲解可以帮助到你,还可以点击[阅读原文]下载文中的范例并参与讨论。关于文中提到的粒子模块信息我们也会添加到文档中。

我们后面还会为大家分享更多的技术内容,请关注Unity官方中文社区(forum.china.unity3d.com)。


点击“阅读原文”进入官方社区,下载文中范例!



您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存