這次作業是讓我們做一個網站首頁的光環效果,網站如下:http://i-remember.fr/en
可以看出,粒子光環大約有兩層,外層粒子呈順時針旋轉,內層粒子呈逆時針旋轉,總的來說可以採用如下方式來設計,建立一個總的對象halo,
其中包含兩個子對象out和in,分別代表外層粒子和內層粒子。粒子的位置和運動採用極座標來控制,從數學上我們知道,與圓和旋轉相關的活動, 採用極座標較爲方便。最後使用pingpong函數,使粒子在半徑方向上游離即可。
下面是具體的設計過程,先創建一個空的halo對象,爲其添加out子對象,代表外層光環,對out子對象增加粒子系統、
新建一個c#腳本halo,這裏因爲代碼量較小,我們採用自上而下的設計方式,先在start和update函數中把需要的功能列好,再一步步實現該功能。
void Start () {
initial ();
RandomOperation ();
}
void Update () {
rotate ();
}
這裏initial函數代表初始化粒子系統,RandomOpeartion函數使得粒子隨機分佈在圓軌道上,rotate函數使得粒子可以進行旋轉。
在實現每個函數之前,我們先來確定一下要聲明的變量,在這裏我引入一個新類,circlePosition,用來表示粒子的極座標,其屬性有半徑,角度和時間,代碼如下:
public class CirclePosition
{
public float radius = 0f, angle = 0f, time = 0f;
public CirclePosition(float ra, float an, float ti) {
radius = ra;
angle = an;
time = ti;
}
}
要聲明的變量如下:
private ParticleSystem pSys; // 粒子系統
private ParticleSystem.Particle[] pArr; // 粒子數組
private CirclePosition[] cirPos; // 每個粒子的極座標
private int count = 10000; // 粒子數量
private float size = 0.1f; // 粒子大小
private float minRadius = 5.0f; // 運動最小半徑
private float maxRadius = 12.0f; // 運動最大半徑
private float speed = 2f; // 運動速度
private float pp = 0.02f; // 遊離範圍
private int speedDiffer = 15; // 與遊離有關的分層變量
接下來便是初始化粒子系統,initial函數的實現,該函數功能主要是爲兩個數組開闢空間,同時爲各個變量進行初始化:
public void initial() {
cirPos = new CirclePosition[count];
pArr = new ParticleSystem.Particle[count];
pSys = this.GetComponent<ParticleSystem> ();
pSys.loop = false;
pSys.startSize = size;
pSys.startSpeed = 0;
pSys.maxParticles = count;
pSys.Emit (count);
pSys.GetParticles (pArr);
}
然後是使得粒子隨機分佈在圓軌道上的函數RandomOperation
注意這裏,我們想讓粒子更多集中的分佈在平均半徑附近,也就是我們上面聲明的最大半徑和最小半徑的均值,注意這裏我們使用的是弧度,產生角度時還要做一個數學上的代換
public void RandomOperation() {
float midRadius;
float minRate;
float maxRate;
float radius;
float angle;
float circleAngle;
float time;
for (int i = 0; i < count; i++) {
midRadius = (maxRadius + minRadius) / 2;
minRate = Random.Range (1.0f, midRadius / minRadius);
maxRate = Random.Range (midRadius / maxRadius, 1.0f);
radius = Random.Range (minRadius * minRate, maxRadius * maxRate);
angle = Random.Range (0.0f, 360.0f);
circleAngle = angle / 180 * Mathf.PI;
time = Random.Range (0.0f, 360.0f);
cirPos [i] = new CirclePosition (radius, angle, time);
pArr [i].position = new Vector3 (cirPos [i].radius * Mathf.Cos (circleAngle), 0f, cirPos [i].radius * Mathf.Sin (circleAngle));
}
pSys.SetParticles (pArr, pArr.Length);
}
最後便是使得粒子旋轉的函數rotate
speedDiffer是一個分層變量,目的是爲了使得粒子角度添加的增量不同,同時利用direction變量控制順時針和逆時針,這裏還要注意一點,隨機產生的角度必須要在0到360度
最後那個pingpong函數是爲了使得粒子產生遊離效果,具體代碼如下:
public void rotate() {
float circleAngle;
for (int i = 0; i < count; i++) {
if(direction)
cirPos [i].angle -= (i % speedDiffer + 1) * (speed / cirPos [i].radius / speedDiffer);
else
cirPos [i].angle += (i % speedDiffer + 1) * (speed / cirPos [i].radius / speedDiffer);
cirPos [i].angle = (360.0f + cirPos [i].angle) % 360.0f;
circleAngle = cirPos [i].angle / 180 * Mathf.PI;
pArr [i].position = new Vector3 (cirPos [i].radius * Mathf.Cos (circleAngle), 0f, cirPos [i].radius * Mathf.Sin (circleAngle));
cirPos [i].time += Time.deltaTime;
cirPos [i].radius += Mathf.PingPong (cirPos [i].time / minRadius / maxRadius, pp) - pp / 2.0f;
}
pSys.SetParticles (pArr, pArr.Length);
}
到這裏基本上一個外層粒子就寫完了,內層粒子也就是將那個direction的鉤打掉,再略微改一下參數就行了
這裏我稍微改了一下顏色,使得內外層粒子顏色各不相同,看的效果更加明顯。
運行效果如下:
最後是此次的完整代碼
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CirclePosition
{
public float radius = 0f, angle = 0f, time = 0f;
public CirclePosition(float ra, float an, float ti) {
radius = ra;
angle = an;
time = ti;
}
}
public class halo : MonoBehaviour {
private ParticleSystem pSys; // 粒子系統
private ParticleSystem.Particle[] pArr; // 粒子數組
private CirclePosition[] cirPos; // 每個粒子的極座標
public bool direction = true;
private int count = 10000; // 粒子數量
private float size = 0.1f; // 粒子大小
private float minRadius = 5.0f; // 運動最小半徑
private float maxRadius = 12.0f; // 運動最大半徑
private float speed = 2f; // 運動速度
private float pp = 0.02f; // 遊離範圍
private int speedDiffer = 15; // 與遊離有關的分層變量
public void initial() {
cirPos = new CirclePosition[count];
pArr = new ParticleSystem.Particle[count];
pSys = this.GetComponent<ParticleSystem> ();
pSys.loop = false;
pSys.startSize = size;
pSys.startSpeed = 0;
pSys.maxParticles = count;
pSys.Emit (count);
pSys.GetParticles (pArr);
}
public void RandomOperation() {
float midRadius;
float minRate;
float maxRate;
float radius;
float angle;
float circleAngle;
float time;
for (int i = 0; i < count; i++) {
midRadius = (maxRadius + minRadius) / 2;
minRate = Random.Range (1.0f, midRadius / minRadius);
maxRate = Random.Range (midRadius / maxRadius, 1.0f);
radius = Random.Range (minRadius * minRate, maxRadius * maxRate);
angle = Random.Range (0.0f, 360.0f);
circleAngle = angle / 180 * Mathf.PI;
time = Random.Range (0.0f, 360.0f);
cirPos [i] = new CirclePosition (radius, angle, time);
pArr [i].position = new Vector3 (cirPos [i].radius * Mathf.Cos (circleAngle), 0f, cirPos [i].radius * Mathf.Sin (circleAngle));
}
pSys.SetParticles (pArr, pArr.Length);
}
public void rotate() {
float circleAngle;
for (int i = 0; i < count; i++) {
if(direction) // 順時針
cirPos [i].angle -= (i % speedDiffer + 1) * (speed / cirPos [i].radius / speedDiffer);
else //逆時針
cirPos [i].angle += (i % speedDiffer + 1) * (speed / cirPos [i].radius / speedDiffer);
//確保範圍在0到360度
cirPos [i].angle = (360.0f + cirPos [i].angle) % 360.0f;
circleAngle = cirPos [i].angle / 180 * Mathf.PI;
pArr [i].position = new Vector3 (cirPos [i].radius * Mathf.Cos (circleAngle), 0f, cirPos [i].radius * Mathf.Sin (circleAngle));
//使得粒子游離
cirPos [i].time += Time.deltaTime;
cirPos [i].radius += Mathf.PingPong (cirPos [i].time / minRadius / maxRadius, pp) - pp / 2.0f;
}
pSys.SetParticles (pArr, pArr.Length);
}
void Start () {
initial ();
RandomOperation ();
}
void Update () {
rotate ();
}
}
|