前幾天在項目中遇到一個問題,需求是界面中先展示一段閃光特效,停頓一段時間后特效飛往一個固定的位置然后消失,類似於跑酷游戲吃到金幣后金幣飛往固定的金幣數值顯示框那種效果(具體是通過特效來實現還是直接通過金幣模型移動和縮放來實現這個我倒沒深入研究過,反正就是這么個意思,有相關經驗的請不吝賜教 )。當時也沒有多想,覺得這個應該是由美術特效來實現整個流程,后來美術資源給過來后發現和實際效果不大一樣,特效播完后不能朝目標點移動,這就尷尬了!!!后來一想也確實是不行,一是雖然美術可以在Inspector面板設置參數來控制特效的一個大概運動方向,但是也不能實現精確的控制。二是即使能精確控制又怎么知道特效到底要飛往哪個點?萬一需求改了目標點位置變了美術豈不是要重新設置特效?
正在苦思冥想之際得一同事指點,可以通過代碼來控制粒子的運行方向及速度等屬性。查閱相關資料后終於解決了這個問題。原來Unity官方文檔里也提供了一個控制粒子屬性的示例,真是慚愧,看來以后得多看幾遍官方文檔了。
大概的方法就是Update的時候獲取粒子系統發出的所有粒子,調整好屬性后再設置給粒子系統繼續顯示,代碼如下:
using UnityEngine;
public class ParticleTarget : MonoBehaviour
{
public Transform targetTransform;
public float speed;
private ParticleSystem particleSys;
private ParticleSystem.Particle[] particles;
private void Awake()
{
this.particleSys = this.GetComponent<ParticleSystem>();
if (this.particleSys)
{
this.particles = new ParticleSystem.Particle[this.particleSys.main.maxParticles];
//自定義粒子系統的模擬空間
ParticleSystem.MainModule main = this.particleSys.main;
main.simulationSpace = ParticleSystemSimulationSpace.Custom;
main.customSimulationSpace = this.targetTransform;
}
}
private void Update()
{
if (!this.targetTransform)
{
return;
}
if (this.particleSys)
{
int count = this.particleSys.GetParticles(this.particles);
for (int i = 0; i < count; i++)
{
//朝目標點插值緩動
this.particles[i].position = Vector3.Lerp(this.particles[i].position, Vector3.zero, this.speed);
}
this.particleSys.SetParticles(this.particles, count);
}
}
}
其中需要注意一下坐標系的問題,我們的粒子特效一般設置成local坐標系模擬,只有保證粒子的模擬坐標系和目標點在同一空間下才能夠正確的達到目的(這不是廢話嗎),好在粒子系統提供了一個自定義模擬空間的設置,這樣就可以很方便的設置粒子系統的模擬空間為目標點的Transform,然后將粒子目標點設置成Vector3.zero(在目標位置的局部空間下,Vector3.zero就相當於目標點位置,這也是廢話)。
上面的代碼在Unity5.5版本運行沒問題,但是在之前的版本沒有提供自定義粒子系統模擬空間的設置。既然沒有提供那我們可以手動將目標點的位置轉換到粒子系統的local坐標系來達到同樣的目的,如果粒子系統的模擬空間是世界坐標系的話可以直接使用目標點的世界坐標,代碼如下:
using UnityEngine;
public class ParticleTarget : MonoBehaviour
{
public Transform targetTransform;
public float speed;
private Vector3 targetPosition;
private ParticleSystem particleSys;
private ParticleSystem.Particle[] particles;
private void Awake()
{
this.particleSys = this.GetComponent<ParticleSystem>();
if (this.particleSys)
{
this.particles = new ParticleSystem.Particle[this.particleSys.main.maxParticles];
Vector3 world = this.targetTransform.TransformPoint(Vector3.zero);
this.targetPosition = this.transform.InverseTransformPoint(world);
}
}
private void Update()
{
if (!this.targetTransform)
{
return;
}
if (this.particleSys)
{
int count = this.particleSys.GetParticles(this.particles);
for (int i = 0; i < count; i++)
{
this.particles[i].position = Vector3.Lerp(this.particles[i].position, this.targetPosition, this.speed);
}
this.particleSys.SetParticles(this.particles, count);
}
}
}
在此基礎上我們還可以做一些擴展,比如可以在一個固定時間后粒子才飛往目標點,這樣就為特效提供了充分的展示時間,整個過程就會顯得不那么突兀。還可以調整粒子飛往目標點的速度或者路徑(不限於直線),從而達到更好的顯示效果。萬變不離其宗,具體實現有興趣的可以自己去研究。
以上內容均為本人拙見,如有錯誤還望大家指出,如有其他方法也請大家不吝賜教。