参考效果: http://i-remember.fr/en
基本思路如下
1. 使用unity3d自带的粒子系统
2. 为圆环设置最大半径maxR和最小半径minR
3. 使用x=R*sin(angle), y=R*cos(angle)来计算每个粒子的坐标
4. 将粒子数组设置到选定的粒子系统中
下面说说具体步骤
一.
设置Main Camera的投影方式为正交投影,将背景设置为黑色,方便观察粒子效果
接着新建一个Empty GameObject,用来挂载粒子系统,注意这个空物体要放在摄像机的投影范围内,为这个物体添加粒子系统,并且按图中方式设置粒子系统
主要是要将Looping选项和Play OnAwake选项去掉,同时把Emission Rate设为0
二.
新建C#脚本,用来控制粒子系统
定义点结构,用来表示粒子的坐标信息
public class Point {
public float angle;
public float radius;
private float x = 0.0f;
private float y = 0.0f;
public void Cal() {
float temp = angle / 180.0f * Mathf.PI;
y = radius * Mathf.Sin(temp);
x = radius * Mathf.Cos(temp);
}
public Point(float angle, float radius) {
this.angle = angle;
this.radius = radius;
}
public float getX() {
return x;
}
public float getY() {
return y;
}
}
三.
声明粒子系统和粒子系统的相关参数
private ParticleSystem particleSystem;
//粒子数组
private ParticleSystem.Particle[] particleArray;
//粒子坐标集合
private Point[] points;
//颜色梯度
public Gradient grad;
//控制透明度的变化
private GradientAlphaKey[] gradAplp;
//控制颜色的变化
private GradientColorKey[] gradCol;
//粒子参数
public int count = 1000;
public float size = 0.5f;
public float minRadius = 5.0f;
public float maxRadius = 12.0f;
//控制主方向是顺时针还是逆时针
public bool clockwise = true;
public float speed = 0.5f;
四:
初始化粒子坐标,在一开始就生成一个圆环
private void Init() {
int i;
for (i = 0; i < count; i++) {
float midRadius = (minRadius + maxRadius) / 2.0f;
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);
points[i] = new Point(angle, radius);
points[i].Cal();
particleArray[i].position = new Vector3(points[i].getX(), points[i].getY(), 0f);
}
particleSystem.SetParticles(particleArray, particleArray.Length);
}
这里为了让粒子尽量集中在圆环的中间,首先计算出最大半径和最小半径之间的平均值,这个值就是圆环的中间,分别求出一个在最小半径和中间之间的一个随机比率以及中间值和最大半径之间的一个随机比率,再在这两个随机比率对应的半径值之间求出一个随机值就可以让粒子的坐标集中在中间值附近,根据概率的知识可知,求出的两个随机比率是符合正太分布的,所以这两个比率的中间值也是接近于圆环的中间,由于这两个比率对应的半径值中选取随机数也是符合正太分布的,所以会让大部分粒子集中在圆环中间的位置
四:
设置颜色梯度,我希望圆环在运动的时候能够根据当前的位置对颜色和透明度做出相应的变化
// Update is called once per frame
void Update () {
int i;
int level = 7; //将整个粒子系统分成七层,每一层的旋转方向和速度都有差异
for (i = 0; i < count; i++) {
if (i % level > 3)
{
//外层顺时针旋转
points[i].angle -= (i % level + 1) * speed;
} else {
//内层逆时针旋转
points[i].angle += (i % level + 1) * speed;
}
points[i].angle = (points[i].angle + 360.0f) % 360.0f;
//粒子旋转半径的微小变化,能够让粒子运动体现出随机性
points[i].radius += 0.04f * (Mathf.PerlinNoise(points[i].angle/360.0f, 0.0f))-0.02f;
//Debug.Log(points[i].radius);
points[i].Cal();
particleArray[i].color = grad.Evaluate(Time.time*0.1f);
particleArray[i].position = new Vector3(points[i].getX(), points[i].getY(), 0.0f);
}
particleSystem.SetParticles(particleArray, particleArray.Length);
}
这里面为了实现样例中的效果,需要让不同层次的粒子旋转方向不同,所以我将粒子分成了7层,每一层都有自己的旋转方向,当然这里还可调整旋转增量的参数,让旋转增量和运动半径产生关联,实现外围的粒子半径增量更大的效果,同时我使用Perlin噪声让粒子的运动半径发生随机的改变,然粒子的运动看起来更加无序,当然每次Update也需要重新计算梯度的变化
最终效果(不同时期的运行效果,可以看到粒子的运行半径和圆环形状,透明度等都有变化):
完整代码
using UnityEngine;
using System.Collections;
public class Point {
public float angle;
public float radius;
private float x = 0.0f;
private float y = 0.0f;
public void Cal() {
float temp = angle / 180.0f * Mathf.PI;
y = radius * Mathf.Sin(temp);
x = radius * Mathf.Cos(temp);
}
public Point(float angle, float radius) {
this.angle = angle;
this.radius = radius;
}
public float getX() {
return x;
}
public float getY() {
return y;
}
}
public class PCircle : MonoBehaviour {
private ParticleSystem particleSystem;
//粒子数组
private ParticleSystem.Particle[] particleArray;
//粒子坐标集合
private Point[] points;
//颜色梯度
public Gradient grad;
//控制透明度的变化
private GradientAlphaKey[] gradAplp;
//控制颜色的变化
private GradientColorKey[] gradCol;
//粒子参数
public int count = 1000;
public float size = 0.5f;
public float minRadius = 5.0f;
public float maxRadius = 12.0f;
//控制主方向是顺时针还是逆时针
public bool clockwise = true;
public float speed = 0.5f;
private void Init() {
int i;
for (i = 0; i < count; i++) {
float midRadius = (minRadius + maxRadius) / 2.0f;
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);
points[i] = new Point(angle, radius);
points[i].Cal();
particleArray[i].position = new Vector3(points[i].getX(), points[i].getY(), 0f);
}
particleSystem.SetParticles(particleArray, particleArray.Length);
}
// Use this for initialization
void Start () {
particleSystem = this.GetComponent();
particleArray = new ParticleSystem.Particle[count];
points = new Point[count];
particleSystem.startSpeed = 0;
particleSystem.startSize = size;
particleSystem.maxParticles = count;
particleSystem.Emit(count);
particleSystem.GetParticles(particleArray);
grad = new Gradient();
gradAplp = new GradientAlphaKey[5];
gradCol = new GradientColorKey[5];
float delt = 0f;
//设置梯度变化有五个程度,且是均匀变化
for (int i = 0; i < 5; i++) {
gradAplp[i].time = delt;
gradAplp[i].alpha = delt;
delt += 0.2f;
}
gradCol[0].time = 0.0f;
gradCol[0].color = Color.black;
gradCol[1].time = 1.0f;
gradCol[1].color = Color.white;
grad.SetKeys(gradCol, gradAplp);
Init();
}
// Update is called once per frame
void Update () {
int i;
int level = 7; //将整个粒子系统分成七层,每一层的旋转方向和速度都有差异
for (i = 0; i < count; i++) {
if (i % level > 3)
{
//外层顺时针旋转
points[i].angle -= (i % level + 1) * speed;
} else {
//内层逆时针旋转
points[i].angle += (i % level + 1) * speed;
}
points[i].angle = (points[i].angle + 360.0f) % 360.0f;
//粒子旋转半径的微小变化,能够让粒子运动体现出随机性
points[i].radius += 0.04f * (Mathf.PerlinNoise(points[i].angle/360.0f, 0.0f))-0.02f;
//Debug.Log(points[i].radius);
points[i].Cal();
particleArray[i].color = grad.Evaluate(Time.time*0.1f);
particleArray[i].position = new Vector3(points[i].getX(), points[i].getY(), 0.0f);
}
particleSystem.SetParticles(particleArray, particleArray.Length);
}
}
当然也可以自己写shader,毕竟unity3d自带的光效还是不太灵活,鉴于水平问题(本人太渣),这里就不做了