Unity:控制粒子特效的移动方向

前几天在项目中遇到一个问题,需求是界面中先展示一段闪光特效,停顿一段时间后特效飞往一个固定的位置然后消失,类似于跑酷游戏吃到金币后金币飞往固定的金币数值显示框那种效果(具体是通过特效来实现还是直接通过金币模型移动和缩放来实现这个我倒没深入研究过,反正就是这么个意思,有相关经验的请不吝赐教 )。当时也没有多想,觉得这个应该是由美术特效来实现整个流程,后来美术资源给过来后发现和实际效果不大一样,特效播完后不能朝目标点移动,这就尴尬了!!!后来一想也确实是不行,一是虽然美术可以在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);
        }
    }
}

在此基础上我们还可以做一些扩展,比如可以在一个固定时间后粒子才飞往目标点,这样就为特效提供了充分的展示时间,整个过程就会显得不那么突兀。还可以调整粒子飞往目标点的速度或者路径(不限于直线),从而达到更好的显示效果。万变不离其宗,具体实现有兴趣的可以自己去研究。

以上内容均为本人拙见,如有错误还望大家指出,如有其他方法也请大家不吝赐教。

posted @ 2017-08-13 18:17  laonzh  阅读(16702)  评论(0编辑  收藏  举报