有关粒子系统
粒子系统是模拟一些不确定、流动现象的技术。它采用许多形状简单且赋予生命的微小粒子作为基本元素来表示物体(一般由点或很小的多边形通过纹理贴图表示),表达物体的总体形态和特征的动态变化。
相关概念
粒子(particle)
粒子是粒子系统管理的基本单位。一般它是材料(Material)。材料包含两个内容,纹理(texture)、shader,分别负责形态、光照效果、两个方面。通常,粒子系统包含基础材料库供用户选择。
渲染(render)
渲染是定义粒子材料与摄像机之间的关系。主要包括材料面方位、显示顺序、光照等信息。
基本参数
- 持续时间(Duration):粒子系统发射粒子的持续时间
- 循环(Looping):粒子系统是否循环
- 预热(Prewarm):当looping开启时,才能启动预热(Prewarm),游戏开始时粒子已经发射了一个周期
- 初始延迟(Start Delay):粒子系统发射粒子之前的延迟。注意在prewarm(预热)启用下不能使用此项
- 初始生命(Start Lifetime):以秒为单位,粒子存活数量
- 初始速度(Start Speed):粒子发射时的速度
- 初始大小(Start Size):粒子发射时的大小
- 初始旋转(Start Rotation):粒子发射时的旋转值
- 初始颜色(Start Color):粒子发射时的颜色
- 重力修改器(Gravity Modifier):粒子在发射时受到的重力影响
- 继承速度(Inherit Velocity):控制粒子速率的因素将继承自粒子系统的移动(对于移动中的粒子系统)
- 模拟空间(Simulation Space):粒子系统在自身坐标系还是世界坐标系
- 唤醒时播放(Play On Awake):如果启用粒子系统当在创建时,自动开始播放
- 最大粒子数(Max Particles):粒子发射的最大数量
- 自动随机种子(Auto Random Seed):自动随机种子,为粒子加入随机性,使得每次播放效果时都会有不同的模拟
- 停止时的动作(Stop Action):所有粒子都消亡后的行为
- 剔除(Culling Mode):如果粒子不再视野范围内,则将其剔除掉
- 粒子移除模式(Ring Buffer Mode):将粒子从系统中移除的方式
实例:小行星带
我们这次来实现一个小行星以及围绕其旋转的小行星带,用粒子系统来管理小行星带中的每一个小行星。
首先新建一个球体(sphere)作为小行星,初始位置设为(0, 0, 0)。除了选择相应地纹理,我们还要在其上挂载一个C#脚本来管理其位置和自传运动。代码如下。
public class Rotate : MonoBehaviour {
void Start () {
transform.localScale = new Vector3(5, 5, 5);
}
void Update () {
this.transform.Rotate(Vector3.down * 30 * Time.deltaTime);
}
}
接着新建一个空对象作为粒子系统,同样是将位置设为(0, 0, 0)。然后通过Add Component添加Particle System,并其中的Renderer中将material选为stone的材质。
最后将写好的StarRing脚本挂载在这个对象上,具体代码如下。
public class ParticleInfo
{
public float radius = 0;
public float angle = 0;
public ParticleInfo(float radius, float angle)
{
this.radius = radius; // 粒子半径
this.angle = angle; // 粒子角度
}
}
public class StarRing : MonoBehaviour {
private ParticleSystem particleSys; // 粒子系统
private ParticleSystem.Particle[] particleArr; // 所有粒子
private ParticleInfo[] info; // 所有粒子信息
float speed = 0.25f; // 粒子速度
public int count = 8000; // 粒子数量
void Start () {
// 初始化
particleArr = new ParticleSystem.Particle[count];
info = new ParticleInfo[count];
particleSys = this.GetComponent<ParticleSystem>();
particleSys.loop = false; // 取消循环
particleSys.startSpeed = 0; // 设置粒子初速度
particleSys.maxParticles = count; // 设置最大粒子量
particleSys.Emit(count); // 发射粒子
particleSys.GetParticles(particleArr); // 获取所有粒子
IniAll(); // 初始化所有粒子
}
// Update is called once per frame
void Update()
{
for (int i = 0; i < count; i++)
{
// 让速度在小幅度内波动
float rotateSpeed = (speed / info[i].radius) * (i % 10 + 1);
// 移动
info[i].angle += rotateSpeed;
// 保证角度合法
info[i].angle %= 360.0f;
// 转换成弧度制
float radian = info[i].angle * Mathf.PI / 180;
// 粒子在半径方向上抖动
float offset = Random.Range(-0.01f, 0.01f); // 偏移范围
info[i].radius += offset;
particleArr[i].position = new Vector3(info[i].radius * Mathf.Cos(radian), 0f, info[i].radius * Mathf.Sin(radian));
}
// 通过粒子数组设置粒子系统
particleSys.SetParticles(particleArr, particleArr.Length);
}
void IniAll()
{
float minRadius = 6.0f; // 最小半径
float maxRadius = 10.0f; // 最大半径
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, 360);
// 转换成弧度制
float radian = angle / 180 * Mathf.PI;
// 随机每个粒子的大小
float size = Random.Range(0.01f, 0.03f);
info[i] = new ParticleInfo(radius, angle);
particleArr[i].position = new Vector3(info[i].radius * Mathf.Cos(radian), 0f, info[i].radius * Mathf.Sin(radian));
particleArr[i].size = size;
}
// 通过初始化好的粒子数组设置粒子系统
particleSys.SetParticles(particleArr, particleArr.Length);
}
}
其中ParticleInfo是自己实现的用于存储粒子信息的类,以此来提高代码的封装性和可读性。然后要保证所有的粒子公转轨迹和行星的自转轨迹都是自西向东的。另外还值得一提的是,为了不让行星带的边缘过于明显,在IniAll()函数中让所有粒子的半径带有了一点随机性,而且还在update函数中让每个粒子在很小的范围内抖动,从而使画面更加真实。最后我还加入了之前使用过很多次的starfield天空盒,来营造太空的感觉。
最终的实现效果如下所示。