粒子系統(tǒng)概述
根據(jù)官方文檔的解釋,粒子系統(tǒng)管理大量既小又簡單的圖片或網(wǎng)格(mesh)一起運動赛不,組成一個整體的效果睬辐。比如利用粒子系統(tǒng)可以制作出煙霧效果,每一個粒子是一個微小的煙云的圖片症汹,成千上萬個這樣的粒子就組成了一整塊煙霧的效果硫朦,用普通的方法就很難做出煙霧的效果。
粒子系統(tǒng)的重要屬性
-
生命周期lifetime:每一個粒子都有一個生命周期背镇,表示粒子被發(fā)射(emit)出來以后能存活多長時間咬展。你可以在Inspector中設(shè)置Start lifetime來設(shè)置粒子的生命周期:
Inspector中設(shè)置lifetime
之所以叫start是因為你設(shè)置的只是粒子默認的生命周期,在運行過程中l(wèi)ifetime還有可能被腳本改變瞒斩。
- 發(fā)射速率emission rate:每秒鐘發(fā)射多少個粒子破婆。你可以在Inspector中找到Emission模塊的rate over time中設(shè)置發(fā)射速率。
實際發(fā)射粒子的時機有一定的隨機性济瓢,不一定是間隔均勻地發(fā)射荠割。
以上兩個屬性描述的是整個粒子系統(tǒng)的狀態(tài)。我們還可以控制單個粒子的樣式和行為旺矾。
- 粒子個體的屬性:速度矢量蔑鹦、顏色、大小箕宙、朝向等嚎朽。有一些屬性除了可以設(shè)置常量值以外,還可以設(shè)置成隨著時間變化的值柬帕,或者在一定范圍內(nèi)隨機的值哟忍。你只需要點擊輸入框右邊的下拉三角按鈕,就可以改變設(shè)置方式陷寝。
由于整個粒子系統(tǒng)有很多的屬性可以自定義锅很,因此Unity將它劃分成了很多個模塊(Particle System modules),方便查找凤跑。這是粒子系統(tǒng)模塊的參考文檔爆安。
項目概述
這個項目制作一個簡單的粒子光環(huán),光環(huán)中的粒子緩慢移動仔引,看起來像太陽系的小行星帶扔仓。效果盡量類似http://i-remember.fr/en。要實現(xiàn)這個效果咖耘,需要使用腳本來控制每一個粒子翘簇。
效果圖
在自己的電腦上運行!
從我的github下載項目資源儿倒,將所有文件放進你的項目的Assets文件夾(如果有重復(fù)則覆蓋)版保,然后在Unity3D中雙擊“hw9”,就可以運行了!
代碼
ParticleStatus 用于記錄某個粒子的狀態(tài):
public class ParticleStatus {
public float radius = 0f, angle = 0f, time = 0f, radiusChange = 0.02f; // 粒子軌道變化范圍;
public ParticleStatus(float radius, float angle, float time, float radiusChange)
{
this.radius = radius; // 半徑
this.angle = angle; // 角度
this.time = time; // 變化的進度條彻犁,用于軌道半徑的變化
radiusChange = this.radiusChange; // 軌道半徑的變化程度
}
}
ParticleHalo控制一個含有粒子系統(tǒng)的對象蹈垢,生成一個光環(huán):
using UnityEngine;
public class ParticleHalo : MonoBehaviour
{
private ParticleSystem particleSys; // 粒子系統(tǒng)組件
private ParticleSystem.Particle[] particleArr; // 粒子數(shù)組
private ParticleStatus[] StatusArr; // 記錄粒子狀態(tài)的數(shù)組
public int particleNum = 10000; // 粒子數(shù)量
public float minRadius = 8.0f; // 光環(huán)最小半徑
public float maxRadius = 12.0f; // 光環(huán)最大半徑
public float maxRadiusChange = 0.02f; // 粒子軌道變化的平均值
public bool clockwise = true; // 光環(huán)是否順時針旋轉(zhuǎn)
public float rotateSpeed = 0.3f; // 光環(huán)旋轉(zhuǎn)速度
public int speedLevel = 5; // 速度有多少個層次
private NormalDistribution normalGenerator; // 高斯分布生成器
public Gradient colorGradient; // 控制粒子的透明度
void Start()
{
particleSys = GetComponent<ParticleSystem>();
particleArr = new ParticleSystem.Particle[particleNum];
StatusArr = new ParticleStatus[particleNum];
var ma = particleSys.main; // 通過ma來設(shè)置粒子系統(tǒng)的maxParticles
ma.maxParticles = particleNum;
particleSys.Emit(particleNum); // 同時發(fā)射particleNum個粒子
particleSys.GetParticles(particleArr); // 將發(fā)射的粒子存在particleArr數(shù)組中
normalGenerator = new NormalDistribution(); // 初始化高斯分布生成器
// 初始化梯度顏色控制器
GradientAlphaKey[] alphaKeys = new GradientAlphaKey[5];
alphaKeys[0].time = 0.0f; alphaKeys[0].alpha = 1.0f;
alphaKeys[1].time = 0.4f; alphaKeys[1].alpha = 0.4f;
alphaKeys[2].time = 0.6f; alphaKeys[2].alpha = 1.0f;
alphaKeys[3].time = 0.9f; alphaKeys[3].alpha = 0.4f;
alphaKeys[4].time = 1.0f; alphaKeys[4].alpha = 0.9f;
GradientColorKey[] colorKeys = new GradientColorKey[2];
colorKeys[0].time = 0.0f; colorKeys[0].color = Color.white;
colorKeys[1].time = 1.0f; colorKeys[1].color = Color.white;
colorGradient.SetKeys(colorKeys, alphaKeys);
initParticle();
}
void initParticle()
{
for (int i = 0; i < particleNum; i++)
{
// 普通的隨機半徑生成
// float midRadius = (maxRadius + minRadius) / 2;
// float minRate = Random.Range(1.0f, midRadius / minRadius);
// float maxRate = Random.Range(midRadius / maxRadius, 1.0f);
// float radius = Random.Range(minRadius * minRate, maxRadius * maxRate);
// 使用高斯分布生成半徑, 均值為midRadius袖裕,標準差為0.7
float midRadius = (maxRadius + minRadius) / 2;
float radius = (float)normalGenerator.NextGaussian(midRadius, 0.7);
float angle = Random.Range(0.0f, 360.0f);
float theta = angle / 180 * Mathf.PI;
float time = Random.Range(0.0f, 360.0f); // 給粒子生成一個隨機的初始進度
float radiusChange = Random.Range(0.0f, maxRadiusChange); // 隨機生成一個軌道變化大小
StatusArr[i] = new ParticleStatus(radius, angle, time, radiusChange);
particleArr[i].position = computePos(radius, theta);
}
particleSys.SetParticles(particleArr, particleArr.Length);
}
Vector3 computePos(float radius, float theta)
{
return new Vector3(radius * Mathf.Cos(theta), 0f, radius * Mathf.Sin(theta));
}
void Update()
{
for (int i = 0; i < particleNum; i++)
{
// 將所有粒子根據(jù)下標i曹抬,給5個不同的速度,分別是rotateSpeed的1/5急鳄、2/5……5/5
if (!clockwise)
{
StatusArr[i].angle += (i % speedLevel + 1) * (rotateSpeed / speedLevel);
}
else
{
StatusArr[i].angle -= (i % speedLevel + 1) * (rotateSpeed / speedLevel);
}
// angle range guarantee
StatusArr[i].angle = (360.0f + StatusArr[i].angle) % 360.0f;
float theta = StatusArr[i].angle / 180 * Mathf.PI;
StatusArr[i].time += Time.deltaTime; // 增加粒子的進度
StatusArr[i].radius += Mathf.PingPong(StatusArr[i].time / maxRadius / maxRadius, StatusArr[i].radiusChange) - StatusArr[i].radiusChange / 2.0f; // 根據(jù)粒子的進度谤民,給粒子的半徑賦予不同的值,這個值在0與StatusArr[i].radiusChange之間來回擺動
particleArr[i].position = computePos(StatusArr[i].radius, theta);
particleArr[i].color = colorGradient.Evaluate(StatusArr[i].angle / 360.0f); // 根據(jù)粒子的angle疾宏,給粒子賦予不同的透明度(顏色)张足,使某一些角度上的粒子暗一些
}
particleSys.SetParticles(particleArr, particleArr.Length);
}
}
NormalDistribution 用于產(chǎn)生正態(tài)分布的隨機數(shù),原理是Marsaglia polar method:
using System;
public class NormalDistribution {
// use Marsaglia polar method to generate normal distribution
private bool _hasDeviate;
private double _storedDeviate;
private readonly Random _random;
public NormalDistribution(Random random = null)
{
_random = random ?? new Random();
}
public double NextGaussian(double mu = 0, double sigma = 1)
{
if (sigma <= 0)
throw new ArgumentOutOfRangeException("sigma", "Must be greater than zero.");
if (_hasDeviate)
{
_hasDeviate = false;
return _storedDeviate*sigma + mu;
}
double v1, v2, rSquared;
do
{
// two random values between -1.0 and 1.0
v1 = 2*_random.NextDouble() - 1;
v2 = 2*_random.NextDouble() - 1;
rSquared = v1*v1 + v2*v2;
// ensure within the unit circle
} while (rSquared >= 1 || rSquared == 0);
// calculate polar tranformation for each deviate
var polar = Math.Sqrt(-2*Math.Log(rSquared)/rSquared);
// store first deviate
_storedDeviate = v2*polar;
_hasDeviate = true;
// return second deviate
return v1*polar*sigma + mu;
}
}
代碼意義已經(jīng)在注釋中解釋
兩層光環(huán)
為了做出兩層光環(huán)坎藐,只需要將同一份腳本掛載在兩個具有Particle System的對象上为牍,然后在Inspector中調(diào)整一下參數(shù),就可以實現(xiàn)內(nèi)層逆時針岩馍、外層順時針碉咆、內(nèi)層快、外層慢蛀恩、內(nèi)層稠密疫铜、外層稀疏的效果了。