程序员也应了解的Unity粒子系统

Posted 丁小未

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了程序员也应了解的Unity粒子系统相关的知识,希望对你有一定的参考价值。

前言

曾经我们是不是以为跟粒子系统打交道多的是特效美术?曾经我们是不是以为改变粒子的位置是不是只要设置transform的position?曾经我们程序对粒子系统做的最多的操作是不是只要加载显示就OK了?曾经我们想要一次又一次的播放粒子特效是不是显示隐藏再显示父节点?曾经我们想要改变一下粒子特效的参数是不是先获取ParticleSystem组件然后修改里面的参数?曾经…,现在我才知道原来ParticleSystem有个Emit的接口,来改变粒子发射器。之前对粒子特效理解不深或许因为公司有特效美术,或许因为需求简单,或许自己没深入研究一下,不过亡羊补牢,为时不晚。我们都知道Unity的粒子很耗,能少用就少用,Unity也推出一款新的粒子系统,需要在Unity2018.3的版本之后使用。新的粒子系统是可视化的并且运行在GPU上的粒子系统,性能和效果大幅度提审,不过是基于HDRP的高清渲染管线,这就意味着大多数移动设备可能不支持。Unity还专门为了介绍这个新的粒子系统出了一款FPS的Demo,画质特别高清,有点像守望先锋的赶脚。

效果

  • 1.改变粒子发射位置

  • 2.改变粒子发射角度

粒子系统常见的参数和接口

  • StartLifetime 粒子生命时长
  • StartSpeed 粒子初始速度
  • 变量的四种类型
    • Constant 数值类型
    • Curve 曲线类型
    • Random Between Two Constants 两个数值之间的随机类型
    • Randoom Between Two Curves 两个曲线之间的随机数值类型
  • Play On Awake 是否一开始就播放
  • Emission 粒子发射器 勾上能在Scene中直接点Play看到效果,如果不勾选就看不到效果 我们一般要隐藏粒子系统的时候直接GameObejct设置为不可见比较突兀,最好用延时先关闭Emission然后再隐藏体验会好很多。
DOVirtual.DelayedCall(1, () =>

     for (int i = 0; i < psComponents.Length; i++)
     
         var emition = psComponents[i].emission;
         emition.enabled = true;
     
     mAttachedGameObject?.SetActive(activeState);
 );
  • Emission / Bursts 几个粒子
  • EmitParams 粒子参数改变类

注意事项

在测试两个Curve曲线取值的时候发现跟实际想要的值不一样,两个曲线的范围值只是-1~1,然后需要乘上系数SpeedCurve.curveMultiplier,其他模式却不需要这样乘以系数就能获取到Curve的值,感觉像是bug一样的存在,获取Curve的四种类型的值的方法如下:

    public float GetCurrentSpeed()
    
        switch (mSpeedCurve.mode)
        
            case ParticleSystemCurveMode.Constant:
                mCurrentSpeed = mSpeedCurve.constant;
                break;
            case ParticleSystemCurveMode.TwoConstants:
                mCurrentSpeed = Random.Range(mSpeedCurve.constantMin, mSpeedCurve.constantMax);
                break;
            case ParticleSystemCurveMode.Curve:
                mCurrentSpeed = mSpeedCurve.Evaluate(Random.value);
                break;
            case ParticleSystemCurveMode.TwoCurves:
                
                    float t = Random.value;
                    mCurrentSpeed = Random.Range(mSpeedCurve.curveMin.Evaluate(t), mSpeedCurve.curveMax.Evaluate(t)) * mSpeedCurve.curveMultiplier;
                
                break;
        
        return mCurrentSpeed;
    

代码

  • ParticleEffectsEmitter
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ParticleEffectsEmitter : MonoBehaviour

    public Vector3 Position;
	XXX if UNITY_EDITOR
    public bool TestEmit;
    public Vector3 TestRotation;
    public Vector3 TestDirection;
	XXX endif

    protected Transform mTrans;
    protected SubParticle[] mSubParticleList;

	xxx if UNITY_EDITOR
    void Update()
    
        if (TestEmit)
        
            TestEmit = false;
            if (TestRotation == Vector3.zero)
            
                Emit(Position, TestDirection);
            
            else
            
                Emit(Position, TestDirection, Quaternion.Euler(TestRotation));
            
        
    
	XXX endif
    public void Emit(Vector3 pos, Vector3 dir, float lifetimeScale = 1.0f, float sizeScale = 1.0f)
    
        if (mSubParticleList == null)
            return;
        for (int i = 0; i < mSubParticleList.Length; i++)
        
            mSubParticleList[i].Emit(pos, dir, lifetimeScale, sizeScale);
        
    
    public void Emit(Vector3 pos, Vector3 dir, Quaternion rotation, float lifetimeScale = 1.0f, float sizeScale = 1.0f)
    
        if (mSubParticleList == null)
            return;
        mTrans.rotation = rotation;
        Emit(pos, dir, lifetimeScale, sizeScale);
    
    public float GetSubParticleSpeed(int idx)
    
        if (mSubParticleList != null && mSubParticleList.Length > idx)
        
            return mSubParticleList[idx].GetCurrentSpeed();
        
        return 0;
    
    public float GetSubParticleSpeed()
    
        return GetSubParticleSpeed(0);
    
    protected void Awake()
    
        Init();
    
    protected void Init()
    
        ParticleSystem[] subParticles = transform.GetComponentsInChildren<ParticleSystem>();
        mSubParticleList = new SubParticle[subParticles.Length];
        mTrans = transform;
        for (int i = 0; i < subParticles.Length; i++)
        
            mSubParticleList[i] = new SubParticle();
            ParticleSystem.EmissionModule em = subParticles[i].emission;
            em.enabled = false;
            ParticleSystem.MainModule main = subParticles[i].main;
            main.maxParticles = main.maxParticles * 50;
            mSubParticleList[i].Init(mTrans, subParticles[i]);
        
    
    protected class SubParticle
    
        public Vector3 PosRelateToRoot;
        public ParticleEmitter Emitter;
        public void Init(Transform root, ParticleSystem ps)
        
            Transform t = ps.transform;
            if (Emitter == null)
                Emitter = new ParticleEmitter();
            Emitter.Init(ps);
            Emitter.GetCurrentSpeed();
            PosRelateToRoot = root.InverseTransformPoint(t.position);
        
        public void Emit(Vector3 rootPos, Vector3 dir, float lifetimeScale = 1.0f, float sizeScale = 1.0f)
        
            Vector3 offset = rootPos + PosRelateToRoot;
            if (dir != Vector3.zero)
            
                Emitter.UpdateDir(dir);
                Emitter.OverrideVelocity = true;
            
            else
            
                Emitter.OverrideVelocity = false;
            
            Emitter.Emit(offset, lifetimeScale, sizeScale);
        
        public float GetCurrentSpeed()
        
            return Emitter.GetCurrentSpeed();
        
    

//宏定义替换成XXX #避免跟MD语法冲突

  • Emitter
using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ParticleEmitter

    protected ParticleSystem.EmitParams mParams;
    protected ParticleSystem mParticleSys;
    protected ParticleSystem.MinMaxCurve mLifeTimeCurve;
    protected ParticleSystem.MinMaxCurve mSizeCurve;
    protected ParticleSystem.MinMaxCurve mDelayTimeCurve;
    protected ParticleSystem.MinMaxCurve mSpeedCurve;
    protected ParticleSystem.Burst[] mBurstList;
    protected float mSizeScale = 1.0f;
    protected float mLifeTimeScale = 1.0f;
    protected Vector3 mDir;
    protected float mCurrentSpeed;
    protected bool mOverrideVelocity = false;
    protected bool mOverrideStartLifeTime = false;
    public bool OverrideVelocity
    
        get  return mOverrideVelocity; 
        set  mOverrideVelocity = value; 
    

    public bool OverrideStartLifeTime
    
        get  return mOverrideStartLifeTime; 
        set  mOverrideStartLifeTime = value; 
    
    public void UpdateDir(Vector3 endStartVector)
    
        mOverrideVelocity = true;
        mDir = endStartVector.normalized;
    

    public void UpdateLifeTime(ParticleSystem.MinMaxCurve lifeTime)
    
        mOverrideStartLifeTime = true;
        mLifeTimeCurve = lifeTime;
    

    public void UpdateLifeTimeConstant(Vector3 endStartVector)
    
        mOverrideStartLifeTime = true;
        var speed = GetCurrentSpeed();
        var length = endStartVector.magnitude;
        var constant = length / speed;
        mLifeTimeCurve = new ParticleSystem.MinMaxCurve();
        mLifeTimeCurve.constant = constant;
    

    public void UpdateStartSize(ParticleSystem.MinMaxCurve startSize)
    
        mSizeCurve = startSize;
    

    public float GetCurrentSpeed()
    
        switch (mSpeedCurve.mode)
        
            case ParticleSystemCurveMode.Constant:
                mCurrentSpeed = mSpeedCurve.constant;
                break;
            case ParticleSystemCurveMode.TwoConstants:
                mCurrentSpeed = Random.Range(mSpeedCurve.constantMin, mSpeedCurve.constantMax);
                break;
            case ParticleSystemCurveMode.Curve:
                mCurrentSpeed = mSpeedCurve.Evaluate(Random.value);
                break;
            case ParticleSystemCurveMode.TwoCurves:
                
                    float t = Random.value;
                    mCurrentSpeed = Random.Range(mSpeedCurve.curveMin.Evaluate(t), mSpeedCurve.curveMax.Evaluate(t)) * mSpeedCurve.curveMultiplier;
                
                break;
        
        return mCurrentSpeed;
    

    public void Init(ParticleSystem ps)
    
        mParticleSys = ps;
        if (ps != null)
        
            ParticleSystem.MainModule mm = ps.main;
            mm.simulationSpace = ParticleSystemSimulationSpace.World;
            mParams.startColor = mm.startColor.color;
            mParams.applyShapeToPosition = true;

            mLifeTimeCurve = mm.startLifetime;
            mSizeCurve = mm.startSize;
            mSpeedCurve = mm.startSpeed;
            mDelayTimeCurve = mm.startDelay;

            ParticleSystem.EmissionModule em = ps.emission;
            if (em.burstCount > 0)
            
                mBurstList = new ParticleSystem.Burst[em.burstCount];
                em.GetBursts(mBurstList);
            
        
    

    public void Emit(Vector3 startPos, float lifetimeScale = 1.0f, float sizeScale = 1.0f)
    
        if (mParticleSys != null)
        
            mParams.position = startPos;
            float delayTime = (mDelayTimeCurve.mode == ParticleSystemCurveMode.Constant ? mDelayTimeCurve.constant : mDelayTimeCurve.Evaluate(Random.value));

            if (OverrideVelocity)
            
                mParams.velocity = mSpeedCurve.Evaluate(Random.value) * mDir;
            
            mOverrideVelocity = false;
            if (mBurstList != null)
            
                for (int i = 0; i < mBurstList.Length; i++)
                
                    int count = mBurstList[i].count.mode == ParticleSystemCurveMode.Constant ? mBurstList[i].maxCount :
                        ((mBurstList[i].minCount == mBurstList[i].maxCount ? mBurstList[i].minCount :
                        Random.Range(mBurstList[i].minCount, mBurstList[i].maxCount)));

                    if (mOverrideStartLifeTime)
                    
                        mParams.startLifetime = mLifeTimeCurve.constant;
                        mOverrideStartLifeTime = false;
                    
                    else
                        mParams.startLifetime = (mLifeTimeCurve.mode == ParticleSystemCurveMode.Constant ? mLifeTimeCurve.constant : mLifeTimeCurve.Evaluate(Random.value)) * lifetimeScale;
                    mParams.startSize = (mSizeCurve.mode == ParticleSystemCurveMode.Constant ? mSizeCurve.constant : mSizeCurve.Evaluate(Random.value)) * sizeScale;

                    float t = delayTime + mBurstList[i].time;
                    Emit(t, 1);
                
            
            else
            
                if (mOverrideStartLifeTime)
                
                    mParams.startLifetime = mLifeTimeCurve.constant;
                    mOverrideStartLifeTime = false;
                
                else
                    mParams.startLifetime = (mLifeTimeCurve.mode == ParticleSystemCurveMode.Constant ? mLifeTimeCurve.constant : mLifeTimeCurve.Evaluate(Random.value));
                mParams.startSize = (mSizeCurve.mode == ParticleSystemCurveMode.Constant ? mSizeCurve.constant : mSizeCurve.Evaluate(Random.value));
                Emit(delayTime, 1);
            
        
    

    protected void Emit(float deltayTime, int count)
    
        if (deltayTime > 0)
        
            DOVirtual.DelayedCall(deltayTime, () =>  mParticleSys.Emit(mParams, count); );
        
        else
        
            mParticleSys.Emit(mParams, count);
        
    

新的粒子系统

更多关于新粒子系统的介绍链接

工程下载

https://github.com/Unity-Technologies/FPSSample

更多精品教程

http://dingxiaowei.cn 拷贝到浏览器访问

以上是关于程序员也应了解的Unity粒子系统的主要内容,如果未能解决你的问题,请参考以下文章

Unity的粒子系统(二)

Unity3D ParticleSystem粒子系统

Unity3D 粒子系统实现一个简单的爆炸效果

Unity3D 粒子系统实现一个简单的爆炸效果

Unity中如何做到这个模型转变成粒子的效果。

Unity的粒子系统(一)