上一篇大約講了粒子怎麼控制,這篇講如何在 LateUpdate 控制粒子。
在這個例子,我們在 LateUpdate 裡用一些數學算式手動把粒子設定在我們要的位置,然後再給它一個隨機的方向速度,讓它噴出去。
設定
以下是我們對粒子系統的設定(其實與上一篇一樣):
- PlayOnAwake 是關閉的。
- Collision 是打開的讓它有碰撞。
- Renderer 把它設定成 Mesh Cube,因為我們會對粒子改顏色,所以建立一個 Material 再寫一個簡單有頂點色的 Shader 給 Renderer。
除了以上三個,其它功能的都是關閉不勾選的。
操作流程
我們在 LateUpdate 操作粒子的流程如下:
- 確認粒子己被建立出來。
- ParticleSystem.GetParticles 取出所有可用粒子
- ParticleSystem.Particle.position 用數學算式設定粒子位置
- ParticleSystem.SetParticles 把設定過的粒子塡回粒子系統
比上一篇簡單多了。
按鍵輸入
為了能看清楚粒子排出來的結果,我們寫成:
- 按下 A 鍵時會建立粒子並把 ParticleSystem.MainModule.simulationSpeed 設為 0,然後用數學公式去移動粒子.
- 按下 B 鍵時再手動把 simulationSpeed 設 1 讓它模擬下去,爆炸開來。
- 按下 C 鍵時把 ParticleSystem.MainModule.simulationSpeed 設為 0,再把噴出去的粒子移回原位成正方型。
大約就這樣子,我們就能看到粒子在變化,簡簡單的又完成了!
以下就是程式及 Shader
CS:
//Arc https://arclee0117.wordpress.com using System.Collections; using System.Collections.Generic; using UnityEngine; public class ParticleExp : MonoBehaviour { public ParticleSystem m_ParticleSystem; ParticleSystem.Particle[] m_Particles; public enum eStatus { NONE = 0, SET_POS, EXPLO, MOVE_BACK, } public eStatus m_Status = eStatus.NONE; public uint m_DimX = 1; public uint m_DimY = 1; public uint m_DimZ = 1; public float m_Size = 1; public float m_ForceScale = 1; public float m_MoveBackTime = 1; float m_MoveBackSetupTime = 0; // Use this for initialization void Start () { } void Update() { if (Input.GetKeyUp(KeyCode.B)) { // 開始。 ParticleSystem.MainModule mm = m_ParticleSystem.main; mm.simulationSpeed = 1.0f; // 炸爆。 m_Status = eStatus.EXPLO; } else if (Input.GetKeyUp(KeyCode.A)) { // 設定。 ParticleSystem.MainModule mm = m_ParticleSystem.main; mm.simulationSpeed = 0.0f; SetupParticle(); // 用數學設定位置 m_Status = eStatus.SET_POS; } else if (Input.GetKeyUp(KeyCode.C)) { // 設定。 ParticleSystem.MainModule mm = m_ParticleSystem.main; mm.simulationSpeed = 0.0f; m_MoveBackSetupTime = 0; // 移回正方型。 m_Status = eStatus.MOVE_BACK; } } void LateUpdate() { switch (m_Status) { case eStatus.SET_POS: { // 確認粒子己建。 if (m_Particles != null) { Vector3 tempV3 = Vector3.zero; int count = m_ParticleSystem.GetParticles(m_Particles); for (int i = 0; i < count; i++) { tempV3 = m_Particles[i].position; // 隨便丟數學公式讓它變化。 tempV3.x += Mathf.Cos(i * Time.realtimeSinceStartup * 0.005f) * 0.1f; tempV3.y += Mathf.Sin(i * Time.realtimeSinceStartup * 0.005f) * 0.1f;; m_Particles[i].position = tempV3; } m_ParticleSystem.SetParticles(m_Particles, m_Particles.Length); } break; } case eStatus.MOVE_BACK: { if (m_Particles != null) { m_MoveBackSetupTime += Time.deltaTime; m_MoveBackSetupTime = Mathf.Min(m_MoveBackSetupTime, m_MoveBackTime); // 取出粒子。 // 因為才剛噴發而己,所以一定都是 alive 的粒子,可以拿來操作。 m_ParticleSystem.GetParticles(m_Particles); // 接下來就假設我們要噴一個大立方體的正方型。 Vector3 tempV3 = Vector3.zero; Vector3 centerPos = Vector3.zero; centerPos.x = -m_DimX / 2; centerPos.y = -m_DimY / 2; centerPos.z = -m_DimZ / 2; for (uint x = 0; x < m_DimX; x++) { for (uint y = 0; y < m_DimY; y++) { for (uint z = 0; z < m_DimZ; z++) { uint idx = (x * m_DimY * m_DimZ) + (y * m_DimZ) + z; // 設定位置。 tempV3.x = centerPos.x + x; tempV3.y = centerPos.y + y; tempV3.z = centerPos.z + z; tempV3 *= m_Size; m_Particles[idx].position = m_Particles[idx].position * (1 - (m_MoveBackSetupTime / m_MoveBackTime)) + (tempV3 * (m_MoveBackSetupTime / m_MoveBackTime)); } } } // 把改過的粒子設定回粒子系統裡。 m_ParticleSystem.SetParticles(m_Particles, m_Particles.Length); } break; } } } // 把粒子設定成目標的型狀。 void SetupParticle() { ParticleSystem.MainModule mm = m_ParticleSystem.main; // 設定數量。 mm.maxParticles = (int)(m_DimX * m_DimY * m_DimZ); // 清空,噴發。 m_ParticleSystem.Clear(); m_ParticleSystem.Emit(m_ParticleSystem.main.maxParticles); if (m_Particles == null || m_Particles.Length != m_ParticleSystem.main.maxParticles) { m_Particles = new ParticleSystem.Particle[m_ParticleSystem.main.maxParticles]; } // 取出粒子。 // 因為才剛噴發而己,所以一定都是 alive 的粒子,可以拿來操作。 m_ParticleSystem.GetParticles(m_Particles); // 接下來就假設我們要噴一個大立方體的正方型。 Vector3 tempV3 = Vector3.zero; Color tempColor = Color.black; Vector3 centerPos = Vector3.zero; centerPos.x = -m_DimX / 2; centerPos.y = -m_DimY / 2; centerPos.z = -m_DimZ / 2; for (uint x = 0; x < m_DimX; x++) { for (uint y = 0; y < m_DimY; y++) { for (uint z = 0; z < m_DimZ; z++) { uint idx = (x * m_DimY * m_DimZ) + (y * m_DimZ) + z; // 設定位置。 tempV3.x = centerPos.x + x; tempV3.y = centerPos.y + y; tempV3.z = centerPos.z + z; m_Particles[idx].position = tempV3 * m_Size; //設定顏色。 tempColor.r = (float)x / (float)m_DimX; tempColor.g = (float)y / (float)m_DimY; tempColor.b = (float)z / (float)m_DimZ; m_Particles[idx].startColor = tempColor; // 設定大小。 m_Particles[idx].startSize = m_Size; // 噴出的方向。 tempV3.x = Random.Range(-1.0f, 1.0f); tempV3.y = Random.Range(-1.0f, 1.0f); tempV3.z = Random.Range(-1.0f, 1.0f); m_Particles[idx].velocity = tempV3 * m_ForceScale; m_Particles[idx].startLifetime = 10; } } } // 把改過的粒子設定回粒子系統裡。 m_ParticleSystem.SetParticles(m_Particles, m_Particles.Length); // 先停止,等一下再手動操作。 mm.simulationSpeed = 0; } }
Shader:
//Arc https://arclee0117.wordpress.com Shader "Custom/meshshader" { SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM // Physically based Standard lighting model, and enable shadows on all light types #pragma surface surf Standard fullforwardshadows vertex:vert // Use shader model 3.0 target, to get nicer looking lighting #pragma target 3.0 sampler2D _MainTex; struct Input { float3 vertColor; }; struct InOutData { float4 vertex : POSITION; // The vertex position in model space. float3 normal : NORMAL; // The vertex normal in model space. float4 color : COLOR; // Per-vertex color }; void vert(inout InOutData v, out Input o) { UNITY_INITIALIZE_OUTPUT(Input, o); o.vertColor = v.color; } void surf (Input IN, inout SurfaceOutputStandard o) { o.Albedo = IN.vertColor;; } ENDCG } FallBack "Diffuse" }
這次爆炸的應該是霹靂星雲吧!?