從Unity 5.3開始,開發者可以透過腳本來存取粒子系統中所有的模組了。我們了解這些新的腳本特性可能會讓大家困惑,為什麼要用非常規的方式來使用結構?本文中你會找到一些答案。
範例:
這是一個典型的修改Emission Module中的速率的範例。
如果你熟悉.NET,你會注意到我們抓了結構體並且設了它的值,但沒有將值貼回粒子系統中。那麼粒子系統是如何知道這個改變呢?怎麼辦到的?
存取模組(Accessing Modules)
範例:
這是一個典型的修改Emission Module中的速率的範例。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
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);
}
}
|
只是一個介面
Unity中的粒子系統完全包含在引擎的C++部分中。當呼叫粒子系統或者其中的一個模組時,會直接呼叫C++端。
在內部,粒子系統模組(Particle system modules)只是粒子系統中一個屬性。他們並非獨立,且無法在系統間改變模組所有權或共用模組。所以雖然可以在腳本中抓取一個模組並傳遞,但模組永遠只會屬於同一個系統。
為了便於理解,讓我們看一下例子
當系統接收到一個對emission module的請求時,引擎會新建一個EmissionModule結構並且作為唯一的參數傳遞給它的擁有者。這麼做是因為存取模組的屬性必需要有粒子系統在上層。
當我們設定速率時,變數m_ParticleSystem用來存取這個模組並直接設定速率值。因此我們完全不需要重新賦予值到粒子系統中,因為它永遠是粒子系統的一部分,任何改變都會立即生效。所以模組做的只是呼叫擁有它的粒子系統,模組結構體本身只是一個進入粒子系統內部的接口。
在內部,模組存儲他們各自的屬性並保留狀態資訊,這就是為何無法在不同的粒子系統中共用模組。
如果你想將屬性從一個系統拷貝到另外一個,建議只拷貝相對應的值而不要拷貝整個類別,這樣就可以讓C++和C#之間拷貝的資料盡可能的少。
有很多模組的屬性都是由MinMaxCurve類別來驅動的。用來描述隨時間的變化的值,支援4種模式。以下是如何在腳本中使用它們的指南。
這是最簡單的模式,Constant只使用單一的值。這個值不會隨著時間改變而改變。如果你想要透過腳本改變一個值,改變這個值是一種方法。
在腳本中存取Constant的程式如下:
在內部,粒子系統模組(Particle system modules)只是粒子系統中一個屬性。他們並非獨立,且無法在系統間改變模組所有權或共用模組。所以雖然可以在腳本中抓取一個模組並傳遞,但模組永遠只會屬於同一個系統。
為了便於理解,讓我們看一下例子
當系統接收到一個對emission module的請求時,引擎會新建一個EmissionModule結構並且作為唯一的參數傳遞給它的擁有者。這麼做是因為存取模組的屬性必需要有粒子系統在上層。
1
2
3
4
5
6
7
8
|
public
sealed
class
ParticleSystem : Component
{
.....
public
EmissionModule emission {
get
{
return
new
EmissionModule(
this
); } }
....
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
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);
}
}
......
}
|
在內部,模組存儲他們各自的屬性並保留狀態資訊,這就是為何無法在不同的粒子系統中共用模組。
如果你想將屬性從一個系統拷貝到另外一個,建議只拷貝相對應的值而不要拷貝整個類別,這樣就可以讓C++和C#之間拷貝的資料盡可能的少。
The MinMaxCurve
有很多模組的屬性都是由MinMaxCurve類別來驅動的。用來描述隨時間的變化的值,支援4種模式。以下是如何在腳本中使用它們的指南。
Constant
這是最簡單的模式,Constant只使用單一的值。這個值不會隨著時間改變而改變。如果你想要透過腳本改變一個值,改變這個值是一種方法。
在腳本中存取Constant的程式如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
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);
}
}
|
Random between two constants
這個模式在兩個值之間產生一個亂數。在內部我們會將這兩個值作為關鍵分別存到最小和最大的曲線中。我們透過在兩個值之間進行線性插值計算來獲取新值,它使用一個標準劃的亂數作為插值量。這取樣方式和Random between two curves幾乎相同。
如何存取模組兩個constant的範例:
![](http://blogs.unity3d.com/wp-content/uploads/2016/04/2.png)
如何存取模組兩個constant的範例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
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);
}
}
|
Curve
在這個模式中,屬性值會透過對一個曲線的查詢來改變,使用腳本中MinMaxCurve的曲線時有一些注意事項。
首先,如果試著在一個Curve模式下讀取曲線值時會得到如下的錯誤:“Reading particle curves from script is unsupported unless they are in constant mode”。
由於曲線在引擎中是壓縮的狀態無法存取到MinMaxCurve,除非使用兩個constant模式之一,我們正著手計畫改進這點。主要原因是系統內部我們沒有實際保存一個AnimationCurve而是從兩個路徑中選擇一個。如果曲線很簡單(不超過三個key並在每個端點都有一個)。那麼我們會用一個性能較好的優化曲線。如果曲線很複雜我們會回頭使用未優化的曲線。在Inspector視窗中,未優化的曲線會在右下顯示一個小圖示讓你手動優化這個曲線。
優化的曲線
![](http://blogs.unity3d.com/wp-content/uploads/2016/04/3.png)
未優化的曲線
![](http://blogs.unity3d.com/wp-content/uploads/2016/04/4.png)
雖然不能用腳本從模組中存取曲線,但可以先存一個自己的曲線然後在需要的時候貼到到模組中,像這樣:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
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);
}
}
|
Random between 2 curves
這個模式從最小和最大曲線之間產生一個亂數,它用時間來決定在X軸上採樣的位置。陰影區表示可能的值。這個模式和曲線模式類似,不能在腳本中存取曲線也會幫你優化曲線(在允許情況下)。想要用這個模式看到效果,兩條曲線都必須被優化,也就是說和曲線模式一樣每條曲線不超過三個key並在每個端點各有一個key,你可以透過inspector視窗檢查右下角來確定曲線是否被優化。
![](http://blogs.unity3d.com/wp-content/uploads/2016/04/5.png)
下面的範例和Curve曲線模式很相似,只不過現在多設定了最小曲線。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
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優化前獲取的,SIMD優化能讓效能明顯提升。在特定測試場景結果如下:
![](http://blogs.unity3d.com/wp-content/uploads/2016/04/6.png)
解除你的苦痛
從MinMaxCurve中讀取曲線
或許你想要從腳本中讀取粒子系統曲線,不管何種模式。我們正努力讓這個功能實現,讓你可以在腳本中存取這些可愛的曲線。還有,現在無法檢查出是否模組正在用曲線。這也正在改進中!
將模組從Struct轉換為Class
我們現在正著手將所有的結構轉換到類別的工作。從功能上他們的行為是一樣的,但是類別是參考類型,這會更加明確模組從屬一個系統。同樣也可以在不保持一個臨時變數的情況下設定/獲取值。但是這就代表在構造時就會分配記憶體,在初始化時會產生GC。
例如:
1
2
3
4
5
6
|
var
em = ps.emission;
em.enabled =
true
;
// Could be written as
ps.emission.enabled =
true
;
|
結尾
希望本文講解能幫到你,可以到這裡參與討論。文中提到的粒子模組資訊也會加到Unity說明文件中。