程序员也应了解的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 拷贝到浏览器访问

  • 11
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值