ParticleRing
做完了两三天了,终于挤出时间来写写博客。
我们仿照remember的粒子光环效果。
其中,鼠标悬浮光环中心,光环会收缩,移开后恢复。
参数设置
- Start Lifetime
我们将粒子的生命时间(生命周期)设置为100,让粒子可以较久的保留在界面上,避免圆环上的粒子太过稀疏。 - Start Size
粒子大小,在参数面板设置。 - Start Speed
将粒子的初始速度设为零,我们在代码中会对粒子速度进行设置。 - Max Particles
粒子最大数目,在代码中可以设置。
代码讲解
我们依旧在了解参数面板参数的含义下,使用代码完成任务。首先,将粒子和粒子系统分开,类似 model 与 actionManager 或工厂模式的设计思路。
粒子
我们分解一下粒子状态:初始态,收缩态和收缩前态。在这三个状态下,我们关注粒子的初始发射角度,初始旋转半径,收缩前的旋转半径,收缩后的旋转半径。所以建立如下数据结构:1
2
3
4
5
6
7
8
9
10
11
12
13public class ParticleData {
public float angle; // 粒子初始角度
public float radius; // 当前粒子半径
public float beforeRadius; // 粒子收缩前半径
public float shringRadius; // 粒子收缩后半径
public ParticleData(float angle, float radius, float beforeRadius, float shringRadius) {
this.angle = angle;
this.radius = radius;
this.beforeRadius = beforeRadius;
this.shringRadius = shringRadius;
}
}粒子系统
我们对于粒子系统,需要对粒子的初始状态,收缩状态和收缩钱状态负责。一步步来完成。- 数据变量
我们分析一下各状态下粒子需要的参数,可以知道,我们需要明确粒子数目和各粒子的数据。 - 初始化粒子位置
粒子的位置,需要构成一个圆环,且在圆环中,我们希望粒子尽量集中在中间。我们采用如下方法进行构造:[min, max] => lowerbound = random [min, middle] & upperbound = random [middle, max] => initialRidus = random [lowerbound, upperbound].
这样,我们通过分级随机的方法得到的半径,会较为集中在中部,而避免太过分散的问题。 - 粒子运动
粒子光环应该是有两层旋转,一层顺时针,一层逆时针。我们简化一下,通过扩大粒子数,然后根据下标奇偶性让其按照不同方向旋转运动,这样就解决了两层的问题。 - 粒子收缩
我们通过在圆环中心位置放置一个 cube 充当按钮,并将其放入一个单独的 layer 中,然后在摄像机中,将这个 layer “无视”,这样可以做成浸入式的体验效果。 - 粒子收缩成圆线
我们在确定收缩半径的时候,可以通过再次随机,将粒子收缩的半径范围限制在某个较大范围内,而不是一个小区域中,这样可以避免圆线的问题。
完整代码如下,代码注释表明了代码含义。
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
77public class ParticleRing : MonoBehaviour {
public Camera camera; //摄像机
public ParticleSystem particleRing; //粒子系统
private int particleNum = 10000; //粒子数目
private ParticleSystem.Particle[] particles; //粒子
private ParticleData[] particleDatas; //粒子数据
private float minRadius = 5.0f; //最小半径
private float maxRadius = 10.0f; //最大半径
private bool isShring = false; //是否收缩
private int level = 5;
private float speed = 0.1f;
private float shringSpeed = 2f;
// Use this for initialization
void Start() {
particleRing.maxParticles = particleNum; //设置最大粒子数
particles = new ParticleSystem.Particle[particleNum]; //新建粒子数组
particleDatas = new ParticleData[particleNum]; //新建粒子数据数组
particleRing.Emit(particleNum); //粒子系统发射粒子
particleRing.GetParticles(particles);
//初始化粒子位置
for (int i = 0; i < particleNum; ++i) {
float middleRadius = (maxRadius + minRadius) / 2;
float upperbound = Random.Range(middleRadius, maxRadius);
float lowerbound = Random.Range(minRadius, middleRadius);
float radius = Random.Range(lowerbound, upperbound);
float angle = Random.Range(0.0f, 360.0f);
particleDatas[i] = new ParticleData(angle, radius, radius, radius - 1.5f * (radius / minRadius));
// 随机收缩半径下界,避免收缩后成为圆线
if (particleDatas[i].shringRadius < minRadius + 0.5f) {
float temp = Random.Range(minRadius, minRadius + 0.25f);
particleDatas[i].shringRadius = Random.Range(temp, minRadius + 0.5f);
}
}
}
// Update is called once per frame
void Update() {
for (int i = 0; i < particleNum; ++i) {
//判断是否收缩
if (isShring) {
if (particleDatas[i].radius > particleDatas[i].shringRadius) {
particleDatas[i].radius -=
shringSpeed * (particleDatas[i].radius / particleDatas[i].shringRadius) * Time.deltaTime;
}
} else {
if (particleDatas[i].radius < particleDatas[i].beforeRadius) {
particleDatas[i].radius +=
shringSpeed * (particleDatas[i].beforeRadius / particleDatas[i].radius) * Time.deltaTime;
} else {
particleDatas[i].radius = particleDatas[i].beforeRadius;
}
}
//根据奇偶数判定顺时针或逆时针运动
if (i % 2 == 0) {
particleDatas[i].angle += (i % level + 1) * speed;
} else {
particleDatas[i].angle -= (i % level + 1) * speed;
}
particleDatas[i].angle %= 360;
float rad = particleDatas[i].angle / 180 * Mathf.PI;
particles[i].position =
new Vector3(particleDatas[i].radius * Mathf.Cos(rad), particleDatas[i].radius * Mathf.Sin(rad), 0);
}
particleRing.SetParticles(particles, particleNum);
//收缩射线判断
Ray ray = camera.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
isShring = (Physics.Raycast(ray, out hit) && hit.collider.gameObject.tag == "button") ? true : false;
}
}- 数据变量