unity3d——粒子光环效果

目标效果

I-remember的主页有一款效果很棒的粒子光环效果,这是通过JS实现的。传送门

效果如下图所展,最近学习了Unity的粒子系统Particile System,下面将尝试使用粒子系统实现这一功能。

效果分析

仔细观察硬格可以发现,这个粒子效果有两层。一层是范围比较广、粒子比较稀疏、运动较慢的外环。一层是范围比较窄、粒子比较集中、运动较快的内环。且各个粒子之间的运动比较独立并不是简单的绕圈。基本实现的思路就是写一个脚本,挂载在粒子系统上,设置不同的参数实现内外环。

实现

步骤一

新建工程和空场景,新建一个空的游戏对象放在原点。作为粒子光环的基准点。接下来创建一个C#脚本并命名为ParticleCircle 。在空对象下新建两个粒子系统作为子对象,分别命名为outer和inner。下面的操作可以先对一个对象进行,另一个对象只需要进行拷贝再修改参数即可。

先设置了粒子系统、粒子数组、粒子数目等三个公有变量。将脚本分别挂载到子对象上。创建粒子并把他们放进粒子系统。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using UnityEngine;
using System.Collections;
public class ParticleCircle : MonoBehaviour {
public int count = 10000;
public ParticleSystem particleSys;
public ParticleSystem.Particle[] particlesArray;
// Use this for initialization
void Start () {
particlesArray = new ParticleSys.Particle[count];
particleSys.maxParticles = count;
particleSys.Emit(count);
particleSys.GetParticles(particlesArray);
}
// Update is called once per frame
void Update () {
}
}

并将粒子系统拖入脚本的粒子系统变量上。

步骤二

我们先为各个粒子分配位置,分配的方法就是随机生成角度和半径,生成角度的范围就是0~360度(当然需要转换成弧度制),生成半径的范围就是在中间半径的正负distance区间内,使粒子能集中在这个范围内。x轴的大小通过半径乘以cos弧度来计算,y轴的大小通过半径乘以sin弧度来计算。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class ParticleCircle : MonoBehaviour {
public int count = 10000;
public float distance = 0.5f; // 环的大小
public float midradius = 5; // 环中间半径
public ParticleSystem particleSys;
public ParticleSystem.Particle[] particlesArray;
// Use this for initialization
void Start () {
particlesArray = new ParticleSystem.Particle[count];
particleSys.maxParticles = count;
particleSys.Emit(count);
particleSys.GetParticles(particlesArray);
// 为每个粒子初始化位置,并且将位置存放入
for (int i = 0; i < count; i++) {
float temp_radius = Random.Range (midradius - distance, midradius + distance);
float temp_angle = Random.Range (0.0f, 360.0f);
float theta = temp_angle / 180 * Mathf.PI;
particlesArray [i].position = new Vector3 (Mathf.Cos (theta) * temp_radius, Mathf.Sin (theta) * temp_radius, 0);
}
particleSys.SetParticles(particlesArray, particlesArray.Length);
}
// Update is called once per frame
void Update () {
}
}

完成了函数的构造之后,我们需要对粒子的一些参数进行设置。主要就是调整lifetime为100,size为0.1,emission rate为0,取消looping,取消play on awake。

再对摄像机的参数进行设置,把背景调整为黑色,高度调整为0就可以了。

下面来看看效果如何。

已经有了一个初步的形状了,接下来就是要让粒子们动起来就行了。

步骤三

要让粒子动起来,需要在update函数里面不断更新它的位置。但是每一个粒子只存储了自己个position而position里面只包含了x、y、z坐标,粒子要做的是圆周运动,随着时间线性改变的应该是角度。所以我们需要设置一个结构体来存储每一个粒子的角度、半径等参数。

1
2
3
4
5
6
7
8
9
10
11
12
using Com.myCircle;
namespace Com.myCircle {
public class CirclePosition {
public float radius = 0f;
public float angle = 0f;
public CirclePosition(float radius, float angle) { // 构造函数用来实例化对象
this.angle = angle; // 角度
this.radius = radius; // 半径
}
}
}

当然我们要需要在开始的时候对这个数组进行声明和实例化,初始化粒子位置的时候,也对这个数组中的元素进行实例化。

1
2
3
private CirclePosition[] positionArray; // 类的变量声明加入
positionArray = new CirclePosition[count]; // start中加入
positionArray [i] = new CirclePosition (temp_radius, temp_angle); // start的for循环中加入
1
2
3
4
5
6
7
8
void Update () {
for (int i = 0; i < count; i++) {
positionArray [i].angle -= 0.1f;
float theta = positionArray [i].angle / 180 * Mathf.PI;
particlesArray [i].position = new Vector3 (Mathf.Cos (theta) * positionArray[i].radius, Mathf.Sin (theta) * positionArray[i].radius, 0);
}
particleSys.SetParticles(particlesArray, particlesArray.Length);
}

在update更新每一个粒子的位置,效果如下:

有一点像单纯整张的图片在旋转,效果不是很好。应该给粒子分不同的层,不同的层上移动速度不同,而且半径也会发生改变,这样子效果可能会更好。

改进update如下:

效果稍微好一点了,但是粒子的大小和数目还是需要在后面再继续修改一下的。

步骤四

光效部分,原效果之中,环不同位置的亮度是不同的。这里就使用Gradient类来实现渐变的透明度。

透明度渐变的设置通过代码来设置会比手动调方便一些

在start中声明 public Gradient colorGradient; 后加入

1
2
3
4
5
6
7
8
9
10
11
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);

在update中更新

1
particlesArray [i].color = colorGradient.Evaluate(positionArray [i].angle / 360.0f);

步骤五

调整粒子参数,拷贝内层对象修改参数得到两个层即可。

最终效果

实现还是比较粗略,没有原效果来的好,太像一个环了。

代码

ParticleCircle.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
using UnityEngine;
using System.Collections;
using Com.myCircle;
namespace Com.myCircle {
public class CirclePosition {
public float radius = 0f;
public float angle = 0f;
public CirclePosition(float radius, float angle) { // 构造函数用来实例化对象
this.angle = angle; // 角度
this.radius = radius; // 半径
}
}
}
public class ParticleCircle : MonoBehaviour {
public int count = 10000;
public float distance = 0.5f; // 环的大小
public float midradius = 5; // 环中间半径
public int layer = 10; // 不同层的粒子速度不同
public ParticleSystem particleSys;
public ParticleSystem.Particle[] particlesArray;
private CirclePosition[] positionArray;
public Gradient colorGradient;
// Use this for initialization
void Start () {
particlesArray = new ParticleSystem.Particle[count];
positionArray = new CirclePosition[count];
particleSys.maxParticles = count;
particleSys.Emit(count);
particleSys.GetParticles(particlesArray);
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);
// 为每个粒子初始化位置,并且将位置存放入
for (int i = 0; i < count; i++) {
float temp_radius = Random.Range (midradius - distance, midradius + distance);
float temp_angle = Random.Range (0.0f, 360.0f);
float theta = temp_angle / 180 * Mathf.PI;
positionArray [i] = new CirclePosition (temp_radius, temp_angle);
particlesArray [i].position = new Vector3 (Mathf.Cos (theta) * temp_radius, Mathf.Sin (theta) * temp_radius, 0);
}
particleSys.SetParticles(particlesArray, particlesArray.Length);
}
// Update is called once per frame
void Update () {
for (int i = 0; i < count; i++) {
positionArray [i].angle -= 0.1f * (i % layer + 1);
// 保证angle在0~360度
positionArray[i].angle = (360.0f + positionArray [i].angle) % 360.0f;
float theta = positionArray [i].angle / 180 * Mathf.PI;
particlesArray [i].position = new Vector3 (Mathf.Cos (theta) * positionArray[i].radius, Mathf.Sin (theta) * positionArray[i].radius, 0);
particlesArray [i].color = colorGradient.Evaluate(positionArray [i].angle / 360.0f);
}
particleSys.SetParticles(particlesArray, particlesArray.Length);
}
}

参考

Unity3D学习笔记(9)—— 粒子光环

Unity制作神奇的粒子海洋!

您的支持将鼓励我继续创作!