github傳送門:https://github.com/dongzizhu/unity3DLearning/tree/master/hw7/ring
視頻傳送門:https://space.bilibili.com/472759319
有關粒子系統
粒子系統是模擬一些不確定、流動現象的技術。它採用許多形狀簡單且賦予生命的微小粒子作爲基本元素來表示物體(一般由點或很小的多邊形通過紋理貼圖表示),表達物體的總體形態和特徵的動態變化。
相關概念
粒子(particle)
粒子是粒子系統管理的基本單位。一般它是材料(Material)。材料包含兩個內容,紋理(texture)、shader,分別負責形態、光照效果、兩個方面。通常,粒子系統包含基礎材料庫供用戶選擇。
渲染(render)
渲染是定義粒子材料與攝像機之間的關係。主要包括材料面方位、顯示順序、光照等信息。
基本參數
- 持續時間(Duration):粒子系統發射粒子的持續時間
- 循環(Looping):粒子系統是否循環
- 預熱(Prewarm):當looping開啓時,才能啓動預熱(Prewarm),遊戲開始時粒子已經發射了一個週期
- 初始延遲(Start Delay):粒子系統發射粒子之前的延遲。注意在prewarm(預熱)啓用下不能使用此項
- 初始生命(Start Lifetime):以秒爲單位,粒子存活數量
- 初始速度(Start Speed):粒子發射時的速度
- 初始大小(Start Size):粒子發射時的大小
- 初始旋轉(Start Rotation):粒子發射時的旋轉值
- 初始顏色(Start Color):粒子發射時的顏色
- 重力修改器(Gravity Modifier):粒子在發射時受到的重力影響
- 繼承速度(Inherit Velocity):控制粒子速率的因素將繼承自粒子系統的移動(對於移動中的粒子系統)
- 模擬空間(Simulation Space):粒子系統在自身座標系還是世界座標系
- 喚醒時播放(Play On Awake):如果啓用粒子系統當在創建時,自動開始播放
- 最大粒子數(Max Particles):粒子發射的最大數量
- 自動隨機種子(Auto Random Seed):自動隨機種子,爲粒子加入隨機性,使得每次播放效果時都會有不同的模擬
- 停止時的動作(Stop Action):所有粒子都消亡後的行爲
- 剔除(Culling Mode):如果粒子不再視野範圍內,則將其剔除掉
- 粒子移除模式(Ring Buffer Mode):將粒子從系統中移除的方式
實例:小行星帶
我們這次來實現一個小行星以及圍繞其旋轉的小行星帶,用粒子系統來管理小行星帶中的每一個小行星。
首先新建一個球體(sphere)作爲小行星,初始位置設爲(0, 0, 0)。除了選擇相應地紋理,我們還要在其上掛載一個C#腳本來管理其位置和自傳運動。代碼如下。
public class Rotate : MonoBehaviour {
void Start () {
transform.localScale = new Vector3(5, 5, 5);
}
void Update () {
this.transform.Rotate(Vector3.down * 30 * Time.deltaTime);
}
}
接着新建一個空對象作爲粒子系統,同樣是將位置設爲(0, 0, 0)。然後通過Add Component添加Particle System,並其中的Renderer中將material選爲stone的材質。
最後將寫好的StarRing腳本掛載在這個對象上,具體代碼如下。
public class ParticleInfo
{
public float radius = 0;
public float angle = 0;
public ParticleInfo(float radius, float angle)
{
this.radius = radius; // 粒子半徑
this.angle = angle; // 粒子角度
}
}
public class StarRing : MonoBehaviour {
private ParticleSystem particleSys; // 粒子系統
private ParticleSystem.Particle[] particleArr; // 所有粒子
private ParticleInfo[] info; // 所有粒子信息
float speed = 0.25f; // 粒子速度
public int count = 8000; // 粒子數量
void Start () {
// 初始化
particleArr = new ParticleSystem.Particle[count];
info = new ParticleInfo[count];
particleSys = this.GetComponent<ParticleSystem>();
particleSys.loop = false; // 取消循環
particleSys.startSpeed = 0; // 設置粒子初速度
particleSys.maxParticles = count; // 設置最大粒子量
particleSys.Emit(count); // 發射粒子
particleSys.GetParticles(particleArr); // 獲取所有粒子
IniAll(); // 初始化所有粒子
}
// Update is called once per frame
void Update()
{
for (int i = 0; i < count; i++)
{
// 讓速度在小幅度內波動
float rotateSpeed = (speed / info[i].radius) * (i % 10 + 1);
// 移動
info[i].angle += rotateSpeed;
// 保證角度合法
info[i].angle %= 360.0f;
// 轉換成弧度制
float radian = info[i].angle * Mathf.PI / 180;
// 粒子在半徑方向上抖動
float offset = Random.Range(-0.01f, 0.01f); // 偏移範圍
info[i].radius += offset;
particleArr[i].position = new Vector3(info[i].radius * Mathf.Cos(radian), 0f, info[i].radius * Mathf.Sin(radian));
}
// 通過粒子數組設置粒子系統
particleSys.SetParticles(particleArr, particleArr.Length);
}
void IniAll()
{
float minRadius = 6.0f; // 最小半徑
float maxRadius = 10.0f; // 最大半徑
for (int i = 0; i < count; ++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);
// 隨機每個粒子的角度
float angle = Random.Range(0, 360);
// 轉換成弧度制
float radian = angle / 180 * Mathf.PI;
// 隨機每個粒子的大小
float size = Random.Range(0.01f, 0.03f);
info[i] = new ParticleInfo(radius, angle);
particleArr[i].position = new Vector3(info[i].radius * Mathf.Cos(radian), 0f, info[i].radius * Mathf.Sin(radian));
particleArr[i].size = size;
}
// 通過初始化好的粒子數組設置粒子系統
particleSys.SetParticles(particleArr, particleArr.Length);
}
}
其中ParticleInfo是自己實現的用於存儲粒子信息的類,以此來提高代碼的封裝性和可讀性。然後要保證所有的粒子公轉軌跡和行星的自轉軌跡都是自西向東的。另外還值得一提的是,爲了不讓行星帶的邊緣過於明顯,在IniAll()函數中讓所有粒子的半徑帶有了一點隨機性,而且還在update函數中讓每個粒子在很小的範圍內抖動,從而使畫面更加真實。最後我還加入了之前使用過很多次的starfield天空盒,來營造太空的感覺。
最終的實現效果如下所示。