学习Unity(10)利用粒子系统制作光环

粒子系统概述

根据官方文档的解释,粒子系统管理大量既小又简单的图片或网格(mesh)一起运动,组成一个整体的效果。比如利用粒子系统可以制作出烟雾效果,每一个粒子是一个微小的烟云的图片,成千上万个这样的粒子就组成了一整块烟雾的效果,用普通的方法就很难做出烟雾的效果。

粒子系统的重要属性

  • 生命周期lifetime:每一个粒子都有一个生命周期,表示粒子被发射(emit)出来以后能存活多长时间。你可以在Inspector中设置Start lifetime来设置粒子的生命周期:


    学习Unity(10)利用粒子系统制作光环_第1张图片
    Inspector中设置lifetime

之所以叫start是因为你设置的只是粒子默认的生命周期,在运行过程中lifetime还有可能被脚本改变。

  • 发射速率emission rate:每秒钟发射多少个粒子。你可以在Inspector中找到Emission模块的rate over time中设置发射速率。

实际发射粒子的时机有一定的随机性,不一定是间隔均匀地发射。

以上两个属性描述的是整个粒子系统的状态。我们还可以控制单个粒子的样式和行为。

  • 粒子个体的属性:速度矢量、颜色、大小、朝向等。有一些属性除了可以设置常量值以外,还可以设置成随着时间变化的值,或者在一定范围内随机的值。你只需要点击输入框右边的下拉三角按钮,就可以改变设置方式。

由于整个粒子系统有很多的属性可以自定义,因此Unity将它划分成了很多个模块(Particle System modules),方便查找。这是粒子系统模块的参考文档。

项目概述

这个项目制作一个简单的粒子光环,光环中的粒子缓慢移动,看起来像太阳系的小行星带。效果尽量类似http://i-remember.fr/en。要实现这个效果,需要使用脚本来控制每一个粒子。

效果图

学习Unity(10)利用粒子系统制作光环_第2张图片

在自己的电脑上运行!

从我的github下载项目资源,将所有文件放进你的项目的Assets文件夹(如果有重复则覆盖),然后在Unity3D中双击“hw9”,就可以运行了!

代码

ParticleStatus 用于记录某个粒子的状态:

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控制一个含有粒子系统的对象,生成一个光环:

using UnityEngine;

public class ParticleHalo : MonoBehaviour
{

    private ParticleSystem particleSys;  // 粒子系统组件
    private ParticleSystem.Particle[] particleArr;  // 粒子数组  
    private ParticleStatus[] StatusArr; // 记录粒子状态的数组
    public int particleNum = 10000; // 粒子数量
    public float minRadius = 8.0f; // 光环最小半径
    public float maxRadius = 12.0f; // 光环最大半径
    public float maxRadiusChange = 0.02f; // 粒子轨道变化的平均值
    public bool clockwise = true;  // 光环是否顺时针旋转
    public float rotateSpeed = 0.3f;  // 光环旋转速度
    public int speedLevel = 5; // 速度有多少个层次
    private NormalDistribution normalGenerator; // 高斯分布生成器
    public Gradient colorGradient;  // 控制粒子的透明度


    void Start()
    {
        particleSys = GetComponent();
        particleArr = new ParticleSystem.Particle[particleNum];
        StatusArr = new ParticleStatus[particleNum];

        var ma = particleSys.main;  // 通过ma来设置粒子系统的maxParticles
        ma.maxParticles = particleNum;

        particleSys.Emit(particleNum);  // 同时发射particleNum个粒子
        particleSys.GetParticles(particleArr);  // 将发射的粒子存在particleArr数组中
        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++)
        {
            // 将所有粒子根据下标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; // 根据粒子的进度,给粒子的半径赋予不同的值,这个值在0与StatusArr[i].radiusChange之间来回摆动

            particleArr[i].position = computePos(StatusArr[i].radius, theta);

            particleArr[i].color = colorGradient.Evaluate(StatusArr[i].angle / 360.0f); // 根据粒子的angle,给粒子赋予不同的透明度(颜色),使某一些角度上的粒子暗一些
        }

        particleSys.SetParticles(particleArr, particleArr.Length);
    }
}

NormalDistribution 用于产生正态分布的随机数,原理是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;
    }
}

代码意义已经在注释中解释


两层光环

为了做出两层光环,只需要将同一份脚本挂载在两个具有Particle System的对象上,然后在Inspector中调整一下参数,就可以实现内层逆时针、外层顺时针、内层快、外层慢、内层稠密、外层稀疏的效果了。

学习Unity(10)利用粒子系统制作光环_第3张图片
外层参数

学习Unity(10)利用粒子系统制作光环_第4张图片
内层参数

你可能感兴趣的:(学习Unity(10)利用粒子系统制作光环)