Unity学习笔记(4)-----粒子效果的实现

Unity学习笔记(4)—–粒子效果的实现

一.效果展示

下面用若干张张动图展示效果:

大概就是这样,并不是很难。 实际效果要比图中的好一点(顺畅得多)。

实现步骤

大致可以分为如下几个步骤,然后逐个实现就可以了:

  1. 生产粒子
  2. 设置粒子初始属性(位置, 颜色, 速度等)
  3. 控制粒子旋转
  4. 改变粒子颜色
  5. 让颜色均匀旋转

    下面分步骤说明。

1.生产粒子

首先我们需要一个ParticleSystem的组件来管理这个粒子系统。 再让组件附着在一个空对象上即可。结构如图:
Unity学习笔记(4)-----粒子效果的实现_第1张图片

Unity学习笔记(4)-----粒子效果的实现_第2张图片

如图中那样, 我们在这个含有particle System的对象上挂载脚本, 准备对这个粒子系统进行控制。

在写脚本之前, 先对Particle System 进行一些必要的设置, 这样可以在测试脚本的时候看起来更舒服 :), 如图:
Unity学习笔记(4)-----粒子效果的实现_第3张图片

嘛, 由于我们都是代码控制, 下面的属性几乎都可以不勾选, renderer除外。 记得给renderer设置一个好看的Material啊 : )
Unity学习笔记(4)-----粒子效果的实现_第4张图片

这些都是为粒子能够正常产生做准备。 这些做好后, 就可以开始写脚本控制了, 产生粒子的相关代码如下:(只是产生的话, 超简单!)

using UnityEngine;
using System.Collections;
public class ParticleSea : MonoBehaviour {

    public ParticleSystem particleSystem;
    private ParticleSystem.Particle[] particlesArray;
    public int seaResolution = 25;
    void Start() {
        particlesArray = new ParticleSystem.Particle[seaResolution * seaResolution];
        particleSystem.maxParticles = seaResolution * seaResolution;
        particleSystem.Emit(seaResolution * seaResolution);
        particleSystem.GetParticles(particlesArray);
    }
}

这些变量的含义:

particle System: 用来得到这个Object的particle System(在Inspector中记得把当前的Particle System 拖给脚本变量)
particlesArray: 用来存放粒子系统所产生的粒子的数组。
seaResolution: 粒子海洋的长度(实际粒子数是这个数的平方)。

Ps : 之所以不改为直接用粒子数表示有两个原因:

  1. 之前我参考的教程的粒子实现时用的这个, 我写这个的时候因为懒直接在那个的基础上做了 ~。~
  2. 事实上写完想改的时候发现这样可以给粒子简单的分层。有一定的可拓展性(好吧其实没有什么用)

In all, 这样做之后创建过程就完成了。

2.设置粒子初始属性

首先考虑我们需要粒子的哪些属性:

  1. 粒子速度 speed
  2. 粒子旋转的半径 radius
  3. 粒子当前旋转角度 angle
ps:粒子位置是可以通过 radius 和 angle 确定的。

考虑到这些, 创建一个类用于表示粒子的属性是个看起来不错的选择。代码如图:

public class particleSettings{
    public float angle { get; set; }
    public float radius{ get; set;}
    public float speed { get; set; }
    public particleSettings(float r) {
        this.radius = r;
        this.angle = Random.value * 2 * Mathf.PI;
        this.speed = Random.value * Mathf.Sqrt(radius);
    }
    public Vector3 getPosition() {
        return radius * new Vector3 (Mathf.Cos(angle), 0, Mathf.Sin(angle));
    }
    public void rotate() {
        this.angle += Time.deltaTime * speed / 10;
        if (this.angle > 2 * Mathf.PI)
            this.angle -= 2 * Mathf.PI;
        this.radius += Random.value * 0.2f - 0.1f;
        if (this.radius > ParticleSea.MaxRadius)
            this.radius = ParticleSea.MaxRadius;
        if (this.radius < ParticleSea.MinRadius)
            this.radius = ParticleSea.MinRadius;
    }
}

方法getPosition就是根据radius和angle计算当前位置, 而rotate则是计算经过一帧的rotate后,该粒子的新参数。这里设置了一个MaxRadius和MinRadius, 是保证粒子的活动范围在一个圆环内。

很明显这样写之后, 我们的主类代码要发生相应变化, 如下:

using UnityEngine;
using System.Collections;

public class particleSettings{
    public float angle { get; set; }
    public float radius{ get; set;}
    public float speed { get; set; }
    public particleSettings(float r) {
        this.radius = r;
        this.angle = Random.value * 2 * Mathf.PI;
        this.speed = Random.value * Mathf.Sqrt(radius);
    }
    public Vector3 getPosition() {
        return radius * new Vector3 (Mathf.Cos(angle), 0, Mathf.Sin(angle));
    }
    public void rotate() {
        this.angle += Time.deltaTime * speed / 10;
        if (this.angle > 2 * Mathf.PI)
            this.angle -= 2 * Mathf.PI;
        this.radius += Random.value * 0.2f - 0.1f;
        if (this.radius > ParticleSea.MaxRadius)
            this.radius = ParticleSea.MaxRadius;
        if (this.radius < ParticleSea.MinRadius)
            this.radius = ParticleSea.MinRadius;
    }
}

public class ParticleSea : MonoBehaviour {

    public ParticleSystem particleSystem;
    private ParticleSystem.Particle[] particlesArray;
    private particleSettings[] psetting;
    public int seaResolution = 25;
    public static float MaxRadius = 120f;
    public static float MinRadius = 50f;
    public float radius = 100.0f;
    public Gradient colorGradient;

    void Start() {
        particlesArray = new ParticleSystem.Particle[seaResolution * seaResolution];
        psetting = new particleSettings[seaResolution * seaResolution];
        particleSystem.maxParticles = seaResolution * seaResolution;
        particleSystem.Emit(seaResolution * seaResolution);
        particleSystem.GetParticles(particlesArray);
        setInitialPosition ();

    }
    void Update() {
        RotateParticles ();
        changeColor ();
        particleSystem.SetParticles(particlesArray, particlesArray.Length);
    }
}

这里我将整个代码结构贴出了。后续步骤就是对这个结构中功能函数实现。那么开始实现的第一步: 设置初始粒子的属性。这里对应代码中的 setInitialPosition () 函数
很明显, 我们只要把particleSetting按照写好的构造函数构建出来, 直接设置其位置就可以了。
函数实现如下:

    void setInitialPosition () {
        for (int i = 0; i < seaResolution; i++) {
            for (int j = 0; j < seaResolution; j++) {
                psetting[i * seaResolution + j] = new particleSettings(radius);
                particlesArray [i * seaResolution + j].position = psetting[i * seaResolution + j].getPosition();
            }
        }
        particleSystem.SetParticles(particlesArray, particlesArray.Length);
    }
ps: 在对数组中粒子修改之后记得要set到Particle System 中去 :)

第二步也就完成了。

3.控制粒子旋转

这一步就是补充上面代码中的 RotateParticles() 函数。
因为之前在particleSettings类中, 我们已经写好这个方法, 所以直接用就可以了。
代码如下:

    void RotateParticles () {
        for (int i = 0; i < seaResolution; i++) {
            for (int j = 0; j < seaResolution; j++) {
                psetting [i * seaResolution + j].rotate ();
                particlesArray [i * seaResolution + j].position = psetting [i * seaResolution + j].getPosition ();
            }
        }
    }

第三步也就完成了:) 做到这里差不多可以看一下效果了, 把update中的changeColor()先注释掉, 运行。

大概是这样的效果:

下面是加特效时间hh。

4.改变粒子颜色

这要用到类里面的 public Gradient colorGradient 这个成员。
首先到Inspector里, 看看这是个什么:
Unity学习笔记(4)-----粒子效果的实现_第5张图片

中央的这根色彩轴按照从左到右,Location值从小到大设置相应的色彩。在这里, Unity帮我们自动渐变了, 我们要做的就是设置那几个关键Location的色彩(和动画的关键帧的作用很像!)
上面那排红色箭头指的是当前Location的Alpha值(透明度), 下面黑色箭头是设置该Location的颜色。
在轴的上方或下方, 右键, 可以自行添加关键位置。(Alpha和Color可以分开设置关键位置)
做好了自己的调色板之后,点下方的“New”可以保存。

然后我们要做的, 就是写代码控制Gradient的行为就可以了。
主要通过 colorGradient.Evaluate(float)来得到相应的调色板的颜色。
先写这样的代码试试看颜色效果:

    void changeColor () {
        for (int i = 0; i < seaResolution; i++) {
            for (int j = 0; j < seaResolution; j++) {
                particlesArray [i * seaResolution + j].color = colorGradient.Evaluate (Random.value);
            }
        }
    }

如果, 之前的Gradient用的和我上面图中一样的话 大致应该是这样的效果:

黄色是红和绿混合的结果~ 这图让我想到显像管电视机…
接下来继续加特效~

5.让颜色均匀旋转

其实这步就是对刚才的changeColor ()函数改造就行了, 很明显 这样直接Random效果 不会很好.
这里其实就是让填入Evaluate的值, 显然一定和angle相关, 但是又不仅和Angle相关。 如果仅仅和angle相关, 那么其实是得到一圈固定的颜色, 而不能旋转。
我的做法是, 加入一个时间变量, 和angle值共同决定value。 一行代码胜千言:

    void changeColor () {
        for (int i = 0; i < seaResolution; i++) {
            for (int j = 0; j < seaResolution; j++) {
                float value = (Time.realtimeSinceStartup - Mathf.Floor (Time.realtimeSinceStartup));
                value+= psetting [i * seaResolution + j].angle / 2 / Mathf.PI;
                while (value > 1)
                    value--;
                particlesArray[i * seaResolution + j].color = colorGradient.Evaluate(value);
                //particlesArray [i * seaResolution + j].color = colorGradient.Evaluate (Random.value);
            }
        }
    }

至此效果就都有了~ 如果调色板用的是上面那个Red-Green-Red, 那么效果如下:

Unity学习笔记(4)-----粒子效果的实现_第6张图片

通过Alpha值的组合, 可以产生更多的炫酷效果~ 例如只设置Alpha不改color, 两端alpha设置为255, 中间设置为0, 可以看到明显的明暗相间效果:)

最后填个坑,关于拓展性的问题。

事实上 seaResolution 相当于对粒子进行分层了, 但是简单的直接设置为粒子数然后之后用求余方法好像是等效的orz… 就简单举一个例子吧:
重写这个方法如下:

    void RotateParticles () {
        for (int i = 0; i < seaResolution; i++) {
            for (int j = 0; j < seaResolution; j++) {
                if(Random.Range(0, seaResolution) < 2 * i) psetting [i * seaResolution + j].rotate ();
                particlesArray [i * seaResolution + j].position = psetting [i * seaResolution + j].getPosition ();
            }
        }
    }  

比起之前加了个对i的判断,进一步操作罢了。 实用性不强。 估计还是要改成直接用粒子数好一点…

不过, 把刚才做好的这个做成prefab, 然后多弄几个到画面中来, 感觉还是挺棒的(只要修改radius和seaResolution值就好), 如下:

还是那句话: 真实效果更好看, 更流畅…

到这之后,然后大家就可以随意发挥了,自己设置好看的调色板, 应该效果还会更好一点。

The End.

你可能感兴趣的:(Unity3D学习,Unity,C#)