这次使用粒子系统模仿I Remember网站上面的光环效果
网站静态图
我实现的效果图
粒子系统设置
添加粒子系统到一个空对象下,并且给空对象挂上文末的脚本,设置粒子参数
注意:别忘了把该粒子系统拖到红线框中
设计思路
观察效果发现光环由两层顺时针转动的环组成
如下图,黑色圆环层粒子分布比较疏松范围广,红色圆环层密集而且有两个对称的缺口,并且红色顺时针转动速度较快
对于每个圆环,设置最大和最小半径,然后利用随机函数使粒子集中分布在中间半径处
对于粒子的具体位置,我们只需要得到它的实际半径以及圆心角即可转换成对应的(x, y, 0)坐标
- 如何使粒子集中在圆环中间区域
如上图,要让粒子尽可能集中在蓝线附近,
①随机生成一个半径max到达黄区
②随机生成一个半径min在绿色区域内
③随机在(min, max)范围内生成新的半径randomRadius,这样randomRadius就会在中间蓝线附近
代码如下:
//maxR是最大半径,minR是最小半径
float midRadius = (maxR + minR) / 2;
float min = Random.Range(minR, midRadius);
float max = Random.Range(midRadius, maxR);
float randomRadius = Random.Range(min, max);
- 如何形成对称的缺口
因为粒子在两个对称方向分布稀疏,相反我们令它们在另两个方 向分布比较密集即可形成同样效果
方法和上面的相似,也是利用三次随机函数
例如,我令粒子在初始状态,密集的分布在0度和180度处
for (int i = 0; i < particleNum; i++){
...
//在(-90,90)范围内生成集中在0度附近的的角度
float minAngle = Random.Range(-90f, 0.0f);
float maxAngle = Random.Range(0.0f, 90f);
float angle = Random.Range(minAngle, maxAngle);
//一半粒子向0度集中、令一半向180度集中
randomAngle = i % 2 == 0 ? angle : angle - 180;
...
}
解决了上面两个主要问题后,再说一下粒子的存储、转动和缩放
- 粒子存储
为了管理粒子数据,新建粒子数据类,然后为其声明一个数组与粒子数组匹配
//创建粒子系统
public ParticleSystem particleSystem;
//粒子数组
private ParticleSystem.Particle[] particlesArray;
//粒子属性数组
private particleClass[] particleAttr;
public class particleClass
{
public float radiu = 0.0f;//初始化半径
public float collect_radiu = 0.0f;//集合后的半径
public float temp_radiu = 0.0f;//粒子扩大缩放过程中的暂存半径
public float angle = 0.0f;
public particleClass(float radiu_, float angle_, float collect_)
{
radiu = radiu_;
angle = angle_;
collect_radiu = collect_;
temp_radiu = radiu_;
}
}
利用一个粒子数组存放了宽环和窄环的所有粒子,试过比例后发现把5/12粒子分配给宽环,剩下的7/12给窄环会比较好看,所以在分配粒子属性时也会先判断是哪个环的粒子再做不同的设置。
- 粒子转动
在Update函数中根据不同环的速率改变粒子角度后,根据原来半径和新角度重新分布位置即可
局部代码如下:
for (int i = 0; i < particleNum; i++)
{
//窄环快、宽环粒子移动速度慢
if (i > particleNum * 5 / 12)
speed = 0.1f;
else
speed = 0.05f;
//改变粒子角度后即可实现粒子顺时针移动
particleAttr[i].angle -= speed;
particleAttr[i].angle = particleAttr[i].angle % 360;
float rad = particleAttr[i].angle / 180 * Mathf.PI;
……
//重新设置粒子位置
particlesArray[i].position = new Vector3(particleAttr[i].now_radiu * Mathf.Cos(rad), particleAttr[i].now_radiu * Mathf.Sin(rad), 0f);
- 粒子缩放
- 需要收缩时
如果粒子当前半径比平均半径大(外围粒子),则以一定的速度向预先算好的收缩后的半径缩小;反之(内围)则向预先算好的收缩半径扩大 - 需要扩张恢复时
则把当前半径与初始半径作比较,移动和上面收缩类似
为了看起来与网站效果接近一些,我令收缩时外环快、内环慢,扩张时外环慢、内环快。
局部代码如下:
//判断需不需要缩放,改变粒子的暂存半径
if(flag == 0)//需要向中间收缩
{
if (particleAttr[i].now_radiu > particleAttr[i].collect_radiu)
{
//两层环的收缩速度不同
if(i < particleNum * 5 / 12)
particleAttr[i].now_radiu -= 3.0f * Time.deltaTime;
else
particleAttr[i].now_radiu -= 4.0f * Time.deltaTime;
}
else if(particleAttr[i].now_radiu < particleAttr[i].collect_radiu)
{
if (i < particleNum * 5 / 12)
particleAttr[i].now_radiu += 2.0f * Time.deltaTime;
else
particleAttr[i].now_radiu += Time.deltaTime;
}
}
else if(flag == 1)//扩大
{
if (particleAttr[i].now_radiu < particleAttr[i].ini_radiu)
{
if (i < particleNum * 5 / 12)
particleAttr[i].now_radiu += 2.0f * Time.deltaTime;
else
particleAttr[i].now_radiu += 3.0f * Time.deltaTime;
}
else if (particleAttr[i].now_radiu > particleAttr[i].ini_radiu)
{
if (i < particleNum * 5 / 12)
particleAttr[i].now_radiu -= 4.0f * Time.deltaTime;
else
particleAttr[i].now_radiu -= 4.0f * Time.deltaTime;
}
}
完整代码
using UnityEngine;
using System.Collections;
public class ParticleRing: MonoBehaviour
{
public class particleClass
{
public float ini_radiu = 0.0f;//初始化半径
public float collect_radiu = 0.0f;//集合后的半径
public float now_radiu = 0.0f;//粒子当前时刻半径,用于扩大缩小时与上两者比较
public float angle = 0.0f;
public particleClass(float radiu_, float angle_, float collect_)
{
ini_radiu = radiu_;
angle = angle_;
collect_radiu = collect_;
now_radiu = radiu_;
}
}
//创建粒子系统,
public ParticleSystem particleSystem;
//粒子数组
private ParticleSystem.Particle[] particlesArray;
//粒子属性数组
private particleClass[] particleAttr;
public int particleNum = 12000;
//较宽的环的内外半径
public float outMinRadius = 5.0f;
public float outMaxRadius = 10.0f;
//较窄的环(带缺口)的内外半径
public float inMinRadius = 6.0f;
public float inMaxRadius = 9.0f;
public float speed = 0.1f;
public int flag;
void Start()
{
flag = -1;
particleAttr = new particleClass[particleNum];
particlesArray = new ParticleSystem.Particle[particleNum];
particleSystem.maxParticles = particleNum;
particleSystem.Emit(particleNum);
particleSystem.GetParticles(particlesArray);
for (int i = 0; i < particleNum; i++)
{
//相应初始化操作,为每个粒子设置半径,角度
float randomAngle;
// 随机产生每个粒子距离中心的半径,同时粒子要集中在平均半径附近
float maxR, minR;
if(i < particleNum * 5 / 12)//这部分粒子属于较宽的那个环
{
maxR = outMaxRadius;
minR = outMinRadius;
randomAngle = Random.Range(0.0f, 360.0f);
}
else//窄环带缺口,设置一半向0度集中、一半向180度集中,以便在90度和-90度形成两个对称缺口
{
maxR = inMaxRadius;
minR = inMinRadius;
float minAngle = Random.Range(-90f, 0.0f);
float maxAngle = Random.Range(0.0f, 90f);
float angle = Random.Range(minAngle, maxAngle);
randomAngle = i % 2 == 0 ? angle : angle - 180;//利用对称性设置另一半粒子
}
float midRadius = (maxR + minR) / 2;
float min = Random.Range(minR, midRadius);
float max = Random.Range(midRadius, maxR);
float randomRadius = Random.Range(min, max);
float collectRadius;
//注意设置平均半径外围的粒子缩小时移动的距离少一些
if (randomRadius > midRadius)
collectRadius = randomRadius - (randomRadius - midRadius) / 2;
else
collectRadius = randomRadius - (randomRadius - midRadius) * 3 / 4;
//粒子属性设置
particleAttr[i] = new particleClass(randomRadius, randomAngle, collectRadius);
particlesArray[i].position = new Vector3(randomRadius * Mathf.Cos(randomAngle), randomRadius * Mathf.Sin(randomAngle), 0.0f);
}
//设置粒子
particleSystem.SetParticles(particlesArray, particleNum);
}
void Update()
{
for (int i = 0; i < particleNum; i++)
{
//根据新的角度
if (i > particleNum * 5 / 12)
speed = 0.1f;
else
speed = 0.05f;
particleAttr[i].angle -= speed;
particleAttr[i].angle = particleAttr[i].angle % 360;
float rad = particleAttr[i].angle / 180 * Mathf.PI;
//判断需不需要缩放,改变粒子的暂存半径
if(flag == 0)//需要向中间收缩
{
if (particleAttr[i].now_radiu > particleAttr[i].collect_radiu)
{
//两层环的收缩速度不同
if(i < particleNum * 5 / 12)
particleAttr[i].now_radiu -= 3.0f * Time.deltaTime;
else
particleAttr[i].now_radiu -= 4.0f * Time.deltaTime;
}
else if(particleAttr[i].now_radiu < particleAttr[i].collect_radiu)
{
if (i < particleNum * 5 / 12)
particleAttr[i].now_radiu += 2.0f * Time.deltaTime;
else
particleAttr[i].now_radiu += Time.deltaTime;
}
}
else if(flag == 1)//扩大
{
if (particleAttr[i].now_radiu < particleAttr[i].ini_radiu)
{
if (i < particleNum * 5 / 12)
particleAttr[i].now_radiu += 2.0f * Time.deltaTime;
else
particleAttr[i].now_radiu += 3.0f * Time.deltaTime;
}
else if (particleAttr[i].now_radiu > particleAttr[i].ini_radiu)
{
if (i < particleNum * 5 / 12)
particleAttr[i].now_radiu -= 4.0f * Time.deltaTime;
else
particleAttr[i].now_radiu -= 4.0f * Time.deltaTime;
}
}
//粒子新的位置
particlesArray[i].position = new Vector3(particleAttr[i].now_radiu * Mathf.Cos(rad), particleAttr[i].now_radiu * Mathf.Sin(rad), 0f);
}
particleSystem.SetParticles(particlesArray, particleNum);
}
void OnGUI()
{
if (GUI.Button(new Rect(0, 10, 100, 30), "扩大/缩小"))
{
flag = (flag == -1)? 0: 1 - flag;
}
}
}