Skip to content

学习笔记11-粒子光环制作

要求

参考 http://i-remember.fr/en 这类网站,使用粒子流编程控制制作一些效果, 如“粒子光环”

实现效果

演示视频

yanshi

实现过程

设计思路

仔细观察I Remember的粒子光环,我们可以看到其实际上可以分成较密集的一部分以及较为稀疏的一部分,而密集的窄环是有一个对称的缺口的。而其光环会根据不同情况收缩。

所以我们在处理的时候,我们将我们的Particle数组分成前和后两部分来进行处理。

if(i < particleNum * 1 / 2) { //将前半部分用于较宽的那个环 
    ...
} else { //后半部分用于窄环
    ...
}

我们只需控制两部分分布的半径既可以实现其分布稀疏以及密集。

那么,窄环的较稀疏的缺口怎么做呢,其实也不难,只用做好angle的分布就可以了,代码如下。

if(i < particleNum * 1 / 2) { //将前半部分用于较宽的那个环 
    maxR = outMaxR;
    minR = outMinR;
    randomAngle = Random.Range(0.0f, 360.0f);
} else { //后半部分用于窄环,根据I Remember,窄环带缺口,因此我们设置一半向0度集中、一半向180度集中,使得90度和-90度形成两个对称缺口
    maxR = inMaxR;
    minR = inMinR;
    float minAngle = Random.Range(-90f, 0.0f);
    float maxAngle = Random.Range(0.0f, 90f);
    float angle = Random.Range(minAngle, maxAngle);

    randomAngle = i % 2 == 0 ? angle : angle - 180; //利用对称来设置另一半粒子
}

以上时一些基本的思路下面我们说说另外一些关键的部分

设置粒子的各属性

屏幕快照 2018-05-28 下午9.15.18

粒子转动

这部分就是通过窄环,宽环调节转动(angle)变化的幅度

// 窄环转动速度更快
if (i > particleNum * 1 / 2)
    speed = 0.1f;
else
    speed = 0.05f;
posArray[i].angle -=  speed;
posArray[i].angle = posArray[i].angle % 360;
... 
particleArray[i].position = new Vector3(posArray[i].cur_r * Mathf.Cos(rad), posArray[i].cur_r * Mathf.Sin(rad), 0f);

粒子的缩放

当用户点击Change按钮后,我们根据flag来设置粒子的半径来控制粒子的"收缩"。

if(flag == 0) { //粒子向中间收缩 
    if (posArray[i].cur_r > posArray[i].combine_r + 0.05f) {
    //两层环的收缩速度不同
    if(i < particleNum * 1 / 2)
        posArray[i].cur_r -=  2.0f * Time.deltaTime;
    else
        posArray[i].cur_r -= Time.deltaTime;
} else if(posArray[i].cur_r < posArray[i].combine_r - 0.05f) {
    if (i < particleNum * 1 / 2)
        posArray[i].cur_r += 2.0f * Time.deltaTime;
    else
        posArray[i].cur_r += Time.deltaTime;
    }
} else if(flag == 1) { //粒子范围扩大 
    if (posArray[i].cur_r < posArray[i].r - 0.05f) {
        if (i < particleNum * 1 / 2)
            posArray[i].cur_r += 2.0f * Time.deltaTime;
        else
            posArray[i].cur_r += Time.deltaTime;
    } else if (posArray[i].cur_r > posArray[i].r + 0.05f) {
        if (i < particleNum * 1 / 2)
            posArray[i].cur_r -= 2.0f* Time.deltaTime;
        else
            posArray[i].cur_r -= Time.deltaTime;
        }
}

完整代码

public class RingOfParticle : MonoBehaviour {
    public class particlePos {
        public float r = 0.0f; //初始化半径
        public float combine_r = 0.0f; //集合后的半径
        public float cur_r = 0.0f; //记录粒子当前时刻半径
        public float angle = 0.0f;

        public particlePos(float radiu, float angle, float combine) {
            r = radiu;
            this.angle = angle;
            combine_r = combine;
            cur_r = radiu;
        }
    }

    public ParticleSystem particleSystem;
    public int particleNum = 10000;
    public float outMinR = 5.0f;
    public float outMaxR = 10.0f;
    public float inMinR = 6.0f;
    public float inMaxR = 9.0f;

    public float speed = 0.1f;

    public int flag;


    private ParticleSystem.Particle[] particleArray;
    private particlePos[] posArray;

    void OnGUI() {
        if (GUI.Button(new Rect(0, 15, 100, 30), "Change")) {
            flag = (flag == -1)? 0: 1 - flag;
        }
    }

    // Use this for initialization
    void Start () {
        flag = -1;
        posArray = new particlePos[particleNum];
        particleArray = new ParticleSystem.Particle[particleNum];
        particleSystem.maxParticles = particleNum;
        particleSystem.Emit(particleNum);
        particleSystem.GetParticles(particleArray);

        for (int i = 0; i < particleNum; i++) {   
            float randomAngle; // 设置粒子的随机角度
            float maxR, minR; // 最大最小半径供随机

            if(i < particleNum * 1 / 2) { //将前半部分用于较宽的那个环 
                maxR = outMaxR;
                minR = outMinR;
                randomAngle = Random.Range(0.0f, 360.0f);
            } else { //后半部分用于窄环,根据I Remember,窄环带缺口,因此我们设置一半向0度集中、一半向180度集中,使得90度和-90度形成两个对称缺口
                maxR = inMaxR;
                minR = inMinR;
                float minAngle = Random.Range(-90f, 0.0f);
                float maxAngle = Random.Range(0.0f, 90f);
                float angle = Random.Range(minAngle, maxAngle);

                randomAngle = i % 2 == 0 ? angle : angle - 180; //利用对称来设置另一半粒子
            }

            float midRadius = (maxR + minR) / 2;
            float min = Random.Range(minR, midRadius);
            float max = Random.Range(midRadius, maxR);
            float randomRadius = Random.Range(min, max);
            float combineRadius;

            // 平均半径以外的粒子集合半径调小,使缩小时移动的距离少一些
            if (randomRadius > midRadius)
                combineRadius = randomRadius - (randomRadius - midRadius) / 2;
            else
                combineRadius = randomRadius - (randomRadius - midRadius) * 3 / 4;

            // 设置粒子的属性
            posArray[i] = new particlePos(randomRadius, randomAngle, combineRadius);
            particleArray[i].position = new Vector3(randomRadius * Mathf.Cos(randomAngle), randomRadius * Mathf.Sin(randomAngle), 0.0f);
        }
        particleSystem.SetParticles(particleArray, particleNum);
    }

    // Update is called once per frame
    void Update () {
        for (int i = 0; i < particleNum; i++)
        {
            // 小圈转动速度更快
            if (i > particleNum * 1 / 2)
                speed = 0.1f;
            else
                speed = 0.05f;
            posArray[i].angle -=  speed;
            posArray[i].angle = posArray[i].angle % 360;
            float rad = posArray[i].angle / 180 * Mathf.PI;

            // 根据flag, 判断粒子的动态,改变粒子的现在的半径
            if(flag == 0) { //粒子向中间收缩 
                if (posArray[i].cur_r > posArray[i].combine_r + 0.05f) {
                    //两层环的收缩速度不同
                    if(i < particleNum * 1 / 2)
                        posArray[i].cur_r -=  2.0f * Time.deltaTime;
                    else
                        posArray[i].cur_r -= Time.deltaTime;
                } else if(posArray[i].cur_r < posArray[i].combine_r - 0.05f) {
                    if (i < particleNum * 1 / 2)
                        posArray[i].cur_r += 2.0f * Time.deltaTime;
                    else
                        posArray[i].cur_r += Time.deltaTime;
                }
            } else if(flag == 1) { //粒子范围扩大 
                if (posArray[i].cur_r < posArray[i].r - 0.05f) {
                    if (i < particleNum * 1 / 2)
                        posArray[i].cur_r += 2.0f * Time.deltaTime;
                    else
                        posArray[i].cur_r += Time.deltaTime;
                } else if (posArray[i].cur_r > posArray[i].r + 0.05f) {
                    if (i < particleNum * 1 / 2)
                        posArray[i].cur_r -= 2.0f* Time.deltaTime;
                    else
                        posArray[i].cur_r -= Time.deltaTime;
                }
            }

            // 通过curR和新的角度设置粒子的位置
            particleArray[i].position = new Vector3(posArray[i].cur_r * Mathf.Cos(rad), posArray[i].cur_r * Mathf.Sin(rad), 0f);
        }
        particleSystem.SetParticles(particleArray, particleNum);
    }
}