Unity3d-粒子光环

这一次做的是用粒子流编程做一个粒子系统,参考了师兄的博客。
效果展示:
这里写图片描述

基本配置

首先,打开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界面操作。

猜你喜欢

转载自blog.csdn.net/hellowangld/article/details/80499979