粒子系统

学习了3D游戏的粒子系统,参考了I-remember上的例子光环,决定自己做一个。开始动手之前,需要细心观察并发现,光环分两层,一层顺时针旋转另外一层逆时针旋转,且中间部分粒子较多;粒子在旋转的同时会在光环之间游离。

基础准备

新建空对象ParticleCircle,再建两个子对象Clockwise和Anticlockwise,为其添加Particle System组件,新建ParticleCircle脚本并挂载在子对象上:

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
using UnityEngine;
using System.Collections;
public class CirclePosition
{
public float radius = 0f, angle = 0f, time = 0f;
public (float radius, float angle, float time)
{
this.radius = radius;
this.angle = angle;
this.time = time;
}
}
public class ParticleCircle : MonoBehaviour {
private ParticleSystem particleSys; // 粒子系统
private ParticleSystem.Particle[] particleArr;
private CirclePosition[] circle; // 极坐标数组
public int count = 10000; // 粒子数量
public float size = 3f; // 粒子大小
public float minRadius = 4.0f; // 最小半径
public float maxRadius = 8.0f; // 最大半径
public bool clockwise = true; // 顺时针|逆时针
public float speed = 2f; // 速度
public float pingPong = 0.02f; // 游离范围
private int tier = 10; // 速度差分层数
public Gradient colorGradient; // 颜色控制
void RandomlySpread() // 随机布置粒子位置
{
for (int i = 0; i < count; ++i)
{
float midRadius = (maxRadius + minRadius)/2;
float minRate = Random.Range(1.0f, midRadius/minRadius);
float maxRate = Random.Range(midRadius/maxRadius, 1.0f);
float radius = Random.Range(minRadius * minRate, maxRadius * maxRate);
float angle = Random.Range(0.0f, 360.0f); // 随机每个粒子的角度
float theta = angle/180 * Mathf.PI;
float time = Random.Range(0.0f, 360.0f); // 随机每个粒子的游离起始时间
circle[i] = new CirclePosition(radius, angle, time);
particleArr[i].position = new Vector3(circle[i].radius * Mathf.Cos(theta), 0f, circle[i].radius * Mathf.Sin(theta));
}
particleSys.SetParticles(particleArr, particleArr.Length);
}
void Start () {
particleArr = new ParticleSystem.Particle[count]; // 初始化粒子数组
circle = new CirclePosition[count];
particleSys = this.GetComponent<ParticleSystem>(); // 初始化粒子系统
particleSys.startSpeed = 0;
particleSys.startSize = size;
particleSys.loop = false;
particleSys.maxParticles = count; // 设置最大粒子量
particleSys.Emit(count); // 发射粒子
particleSys.GetParticles(particleArr);
RandomlySpread(); // 初始化各粒子位置
}

旋转粒子

在Update函数里逐渐改变粒子的角度使粒子旋转,其中tier使粒子角度改变量不一致,这样粒子旋转就不会看起来像是图片在旋转:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void Update () { // 通过改变角度使粒子旋转
for (int i = 0; i < count; i++)
{
if (clockwise)
circle[i].angle -= (i%tier + 1)*(speed/circle[i].radius/tier);
else
circle[i].angle += (i%tier + 1)*(speed/circle[i].radius/tier);
circle[i].angle = (360.0f + circle[i].angle)%360.0f; // 保证angle在0~360度
float theta = circle[i].angle/180 * Mathf.PI;
particleArr[i].position = new Vector3(circle[i].radius * Mathf.Cos(theta), 0f, circle[i].radius * Mathf.Sin(theta));
}
particleSys.SetParticles(particleArr, particleArr.Length);
}

游离粒子

Unity的Mathf的PingPong函数使得值在范围内来回变动,使用它让粒子半径来回变动。以下添加到Update函数中:

1
2
circle[i].time += Time.deltaTime;
circle[i].radius += Mathf.PingPong(circle[i].time / minRadius / maxRadius, pingPong) - pingPong / 2.0f;

添加透明度

仔细观察i-remember会发现不同地方亮度不同,也可以说是透明度不同,使用Gradient类可以解决这个问题。一下添加到start函数中:

1
2
3
4
5
6
7
8
9
10
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;
大专栏  粒子系统 class="line">colorGradient.SetKeys(colorKeys, alphaKeys);

然后在Update中根据粒子的角度改变粒子的透明度:

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

修改参数

修改主摄像机到合适位置:

另外一层是逆时针旋转的,因此把clockwise的勾去掉,同时修改参数直到效果满意:

完整代码

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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
using UnityEngine;
using System.Collections;
public class CirclePosition
{
public float radius = 0f, angle = 0f, time = 0f;
public (float radius, float angle, float time)
{
this.radius = radius;
this.angle = angle;
this.time = time;
}
}
public class ParticleCircle : MonoBehaviour {
private ParticleSystem particleSys; // 粒子系统
private ParticleSystem.Particle[] particleArr;
private CirclePosition[] circle; // 极坐标数组
public int count = 10000; // 粒子数量
public float size = 3f; // 粒子大小
public float minRadius = 4.0f; // 最小半径
public float maxRadius = 8.0f; // 最大半径
public bool clockwise = true; // 顺时针|逆时针
public float speed = 2f; // 速度
public float pingPong = 0.02f; // 游离范围
private int tier = 10; // 速度差分层数
public Gradient colorGradient; // 颜色控制
void RandomlySpread() // 随机布置粒子位置
{
for (int i = 0; i < count; ++i)
{
float midRadius = (maxRadius + minRadius)/2;
float minRate = Random.Range(1.0f, midRadius/minRadius);
float maxRate = Random.Range(midRadius/maxRadius, 1.0f);
float radius = Random.Range(minRadius * minRate, maxRadius * maxRate);
float angle = Random.Range(0.0f, 360.0f); // 随机每个粒子的角度
float theta = angle/180 * Mathf.PI;
float time = Random.Range(0.0f, 360.0f); // 随机每个粒子的游离起始时间
circle[i] = new CirclePosition(radius, angle, time);
particleArr[i].position = new Vector3(circle[i].radius * Mathf.Cos(theta), 0f, circle[i].radius * Mathf.Sin(theta));
}
particleSys.SetParticles(particleArr, particleArr.Length);
}
void Start () {
particleArr = new ParticleSystem.Particle[count]; // 初始化粒子数组
circle = new CirclePosition[count];
particleSys = this.GetComponent<ParticleSystem>(); // 初始化粒子系统
particleSys.startSpeed = 0;
particleSys.startSize = size;
particleSys.loop = false;
particleSys.maxParticles = count; // 设置最大粒子量
particleSys.Emit(count); // 发射粒子
particleSys.GetParticles(particleArr);
RandomlySpread(); // 初始化各粒子位置
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);
}
void Update () { // 通过改变角度使粒子旋转
for (int i = 0; i < count; i++)
{
if (clockwise)
circle[i].angle -= (i%tier + 1)*(speed/circle[i].radius/tier);
else
circle[i].angle += (i%tier + 1)*(speed/circle[i].radius/tier);
circle[i].angle = (360.0f + circle[i].angle)%360.0f; // 保证angle在0~360度
float theta = circle[i].angle/180 * Mathf.PI;
particleArr[i].position = new Vector3(circle[i].radius * Mathf.Cos(theta), 0f, circle[i].radius * Mathf.Sin(theta));
circle[i].time += Time.deltaTime; // 使粒子半径波动
circle[i].radius += Mathf.PingPong(circle[i].time/minRadius/maxRadius, pingPong) - pingPong/2.0f;
particleArr[i].color = colorGradient.Evaluate(circle[i].angle/360.0f); // 根据角度改变透明度
}
particleSys.SetParticles(particleArr, particleArr.Length);
}
}

ParticleSea

以下是另外一个利用粒子系统写的粒子海洋,有兴趣可以做做:

原文地址:https://www.cnblogs.com/lijianming180/p/12433283.html