当前位置:网站首页>程序员也应了解的Unity粒子系统
程序员也应了解的Unity粒子系统
2022-08-04 05:25:00 【丁小未】
前言
曾经我们是不是以为跟粒子系统打交道多的是特效美术?曾经我们是不是以为改变粒子的位置是不是只要设置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 拷贝到浏览器访问
边栏推荐
猜你喜欢
嵌入式系统驱动初级【4】——字符设备驱动基础下_并发控制
代码重构:面向单元测试
你以为border-radius只是圆角吗?【各种角度】
CentOS7 —— yum安装mysql
Summary of MySQL database interview questions (2022 latest version)
MySQL数据库面试题总结(2022最新版)
Can‘t connect to MySQL server on ‘localhost3306‘ (10061) 简洁明了的解决方法
Towards Real-Time Multi-Object Tracking (JDE)
Wwise入门和实战
符号表
随机推荐
[One step in place] Jenkins installation, deployment, startup (complete tutorial)
npm安装依赖报错npm ERR! code ENOTFOUNDnpm ERR! syscall getaddrinfonpm ERR! errno ENOTFOUND
[Evaluation model] Topsis method (pros and cons distance method)
注意!软件供应链安全挑战持续升级
What is the salary of a software testing student?
Do you think border-radius is just rounded corners?【Various angles】
As soon as flink cdc is started, the CPU of the source Oracle server soars to more than 80%. What is the reason?
Unity表格配置编辑工具
力扣:509. 斐波那契数
嵌入式系统驱动初级【3】——字符设备驱动基础中_IO模型
7.13 Day20----MYSQL
Unity自动生成阻挡Collider的GameObject工具
C Expert Programming Chapter 5 Thinking about Linking 5.3 5 Special Secrets of Library Linking
Grain Mall - Basics (Project Introduction & Project Construction)
一个对象引用的思考
8.03 Day34---BaseMapper query statement usage
想低成本保障软件安全?5大安全任务值得考虑
12、分页插件
[SemiDrive source code analysis] [MailBox inter-core communication] 47 - Analysis of RPMSG_IPCC_RPC mode limit size of single transmission and limit bandwidth test
Cannot read properties of null (reading 'insertBefore')