unity3d——粒子光环效果

目标效果

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

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

效果分析

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

实现

步骤一

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

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

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

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

步骤二

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

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

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

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

下面来看看效果如何。

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

步骤三

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

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

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

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

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

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

改进update如下:

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

步骤四

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

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

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

1
GradientAlphaKey[] alphaKeys = new GradientAlphaKey[5];  
2
alphaKeys[0].time = 0.0f; alphaKeys[0].alpha = 1.0f;  
3
alphaKeys[1].time = 0.4f; alphaKeys[1].alpha = 0.4f;  
4
alphaKeys[2].time = 0.6f; alphaKeys[2].alpha = 1.0f;  
5
alphaKeys[3].time = 0.9f; alphaKeys[3].alpha = 0.4f;  
6
alphaKeys[4].time = 1.0f; alphaKeys[4].alpha = 0.9f;  
7
GradientColorKey[] colorKeys = new GradientColorKey[2];  
8
colorKeys[0].time = 0.0f; colorKeys[0].color = Color.white;  
9
colorKeys[1].time = 1.0f; colorKeys[1].color = Color.white;  
10
11
colorGradient.SetKeys(colorKeys, alphaKeys);

在update中更新

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

步骤五

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

最终效果

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

代码

ParticleCircle.cs

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

参考

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

Unity制作神奇的粒子海洋!

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