这一次做的是用粒子流编程做一个粒子系统,参考了师兄的博客。
效果展示:
基本配置
首先,打开Unity并创建一个空的对象,这里我命名为ParticalHalo,然后在ParticalHalo下创建两个空对象,分别为inner和outer,并为两个对象添加粒子系统Particle System。到这里,基本的配置就差不多了,接下来就是脚本的控制了。
脚本控制
由于两个空对象inner和outer使用同一个脚本控制,因此只用写一个脚本即可。创建脚本命名为ParticalHalo.cs。
首先添加粒子系统,粒子数组以及粒子属性的数组:
private ParticleSystem particleSys; // 粒子系统
private ParticleSystem.Particle[] particleArr; // 粒子数组
private CirclePosition[] circle; //粒子属性
然后我们从简单的开始,先定义该粒子属性的类:
public class CirclePosition
{
public float radius = 0f, angle = 0f, time = 0f;
public CirclePosition(float radius, float angle, float time)
{
this.radius = radius;
this.angle = angle;
this.time = time;
}
}
这个类中定义了每个粒子的半径,角度,游离的起始时间。这个类会在初始化每个粒子位置时用到。
接下来要定义一堆东西,包括粒子数量,粒子的运动半径范围,粒子大小,运动方向等:
public int count = 10000;
public float size = 0.06f;
public float minRadius = 5.0f;
public float maxRadius = 12.0f;
public bool clockwise = true;
public float speed = 2f;
public float pingPong = 0.02f; // 游离范围
public Gradient colorGradient;
值得一提的是Gradient是unity控制渐变的类,定义一个类的实例,在之后的光环渐变中用到,这些属性都定义成public以便于实时调节和设定。
之后可以对粒子系统进行初始化了:
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(); // 初始化各粒子位置
}
这些粒子系统的属性都可以在UI界面找到,这里只是用脚本控制。
然后实现其中的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);
用极坐标控制每个粒子的位置,保证每个粒子在平均半径附近。
这样就基本上固定了粒子的位置,大概是一个圆吧,然后就需要让每个粒子动起来,绕着圆旋转,这一步在update中实现:
for (int i = 0; i < count; i++)
{
if (clockwise) // 顺时针旋转
circle[i].angle -= Time.deltaTime * speed / 10;
else // 逆时针旋转
circle[i].angle += Time.deltaTime * speed / 10;
// 保证angle在0~360度
circle[i].angle = (360.0f + circle[i].angle) % 360.0f;
circle[i].time += Time.deltaTime;
circle[i].radius += Mathf.PingPong(circle[i].time / minRadius / maxRadius, pingPong) - pingPong / 2.0f;
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));
}
其中
Mathf.PingPong(circle[i].time / minRadius / maxRadius, pingPong) - pingPong / 2.0f;
这段语句保证了粒子可以在最大半径和最小半径之间游动,而不必非要绕着环跑。
做到这里基本上实现了粒子的循环跑动:
接下来就是加特效的时间了,这里我只添加了一个特效,就是用到上面提及的渐变器让粒子光环能够在跑动中变换颜色:
void changeColor () {
for (int i = 0; i < (int)Mathf.Sqrt(count); i++) {
for (int j = 0; j < (int)Mathf.Sqrt (count); j++) {
float value = (Time.realtimeSinceStartup - Mathf.Floor (Time.realtimeSinceStartup));
value += circle [(int)(i*Mathf.Sqrt(count) + j)].angle / 2 / Mathf.PI;
while (value > 1)
value--;
particleArr [(int)(i*Mathf.Sqrt(count) + j)].color = colorGradient.Evaluate (value);
}
}
}
这里调用了colorGradient.Evaluate (value)这个函数传入时间来控制颜色的变化,这就要求我们再UI界面编辑渐变器。这里值得一提的是我将整个圆环分成了100块,在每一块中都会出现颜色的渐变达到上面展示的最终效果。当然,这还需要一个前提就是设置Particle System中render中的material为默认的空白材料,不然的话渐变的效果基本没有。
所以,整个的update为:
void Update () {
for (int i = 0; i < count; i++)
{
if (clockwise) // 顺时针旋转
circle[i].angle -= Time.deltaTime * speed / 10;
else // 逆时针旋转
circle[i].angle += Time.deltaTime * speed / 10;
// 保证angle在0~360度
circle[i].angle = (360.0f + circle[i].angle) % 360.0f;
circle[i].time += Time.deltaTime;
circle[i].radius += Mathf.PingPong(circle[i].time / minRadius / maxRadius, pingPong) - pingPong / 2.0f;
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));
}
changeColor ();
particleSys.SetParticles(particleArr, particleArr.Length);
}
最后,还可以通过导入包来添加render中的材料,毕竟unity自带的材料不太好看就是了。
搞定之后挂载inner和outer对象上就可以了,顺时针,逆时针什么的可以直接在UI界面操作。