天天看點

Unity3d Note8(粒子光環)

      • 1.作業要求
      • 2.實作過程
          • 2.1遊戲對象建立
          • 2.2圓環制備
          • 2.3旋轉
          • 2.4改進
      • 3.源代碼

1.作業要求

參考 http://i-remember.fr/en 這類網站,使用粒子流程式設計控制制作一些效果。

2.實作過程

這次作業實話說相對于之前的幾次子產品小遊戲程式設計來說是非常的輕松的,因為前幾次要求的代碼量大,而且都是一些完整的小遊戲,還要求用上一些程式設計模式,而現在隻要專心于一個效果,而且最重要的是最終效果很好看。

我參考了兩篇部落格:Unity3d 粒子光環制作、Unity3D學習筆記(9)—— 粒子光環

然後看看效果圖:

Unity3d Note8(粒子光環)
可以看到效果圖中光環是有最大半徑和最小半徑,而且粒子都是集中于平均半徑上的,而且粒子是分成兩部分的,一部分順時針旋轉,一部分逆時針旋轉。而且粒子顔色是循環變化的,粒子的位置是遊離的,而不是簡單的旋轉。
2.1遊戲對象建立

先在Hierarchy界面建立一個空對象,命名為Main,然後右鍵點選Main,選擇Effects,選擇Particle System,再将生成的對象重命名為Ring。

Unity3d Note8(粒子光環)
2.2圓環制備

建立腳本,命名為MyRing.cs,利用腳本對粒子的行為進行控制。

  • 先提供一些公有變量,友善我們通過外部通過這些參數來設定粒子系統的屬性。
public ParticleSystem _particleSystem;      // 粒子系統對象。
    public int maxParticleNumber = ;       // 發射的最大粒子數
    public float pingPong = f;              // 遊離範圍
    public float particleSize = f;          // 粒子的大小
    public float maxRadius = , minRadius = ; // 粒子旋轉半徑範圍
    public float particleSpeed = f;         // 粒子運動的速度
           
  • 然後寫一些私有變量,用來控制粒子的位置、顔色。
private float[] particleAngles;         // 每個粒子的位置偏移角
    private float[] particleRadius;         // 每個粒子的運動半徑
    private float time = ;
    private ParticleSystem.Particle[] particleArray;
    public Color[] colors = { new Color(, , ), new Color(, , ), new Color(, , ), new Color(, , ), new Color(, , ), new Color(, , ), new Color(, , ), new Color(, , ) };
    private float colorTimeOut = ;
    private Gradient gradient;              // 顔色控制器
    private GradientAlphaKey[] alphaKeys;
    private GradientColorKey[] colorKeys;
           
  • 最後在start函數裡面初始化剛才的那些各種屬性,為那些數組申請記憶體,然後設定各個粒子的初始位置,通過随機生成的半徑、角度來控制粒子的位置。同時初始化梯度顔色控制器。
void Start()
    {
        // 初始化粒子數組、粒子角度數組、粒子半徑數組
        _particleSystem = GetComponent<ParticleSystem>();
        particleArray = new ParticleSystem.Particle[maxParticleNumber];
        particleAngles = new float[maxParticleNumber];
        particleRadius = new float[maxParticleNumber];

        // 初始化粒子系統
        _particleSystem.startSpeed = ;
        _particleSystem.loop = false;
        _particleSystem.maxParticles = maxParticleNumber;
        _particleSystem.Emit(maxParticleNumber);            // 發射粒子
        _particleSystem.GetParticles(particleArray);

        // 初始化各粒子位置
        for (int i = ; i < maxParticleNumber; i++)
        {
            // 随機生成每個粒子距離中心的半徑,同時希望粒子集中于平均半徑附近
            float midRadius = (maxRadius + minRadius) / ;
            float rate1 = Random.Range(, midRadius / minRadius);
            float rate2 = Random.Range(midRadius / maxRadius, );
            particleRadius[i] = Random.Range(minRadius * rate1, maxRadius * rate2);
            // 設定粒子大小
            particleArray[i].startSize = particleSize;
            // 随機生成每個粒子的角度
            particleAngles[i] = Random.Range(, );
            float rad = particleAngles[i] /  * Mathf.PI;
            particleArray[i].position = new Vector3(particleRadius[i] * Mathf.Cos(rad), particleRadius[i] * Mathf.Sin(rad), ); // 放置粒子
        }

        _particleSystem.SetParticles(particleArray, particleArray.Length);

        // 初始化梯度顔色控制器
        alphaKeys = new GradientAlphaKey[];
        alphaKeys[].time = ; alphaKeys[].alpha = ;
        alphaKeys[].time = ; alphaKeys[].alpha = ;
        alphaKeys[].time = ; alphaKeys[].alpha = ;
        alphaKeys[].time = ; alphaKeys[].alpha = ;
        alphaKeys[].time = ; alphaKeys[].alpha = ;
        colorKeys = new GradientColorKey[];
        colorKeys[].time = ; colorKeys[].color = Color.white;
        colorKeys[].time = ; colorKeys[].color = Color.white;
        gradient = new Gradient();
        gradient.SetKeys(colorKeys, alphaKeys);
    }
           

将粒子集中于平均半徑的做法就是像上面一樣,先求出平均半徑,然後取一個位于1和平均半徑與最大/最小半徑的比值的區間的随機數,再乘以最大半徑/最小半徑,作為上限/下限就可以随機生成一些集中于平均半徑的随機數,作為粒子的位置。這個原理是可以通過機率學證明的,再這裡我就不多說了。

2.3旋轉
  • 上面的代碼生成的粒子已經是圓形了,雖然一下子就會散開,但隻要在update裡面限制住每個粒子的位置就行了,而粒子的旋轉也是通過改變粒子的位置來實作的,而在這裡就要将粒子分為兩份,一份逆時針旋轉,一份順時針旋轉,由粒子在粒子數組中的下标來決定。
void Update()
    {
        for (int i = ; i < particleArray.Length; i++)
        {
            // 更改粒子角度(一半在逆時針,一半在順時針)
            particleAngles[i] = (particleAngles[i] + (i %  ==  ? particleSpeed :  - particleSpeed) + ) % ;
            // 設定粒子位置
            float rad = particleAngles[i] /  * Mathf.PI;
            particleArray[i].position = new Vector3(particleRadius[i] * Mathf.Cos(rad), particleRadius[i] * Mathf.Sin(rad), );
        }
        _particleSystem.SetParticles(particleArray, particleArray.Length);
    }
           
  • 令粒子有不同的速度,讓它們看起來更靈動些。

    可以通過改變粒子速度的增量來改變粒子的速度,将其分為10個層級,如下:

  • 給粒子添加粒子波動效果,而且波動是是在一定範圍内的。

    在Update函數的for循環裡面添加這樣的語句,利用unity的PingPong方法實作粒子抖動,PingPong可以使值在一定範圍内波動,具體的描述可以看官方文檔。

time += Time.deltaTime;
particleRadius[i] += (Mathf.PingPong(time / minRadius / maxRadius, pingPong) - pingPong / );
           
  • 更改粒子的顔色,并改變粒子的透明度。

    可以通過更改GradientColorKey的color屬性,然後用Gradient重新渲染一下,再根據位置給每個粒子設定顔色和透明度,代碼如下:

colorKeys[].time = ;
colorKeys[].time = ;
colorKeys[].color = colors[(int)colorTimeOut % colors.Length];
colorKeys[].color = colors[(int)colorTimeOut % colors.Length];
gradient.SetKeys(colorKeys, alphaKeys);
particleArray[i].startColor = gradient.Evaluate(particleAngles[i] / f);
           
2.4改進

這個時候可以看到大緻的粒子效果 —— 一個旋轉而且顔色變幻的圓環了,但是由于unity的粒子系統不夠給力,隻要粒子太小粒子的顔色就暗淡下來了,是以這個圓環很暗,是以為了讓圓環清晰些,我們可以用Glow11插件(聽說還可以用shader方法,但由于我是初學者,還是走一些捷徑吧)。

首先下載下傳Glow11插件,然後,通過Assets -> Import Package->Custom Package,找到Glow11插件,将其導入。

標明錄影機,在Inspector面闆中,在Add Component處,找到Glow11腳本,将其增加到錄影機的Inspector面闆中(記得在腳本那裡打鈎)。

最後找到粒子系統的Renderer處的Material,将其改為Default-Material即可。

3.源代碼

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MyRing : MonoBehaviour
{
    public ParticleSystem _particleSystem;      // 粒子系統對象。
    public int maxParticleNumber = ;       // 發射的最大粒子數
    public float pingPong = f;              // 遊離範圍
    public float particleSize = f;          // 粒子的大小
    public float maxRadius = , minRadius = ; // 粒子旋轉半徑範圍
    public float particleSpeed = f;         // 粒子運動的速度

    private float[] particleAngles;         // 每個粒子的位置偏移角
    private float[] particleRadius;         // 每個粒子的運動半徑
    private float time = ;
    private ParticleSystem.Particle[] particleArray;
    public Color[] colors = { new Color(, , ), new Color(, , ), new Color(, , ), new Color(, , ), new Color(, , ), new Color(, , ), new Color(, , ), new Color(, , ) };
    private float colorTimeOut = ;
    private Gradient gradient;              // 顔色控制器
    private GradientAlphaKey[] alphaKeys;
    private GradientColorKey[] colorKeys;

    // Use this for initialization
    void Start()
    {
        // 初始化粒子數組、粒子角度數組、粒子半徑數組
        _particleSystem = GetComponent<ParticleSystem>();
        particleArray = new ParticleSystem.Particle[maxParticleNumber];
        particleAngles = new float[maxParticleNumber];
        particleRadius = new float[maxParticleNumber];

        // 初始化粒子系統
        _particleSystem.startSpeed = ;
        _particleSystem.loop = false;
        _particleSystem.maxParticles = maxParticleNumber;
        _particleSystem.Emit(maxParticleNumber);            // 發射粒子
        _particleSystem.GetParticles(particleArray);

        // 初始化各粒子位置
        for (int i = ; i < maxParticleNumber; i++)
        {
            // 随機生成每個粒子距離中心的半徑,同時希望粒子集中于平均半徑附近
            float midRadius = (maxRadius + minRadius) / ;
            float rate1 = Random.Range(f, midRadius / minRadius);
            float rate2 = Random.Range(midRadius / maxRadius, f);
            particleRadius[i] = Random.Range(minRadius * rate1, maxRadius * rate2);
            // 設定粒子大小
            particleArray[i].startSize = particleSize;
            // 随機生成每個粒子的角度
            particleAngles[i] = Random.Range(, );
            float rad = particleAngles[i] /  * Mathf.PI;
            particleArray[i].position = new Vector3(particleRadius[i] * Mathf.Cos(rad), particleRadius[i] * Mathf.Sin(rad), ); // 放置粒子
        }

        _particleSystem.SetParticles(particleArray, particleArray.Length);

        // 初始化梯度顔色控制器
        alphaKeys = new GradientAlphaKey[];
        alphaKeys[].time = f; alphaKeys[].alpha = f;
        alphaKeys[].time = f; alphaKeys[].alpha = f;
        alphaKeys[].time = f; alphaKeys[].alpha = f;
        alphaKeys[].time = f; alphaKeys[].alpha = f;
        alphaKeys[].time = f; alphaKeys[].alpha = f;
        colorKeys = new GradientColorKey[];
        colorKeys[].time = ; colorKeys[].color = Color.white;
        colorKeys[].time = ; colorKeys[].color = Color.white;
        gradient = new Gradient();
        gradient.SetKeys(colorKeys, alphaKeys);
    }

    // Update is called once per frame
    void Update()
    {
        colorTimeOut += Time.deltaTime;
        for (int i = ; i < particleArray.Length; i++)
        {
            time += Time.deltaTime;
            // 更改粒子角度(一半在逆時針,一半在順時針,而且速度有10個層級)
            particleAngles[i] = (particleAngles[i] + (i %  + ) * (i %  ==  ? particleSpeed :  - particleSpeed) + ) % ;
            // 更改粒子半徑(造成粒子波動效果,而且波動是在一定範圍内的)
            particleRadius[i] += (Mathf.PingPong(time / minRadius / maxRadius, pingPong) - pingPong / );
            // 更改粒子顔色
            // particleArray[i].startColor = colors[(int)colorTimeOut % colors.Length];
            colorKeys[].time = ;
            colorKeys[].time = ;
            colorKeys[].color = colors[(int)colorTimeOut % colors.Length];
            colorKeys[].color = colors[(int)colorTimeOut % colors.Length];
            gradient.SetKeys(colorKeys, alphaKeys);
            particleArray[i].startColor = gradient.Evaluate(particleAngles[i] / f);
            // 設定粒子位置
            float rad = particleAngles[i] /  * Mathf.PI;
            particleArray[i].position = new Vector3(particleRadius[i] * Mathf.Cos(rad), particleRadius[i] * Mathf.Sin(rad), );
        }
        _particleSystem.SetParticles(particleArray, particleArray.Length);
    }
}