霹靂星球又爆炸了

上一篇大約講了粒子怎麼控制,這篇講如何在 LateUpdate 控制粒子。

moveback.mov

在這個例子,我們在 LateUpdate 裡用一些數學算式手動把粒子設定在我們要的位置,然後再給它一個隨機的方向速度,讓它噴出去。

設定

以下是我們對粒子系統的設定(其實與上一篇一樣):

  • PlayOnAwake 是關閉的。
  • Collision 是打開的讓它有碰撞。
  • Renderer 把它設定成 Mesh Cube,因為我們會對粒子改顏色,所以建立一個 Material 再寫一個簡單有頂點色的 Shader 給 Renderer。

除了以上三個,其它功能的都是關閉不勾選的。

操作流程

我們在 LateUpdate 操作粒子的流程如下:

  1. 確認粒子己被建立出來。
  2. ParticleSystem.GetParticles 取出所有可用粒子
  3. ParticleSystem.Particle.position 用數學算式設定粒子位置
  4. 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"
}

這次爆炸的應該是霹靂星雲吧!?

發表留言