Unity-3D 粒子光圈效果

这一次要完成的是http://i-remember.fr/en 网站所示的白色圆圈效果。
首先,我们先来看一下它的效果:

一、网站效果展示

二、创建粒子
这里写图片描述

设置相关参数
Unity-3D 粒子光圈效果_第1张图片

把相机背景色调黑
这里写图片描述

三、编写脚本
1、新建脚本:ParticleRotate.cs,并将其拖到Paticle System中。
2、创建粒子数组,初始化。同时我们需要为记录每个粒子的初始角度,初始半径。考虑后续可能每个粒子会有更多属性,所以写了一个class来管理粒子属性

根据下图我们知道,需要随机生成角度以及半径,从而表示出某一个粒子的特定位置。

Unity-3D 粒子光圈效果_第2张图片

    //创建粒子系统,粒子数组,粒子数目,声明粒子环的半径
    public ParticleSystem particleSystem;
    private ParticleSystem.Particle[] particlesArray;
    private particleClass[] particleAttr; //粒子属性数组
    public int particleNum = 10000;

    public class particleClass {
        public float radiu = 0.0f;
        public float angle = 0.0f;
        public particleClass(float radiu_, float angle_)
        {
            radiu = radiu_;
            angle = angle_;
        }
    }
    void Start()
    {
        particlesArray = new ParticleSystem.Particle[particleNum];
        particleSystem.maxParticles = particleNum;
        particleSystem.Emit(particleNum);
        particleSystem.GetParticles(particlesArray);
        for (int i = 0; i < particleNum; i++)
        {//相应初始化操作,为每个粒子设置半径,角度
        }
        //设置粒子
        particleSystem.SetParticles(particlesArray, particleNum);
    }

3、先考虑在最大半径为maxRadius 最小半径为minRadius的区间内随机为每个粒子生成半径

    float randomAngle = Random.Range(0.0f, 360.0f);
    float randomRadius = Random.Range(minRadius, maxRadius);
    particleAttr[i] = new particleClass(randomRadius, randomAngle);
    particlesArray[i].position = new Vector3(randomRadius * Mathf.Cos(randomAngle), randomRadius * Mathf.Sin(randomAngle), 0.0f);

效果如下:
Unity-3D 粒子光圈效果_第3张图片

4、OK接下来要让它动起来~~那么问题来了~怎么动。
我的想法是,把这一堆粒子分成两Part,第一部分顺时针转,第二部分逆时针转。只要在update函数里面更改它的角度就好了。

    public int Part = 2;
    void Update()
    {
        //设置为两部分的粒子,一部分顺时针,一部分逆时针。
        for (int i = 0; i < particleNum; i++)
        {
            if (i % 2 == 0)  particleAttr[i].angle += (i % Part + 1) * speed;
            else particleAttr[i].angle -= (i % Part + 1) * speed;

            particleAttr[i].angle = particleAttr[i].angle % 360;
            float rad = particleAttr[i].angle / 180 * Mathf.PI;
            particlesArray[i].position = new Vector3(particleAttr[i].radiu * Mathf.Cos(rad), particleAttr[i].radiu * Mathf.Sin(rad), 0f);
        }
        particleSystem.SetParticles(particlesArray, particleNum);
    }

动起来的效果:
Unity-3D 粒子光圈效果_第4张图片

5、好吧不得不承认动起来的效果太奇怪了。仔细研究一下就会发现。产生的半径必须是要在[minRadius, maxRadius]区间的没错,但是同时也要更为集中在中间的某一块区域,这样才能实现网站的效果。

这也是我觉得的最有趣的地方。
首先想到的解决方式:正态分布。
不过只有python有相应的正态分布的库。利用c#写一个正态分布太麻烦了。
然后我就找到了box-muller
传送门: http://baike.baidu.com/view/1710258.htm
方法如下:
如果在 (0,1] 值域内有两个独立的随机数字 U1 和 U2,
可以使用以下两个等式中的任一个算出一个正态分布的随机数字 Z:
Z = R * cos( θ )或Z = R * sin( θ )
其中,R = sqrt(-2 * ln(U2)) && θ = 2 * π * U1
正态值 Z 有一个等于 0 的平均值和一个等于 1 的标准偏差,可使用以下等式将 Z 映射到一个平均值为 m、标准偏差为 sd 的统计量 X:X = m + (Z * sd)

然后将该方法写入for循环中。

            // 一种待完善的产生随机粒子的办法 box-muller
            float sita = 2 * Mathf.PI * Random.Range(0, 1);
            float R = Mathf.Sqrt(-2 * Mathf.Log(Random.Range(0, 1)));
            float Z = R * Mathf.Cos(sita);
            float randomRadius = (minRadius + maxRadius) / 2 + Z * 2.5f;
            //print(randomRadius);
            Debug.Log("r = " + R);
            Debug.Log(randomRadius);

什么问题呢? R求出来是无限,等着把Log里面的数值改为0.7,
运行慢不说。10000个粒子unity跑了接近一分钟才运行出来。吓得我以为炸了。
而且效果十分糟糕。
Unity-3D 粒子光圈效果_第5张图片

好吧~我无能为力。然后就看了大神这一块实现的代码。

            // 随机产生每个粒子距离中心的半径,同时粒子要集中在平均半径附近  
            float midRadius = (maxRadius + minRadius) / 2;
            float minRate = Random.Range(1.0f, midRadius / minRadius);
            float maxRate = Random.Range(midRadius / maxRadius, 1.0f);
            float randomRadius = Random.Range(minRadius * minRate, maxRadius * maxRate);

非常巧妙地利用了三个随机函数,实现粒子的集中。
相当于[ [minRadius, midRadius] , [midRadius, maxRadius] ] 这样的区间中产生随机数。自然靠中间的随机数也会更加集中。
真是神奇。

6、那把这部分代码添加到我的代码中之后。实现的效果就很不错啦。

至此实验完成:
下面是完整的代码:

using UnityEngine;
using System.Collections;

public class ParticleRotate : MonoBehaviour
{
    public class particleClass
    {
        public float radiu = 0.0f;
        public float angle = 0.0f;
        public particleClass(float radiu_, float angle_)
        {
            radiu = radiu_;
            angle = angle_;
        }
    }

    //创建粒子系统,粒子数组,粒子数目,声明粒子环的半径
    public ParticleSystem particleSystem;
    private ParticleSystem.Particle[] particlesArray;
    private particleClass[] particleAttr; //粒子属性数组
    public int particleNum = 10000;
    public float minRadius = 5.0f;
    public float maxRadius = 10.0f;
    public int Part = 2;
    public float speed = 0.1f;

    void Start()
    {
        particleAttr = new particleClass[particleNum];
        particlesArray = new ParticleSystem.Particle[particleNum];
        particleSystem.maxParticles = particleNum;
        particleSystem.Emit(particleNum);
        particleSystem.GetParticles(particlesArray);
        for (int i = 0; i < particleNum; i++)
        {   //相应初始化操作,为每个粒子设置半径,角度
            //产生一个随机角度
            float randomAngle = Random.Range(0.0f, 360.0f);

            // 随机产生每个粒子距离中心的半径,同时粒子要集中在平均半径附近  
            float midRadius = (maxRadius + minRadius) / 2;
            float minRate = Random.Range(1.0f, midRadius / minRadius);
            float maxRate = Random.Range(midRadius / maxRadius, 1.0f);
            float randomRadius = Random.Range(minRadius * minRate, maxRadius * maxRate);

            ////一种待完善的产生随机粒子的办法 box-muller
            //float sita = 2 * Mathf.PI * Random.Range(0, 1);
            //float R = Mathf.Sqrt(-2 * Mathf.Log(0.7f));
            //float Z = R * Mathf.Cos(sita);
            //float randomRadius = (minRadius + maxRadius) / 2 + Z * 2.5f;
            //print(randomRadius);
            //Debug.Log("r = " + R);
            //Debug.Log(randomRadius);

            //粒子属性设置
            particleAttr[i] = new particleClass(randomRadius, randomAngle);
            particlesArray[i].position = new Vector3(randomRadius * Mathf.Cos(randomAngle), randomRadius * Mathf.Sin(randomAngle), 0.0f);
        }
        //设置粒子
        particleSystem.SetParticles(particlesArray, particleNum);
    }


    void Update()
    {
        //设置为两部分的粒子,一部分顺时针,一部分逆时针。
        for (int i = 0; i < particleNum; i++)
        {
            if (i % 2 == 0) particleAttr[i].angle += (i % Part + 1) * speed;
            else particleAttr[i].angle -= (i % Part + 1) * speed;

            //根据新的角度重新设置位置
            particleAttr[i].angle = particleAttr[i].angle % 360;
            float rad = particleAttr[i].angle / 180 * Mathf.PI;
            particlesArray[i].position = new Vector3(particleAttr[i].radiu * Mathf.Cos(rad), particleAttr[i].radiu * Mathf.Sin(rad), 0f);
        }
        particleSystem.SetParticles(particlesArray, particleNum);
    }
}

你可能感兴趣的:(大二)