Unity3d--粒子光环

一.作业要求

本次作业基本要求是三选一

  1. 简单粒子制作
    按参考资源要求,制作一个粒子系统,参考资源
    使用 3.3 节介绍,用代码控制使之在不同场景下效果不一样
  2. 完善官方的“汽车尾气”模拟
    使用官方资源资源 Vehicle 的 car, 使用 Smoke 粒子系统模拟启动发动、运行、故障等场景效果
  3. 参考 http://i-remember.fr/en 这类网站,使用粒子流编程控制制作一些效果, 如“粒子光环”

可参考以前作业
我选择任务3,制作粒子光环。

二.实现过程

首先创建空对象ParticleHalo,然后在其下面创建一个子对象,命名为Clockwise,以及另一个反向旋转的子对象,命名为Counterclockwise。
在这里插入图片描述
然后创建C#脚本控制两个粒子光环ParticleHalo。
接着进行实现。
首先定义新的结构ParticalInfo存储粒子的数据,数据包含每个粒子的当前半径、角度和时间,其中时间是做PingPong时根据当前时间来决定漂浮的大小。

public class ParticalInfo {
	public float radius = 0f, angle = 0f, time = 0f;
	public ParticalInfo(float radius, float angle, float time){this.radius = radius;this.angle = angle;this.time = time;}
}

然后定义一个粒子系统的变量,粒子的数组和对应的粒子的数据,以及一些关于粒子属性的变量

	private ParticleSystem particleSys;
	private ParticleSystem.Particle[] particleArr; 
	private ParticalInfo[] particles;
	private float[] radius; 
	private float[] collect_radius;
	private int tier = 10;
	private int time = 0;
	public Gradient colorGradient;
	public int particleNum = 10000;
	public float size = 0.03f;
	public float minRadius = 7.0f;
	public float maxRadius = 10.0f;
	public float collect_MaxRadius = 4.0f;
	public float collect_MinRadius = 1.0f;
	public bool clockwise = true; 
	public float speed = 2f; 
	public float pingPong = 0.02f; 
	public int isCollected = 0;

然后在start函数里实例化粒子系统和初始化各粒子的属性与位置

void Start () {
		particleArr = new ParticleSystem.Particle[particleNum];
		particles = new ParticalInfo[particleNum];
		radius = new float[particleNum];
		collect_radius = new float[particleNum];
		particleSys = this.GetComponent<ParticleSystem>();
		particleSys.startSpeed = 0;            
		particleSys.startSize = size; 
		particleSys.loop = false;
		particleSys.maxParticles = particleNum;      
		particleSys.Emit(particleNum); 
		particleSys.GetParticles(particleArr);
		RandomlySpread();
	}

其中RandomlySpread函数将粒子随机分布在圆圈轨道上,实现如下:

void RandomlySpread(){
		for (int i = 0; i < particleNum; ++i){  
			float midRadius = (maxRadius + minRadius) / 2;
			float minRate = UnityEngine.Random.Range(1.0f, midRadius / minRadius);
			float maxRate = UnityEngine.Random.Range(midRadius / maxRadius, 1.0f);
			float _radius = UnityEngine.Random.Range(minRadius * minRate, maxRadius * maxRate);
			radius[i] = _radius;
			float collect_MidRadius = (collect_MaxRadius + collect_MinRadius) / 2;
			float collect_outRate = Random.Range (1f, collect_MidRadius / collect_MinRadius);
			float collect_inRate = Random.Range (collect_MaxRadius / collect_MidRadius, 1f);
			float _collect_radius = Random.Range (collect_MinRadius * collect_outRate, collect_MaxRadius * collect_inRate);
			collect_radius[i] = _collect_radius;
			float angle = UnityEngine.Random.Range(0.0f, 360.0f);
			float theta = angle / 180 * Mathf.PI;
			float time = UnityEngine.Random.Range(0.0f, 360.0f);
			if(isCollected == 0) particles [i] = new ParticalInfo (_radius, angle, time);
			else particles [i] = new ParticalInfo (_collect_radius, angle, time);
			particleArr[i].position = new Vector3(particles[i].radius * Mathf.Cos(theta), 0f, particles[i].radius * Mathf.Sin(theta));
		}
		particleSys.SetParticles(particleArr, particleArr.Length);
	}

这样运行后就可以显示一个粒子光环了,但是粒子既不会动,也没有一些特殊效果。
接着对粒子旋转进行实现,粒子旋转的原理为:让每个粒子的角度在每一帧都减少或增加一个值,同时需要添加一个速度差分层数让不同层的粒子转的速度不一样,这样会使得例子旋转的效果更好。其中通过clockwise判断是顺时针还是逆时针旋转。Update实现如下:

void Update (){
		for (int i = 0; i < particleNum; i++) {
			if (clockwise) particles [i].angle -= (i % tier + 1) * (speed / particles [i].radius / tier);
			else particles [i].angle += (i % tier + 1) * (speed / particles [i].radius / tier);
			particles [i].angle = (360.0f + particles [i].angle) % 360.0f;
			float theta = particles [i].angle / 180 * Mathf.PI;
			if (isCollected == 1){
				if (particles [i].radius > collect_radius [i]) particles [i].radius -= 15f * (collect_radius [i] / collect_radius [i]) * Time.deltaTime;  
			 	else particles [i].radius = collect_radius [i];
			} 
			else {
				if (particles [i].radius < radius [i]) particles [i].radius += 15f * (collect_radius [i] / collect_radius [i]) * Time.deltaTime;  
				else particles [i].radius += Mathf.PingPong (particles [i].time / minRadius / maxRadius, pingPong) - pingPong / 2.0f;
			}
			particleArr [i].position = new Vector3 (particles [i].radius * Mathf.Cos (theta), 0f, particles [i].radius * Mathf.Sin (theta));
		}
		changeColor ();
		particleSys.SetParticles(particleArr, particleArr.Length);
	}

粒子的漂浮效果既是调用PingPong函数实现:

 particles[i].time += Time.deltaTime;
 particles[i].radius += Mathf.PingPong(particleData[i].time / minRadius / maxRadius, pingPong) - pingPong / 2.0f;

这样粒子光环的旋转效果就基本实现了。接下来我们添加一个颜色、光效匀速旋转的效果,通过changeColor函数实现,具体如下:

void changeColor(){
		float colorValue;
		for (int i = 0; i < particleNum; i++){
			colorValue = (Time.realtimeSinceStartup - Mathf.Floor(Time.realtimeSinceStartup));
			colorValue += particles[i].angle/360;
			while (colorValue > 1) colorValue--;
			particleArr[i].color = colorGradient.Evaluate(colorValue);
		}
	}

实现的方法是填入Evaluate的值, 要与angle和时间相关。所以可以加入一个时间变量, 和angle值共同决定value。
接着实现第二个功能即点击屏幕可以对内圈外圈进行交换,通过OnGUI控制使用函数Input.GetMouseButtonDown(0)获取鼠标点击。首先,我们需要两个数组来存储收缩和扩展后的运动半径,好让它们能恢复到原来的轨道上

private float[] radius; 
private float[] collect_radius;

以及一个变量判断现在该全是在外圈还是内圈。

public int isCollected = 0;

接着在start的RandomlySpread函数中实现对两个数组的赋值,即一个是内圈一个是外圈,赋值方法和原本类似,然后根据在内圈或外圈对particle数组的半径进行赋值

float midRadius = (maxRadius + minRadius) / 2;
float minRate = UnityEngine.Random.Range(1.0f, midRadius / minRadius);
float maxRate = UnityEngine.Random.Range(midRadius / maxRadius, 1.0f);
float _radius = UnityEngine.Random.Range(minRadius * minRate, maxRadius * maxRate);
radius[i] = _radius;
float collect_MidRadius = (collect_MaxRadius + collect_MinRadius) / 2;
float collect_outRate = Random.Range (1f, collect_MidRadius / collect_MinRadius);
float collect_inRate = Random.Range (collect_MaxRadius / collect_MidRadius, 1f);
float _collect_radius = Random.Range (collect_MinRadius * collect_outRate, collect_MaxRadius * collect_inRate);
collect_radius[i] = _collect_radius;
if(isCollected == 0) particles [i] = new ParticalInfo (_radius, angle, time);

然后再Update中对内圈外圈进行分类,执行不同的操作,即外圈变换到内圈,半径大的进行收缩直到小于目标半径;内圈变换到外圈,半径小的进行扩展直到大于目标半径。

if (isCollected == 1){
	if (particles [i].radius > collect_radius [i]) particles [i].radius -= 15f * (collect_radius [i] / collect_radius [i]) * Time.deltaTime;  
	else particles [i].radius = collect_radius [i];
} else {
	if (particles [i].radius < radius [i]) particles [i].radius += 15f * (collect_radius [i] / collect_radius [i]) * Time.deltaTime;  
	else particles [i].radius += Mathf.PingPong (particles [i].time / minRadius / maxRadius, pingPong) - pingPong / 2.0f;
}

最后再OnGUI中获取鼠标点击屏幕,若点击则改变控制变量isCollected

void OnGUI(){
		if(Input.GetMouseButtonDown(0)){
			time++;
			if(time==2){
				isCollected = 1 - isCollected;
				time = 0;
			}
		}
	}

这样就实现了通过点击转换内圈外圈。

三.实验总结与心得

这次作业实现了粒子光环的效果,在其中主要学到了对粒子流的控制编程以及许多数学相关的计算。也查询资料学习了色彩变幻的相关内容。
Github地址:ParticleHalo
视频展示:粒子光环
最后感谢师兄的博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值