关于Unity ParticleSystem的一些冷知识

2024-04-12 21:18
文章标签 知识 unity particlesystem

本文主要是介绍关于Unity ParticleSystem的一些冷知识,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  目前的游戏开发中,粒子系统的使用想必是标配了,Unity自然也提供了相应的解决方案:ParticleSystem,网上对ParticleSystem的介绍也非常之多(譬如这里这里这里还有这里),虽然相关的资料教程不少,但多是一些如何使用之类的常规信息,对于一些虽然有些“生冷”,但仍然可能遇到的粒子知识,网上涉及的资源便不多了,本篇就随便讲讲自己遇到的几个ParticleSystem的“冷”知识~

 

  1. 如何在EditorPlayback粒子效果?

 

  Unity本身自带ParticleEditor,正常情况下选中所编辑的粒子即可Playback,但是当我们切换选择到其他GameObject的时候,之前粒子的Playback便停止了,这在某些时候可能会给我们开发带来不便,考虑你基于Unity开发了一个表现预览器,预览器里显示的内容可能包括角色模型,技能特效等等各种内容,如果我们在编辑器中选择了某个GameObject节点,我们的期望自然是在视图中可以看到挂接在该GameObject下的所有显示效果,其中几乎肯定会有粒子特效,如果你简单的尝试调用一下ParticleSystemPlay接口,就会发现在Editor模式下粒子并不会如期的进行Playback……

  

  一个Workaround的方案是仅在UnityEditorPlay模式下才提供粒子的Playback,这在一般情况下是可以接受的,但是算不上一个很好的方案,更好的解决方法还是实现Editor模式下的粒子Playback功能,能办到吗?

 

  其实是可以的,其中的关键是用好ParticleSystem.Simulate接口~

 

  简单看下该接口的API说明:

 

  Fastforwards the particle system by simulating particles over given period of time, then pauses it.

 

  该接口能够模拟粒子快进一段指定时间并停止粒子,Nice,如果我们在Editor下的Update中持续对该接口进行调用,并传递deltaTime作为时间参数,那么粒子便能在EditorPlayback起来了!

 

  简单看下示例代码:


// desc simple particle playback window
// maintainer hugoyuusing System;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;public class ParticlePlaybackWindow : EditorWindow
{// implementation details here// Called multiple times per second on all visible windows.void Update(){if (m_playbackState == PlaybackState.Playing){if (m_particlePlayback.HaveParticleSystems()){// simulate particlesif (m_lastUpdateTime <= 0){m_lastUpdateTime = Time.realtimeSinceStartup;}m_particlePlayback.Update(Time.realtimeSinceStartup - m_lastUpdateTime);m_lastUpdateTime = Time.realtimeSinceStartup;// repaint viewsEditorUtil.RepaintView();}}}// implementation details here}

  其中的核心代码便是这句

 

  m_particlePlayback.Update(Time.realtimeSinceStartup - m_lastUpdateTime);

 

  简单看下ParticlePlayback的实现:


// desc simple particle play back implementation
// maintainer hugoyuusing System.Collections.Generic;
using System.Collections.ObjectModel;
using UnityEngine;
using UnityEditor;class ParticlePlayback
{// implementation details herepublic void Update(float deltaTime){for (int i = 0; i < m_particleSystems.Count; ++i){// NOTE need false params here since default params are all truem_particleSystems[i].Simulate(deltaTime, false, false);}}// implementation details here}

2. 粒子可以Scale吗?

 

  对于很早便开始接触Unity的朋友,对于这个问题的回答可能是否定的,至少在运行模式下是否定的,不过这个观点已经过时了,现在我们已经可以很方便的缩放ParticleSystem了,至于如何做,容我细细道来~

 

  Unity5.3版本之前,ParticleSystem并不能很好的受Transform的缩放影响,为了达到Scale的目的,一个方法是根据Scale动态的改变ParticleSystem的各项粒子属性,譬如大小、速度等等,但是由于粒子的不少属性并没有暴露给脚本使用,导致必须使用SerializedObject来进行反射获取,这也进一步导致该方法只能在编辑器环境下使用,网上相关的资料不少也是该方法的不同实现(譬如这里这里),简单列下示例代码:


void ScaleShurikenSystems(float scaleFactor)
{
#if UNITY_EDITOR //get all shuriken systems we need to do scaling onParticleSystem[] systems = GetComponentsInChildren<ParticleSystem>();for (int index = 0; index < systems.Length; index++){ParticleSystem system = systems[index];system.startSpeed *= scaleFactor;system.startSize *= scaleFactor;system.gravityModifier *= scaleFactor;//some variables cannot be accessed through regular script, we will acces them through a serialized objectSerializedObject so = new SerializedObject(system);//unity 4.0 and onwards will already do this one for us
#if UNITY_3_5 so.FindProperty("ShapeModule.radius").floatValue *= scaleFactor;so.FindProperty("ShapeModule.boxX").floatValue *= scaleFactor;so.FindProperty("ShapeModule.boxY").floatValue *= scaleFactor;so.FindProperty("ShapeModule.boxZ").floatValue *= scaleFactor;
#endifso.FindProperty("VelocityModule.x.scalar").floatValue *= scaleFactor;so.FindProperty("VelocityModule.y.scalar").floatValue *= scaleFactor;so.FindProperty("VelocityModule.z.scalar").floatValue *= scaleFactor;so.FindProperty("ClampVelocityModule.magnitude.scalar").floatValue *= scaleFactor;so.FindProperty("ClampVelocityModule.x.scalar").floatValue *= scaleFactor;so.FindProperty("ClampVelocityModule.y.scalar").floatValue *= scaleFactor;so.FindProperty("ClampVelocityModule.z.scalar").floatValue *= scaleFactor;so.FindProperty("ForceModule.x.scalar").floatValue *= scaleFactor;so.FindProperty("ForceModule.y.scalar").floatValue *= scaleFactor;so.FindProperty("ForceModule.z.scalar").floatValue *= scaleFactor;so.FindProperty("ColorBySpeedModule.range").vector2Value *= scaleFactor;so.FindProperty("SizeBySpeedModule.range").vector2Value *= scaleFactor;so.FindProperty("RotationBySpeedModule.range").vector2Value *= scaleFactor;so.ApplyModifiedProperties();}
#endif
}

  另外的一种缩放粒子的方式是使用定制的Shader,这种方法便不存在只能在Editor下使用的限制了,但是需要为所有粒子使用到的Shader扩展Scale功能,感觉上也是略有不便,网上也有不少相关的介绍,譬如这里这里,有兴趣的朋友可以细致看看,在此便不列代码了。

 

  Unity5.3之后,ParticleSystem为脚本导出了相关的属性接口,并且很好的支持了TransformScale变换,相关的介绍可以看这里这里这里还有个小讨论),同样的,我们仍然可以沿用之前改变粒子属性的方法来达到缩放的目的,只是这次我们不再需要SerializedObject了,自然代码也“清新”了许多,gist上有段示例代码,大概贴一下:


static void ScaleSystem(ParticleSystem particles, float scale, bool scalePosition, ParticleScalerOptions options = null)
{if (options == null) { options = defaultOptions; }if (scalePosition) { particles.transform.localPosition *= scale; }particles.startSize *= scale;particles.gravityModifier *= scale;particles.startSpeed *= scale;if (options.shape){var shape = particles.shape;shape.radius *= scale;shape.box = shape.box * scale;}if (options.velocity) {var vel = particles.velocityOverLifetime;vel.x = ScaleMinMaxCurve(vel.x, scale);vel.y = ScaleMinMaxCurve(vel.y, scale);vel.z = ScaleMinMaxCurve(vel.z, scale);}if (options.clampVelocity) {var clampVel = particles.limitVelocityOverLifetime;clampVel.limitX = ScaleMinMaxCurve(clampVel.limitX, scale);clampVel.limitY = ScaleMinMaxCurve(clampVel.limitY, scale);clampVel.limitZ = ScaleMinMaxCurve(clampVel.limitZ, scale);}if (options.force) {var force = particles.forceOverLifetime;force.x = ScaleMinMaxCurve(force.x, scale);force.y = ScaleMinMaxCurve(force.y, scale);force.z = ScaleMinMaxCurve(force.z, scale);}
}

  不过个人感觉更简洁的方式,还是使用ParticleSystemScalingMode.Hierarchy,简单设置一下粒子的scalingModeHierarchy,之后便可以简单的调整Hierarchy层级的Transform缩放了,如果使用ParticleSystemScalingMode.Local,缩放粒子也并不困难,概念上和Hierarchy也是类似的,大概贴下代码:


static float GetScaleInternal(ParticleSystem particleSystem)
{Debug.Assert(particleSystem != null);var localScale = particleSystem.transform.localScale;// NOTE now we assume local scale component values are always same, pay attentionDebug.Assert(MathUtil.IsEqual(localScale.x, localScale.y, localScale.z));return localScale.x;
}static void ScaleInternal(ParticleSystem particleSystem, float scale)
{Debug.Assert(particleSystem != null);// set scaling mode and adjust local scale and gravityparticleSystem.scalingMode = ParticleSystemScalingMode.Local;var oldScale = GetScaleInternal(particleSystem);particleSystem.transform.localScale = Vector3.one * scale;// NOTE now we assume gravityModifier is always sync local scale value, pay attentionparticleSystem.gravityModifier *= (scale / oldScale);
}public static void Scale(ParticleSystem particleSystem, float scale, bool includeChildren)
{if (particleSystem != null){ScaleInternal(particleSystem, scale);if (includeChildren){var childrenParticleSystems = particleSystem.GetComponentsInChildren<ParticleSystem>();for (int i = 0; i < childrenParticleSystems.Length; ++i){if (childrenParticleSystems[i] != particleSystem){ScaleInternal(childrenParticleSystems[i], scale);}}}}
}

  至于ParticleSystemScalingMode.Shape,该模式下,TransformScale只会影响粒子的Shape大小,并不会影响诸如粒子大小速度之类的其他属性~

 

  最后贴张测试图,从左到右的三个ParticleSystem的属性都是相同的,差别仅在scalingMode上,第一个为Hierarchy,第二为Local,第三为Shape ~




  现在我们尝试缩放粒子,第一和第三都仅是调整TransformScale数值,第二个我们通过改变粒子属性值来进行缩放,结果如下:



  

  可以看到,第一个粒子“完美”的被放大了,第三个粒子确实只有EmitShape被放大了,至于第二个粒子感觉可能有些奇怪,似乎是部分被放大的感觉,其实是因为我们使用了改变粒子属性的方法,遂而导致旧粒子不会受到缩放影响,于是产生了上述效果~


  OK,粒子系统的“冷”知识大概就讲这么多吧,下次再见了~



这篇关于关于Unity ParticleSystem的一些冷知识的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/898257

相关文章

一文详解Java异常处理你都了解哪些知识

《一文详解Java异常处理你都了解哪些知识》:本文主要介绍Java异常处理的相关资料,包括异常的分类、捕获和处理异常的语法、常见的异常类型以及自定义异常的实现,文中通过代码介绍的非常详细,需要的朋... 目录前言一、什么是异常二、异常的分类2.1 受检异常2.2 非受检异常三、异常处理的语法3.1 try-

国内环境搭建私有知识问答库踩坑记录(ollama+deepseek+ragflow)

《国内环境搭建私有知识问答库踩坑记录(ollama+deepseek+ragflow)》本文给大家利用deepseek模型搭建私有知识问答库的详细步骤和遇到的问题及解决办法,感兴趣的朋友一起看看吧... 目录1. 第1步大家在安装完ollama后,需要到系统环境变量中添加两个变量2. 第3步 “在cmd中

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

sqlite3 相关知识

WAL 模式 VS 回滚模式 特性WAL 模式回滚模式(Rollback Journal)定义使用写前日志来记录变更。使用回滚日志来记录事务的所有修改。特点更高的并发性和性能;支持多读者和单写者。支持安全的事务回滚,但并发性较低。性能写入性能更好,尤其是读多写少的场景。写操作会造成较大的性能开销,尤其是在事务开始时。写入流程数据首先写入 WAL 文件,然后才从 WAL 刷新到主数据库。数据在开始

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

【Python知识宝库】上下文管理器与with语句:资源管理的优雅方式

🎬 鸽芷咕:个人主页  🔥 个人专栏: 《C++干货基地》《粉丝福利》 ⛺️生活的理想,就是为了理想的生活! 文章目录 前言一、什么是上下文管理器?二、上下文管理器的实现三、使用内置上下文管理器四、使用`contextlib`模块五、总结 前言 在Python编程中,资源管理是一个重要的主题,尤其是在处理文件、网络连接和数据库

dr 航迹推算 知识介绍

DR(Dead Reckoning)航迹推算是一种在航海、航空、车辆导航等领域中广泛使用的技术,用于估算物体的位置。DR航迹推算主要通过已知的初始位置和运动参数(如速度、方向)来预测物体的当前位置。以下是 DR 航迹推算的详细知识介绍: 1. 基本概念 Dead Reckoning(DR): 定义:通过利用已知的当前位置、速度、方向和时间间隔,计算物体在下一时刻的位置。应用:用于导航和定位,

Unity Post Process Unity后处理学习日志

Unity Post Process Unity后处理学习日志 在现代游戏开发中,后处理(Post Processing)技术已经成为提升游戏画面质量的关键工具。Unity的后处理栈(Post Processing Stack)是一个强大的插件,它允许开发者为游戏场景添加各种视觉效果,如景深、色彩校正、辉光、模糊等。这些效果不仅能够增强游戏的视觉吸引力,还能帮助传达特定的情感和氛围。 文档

【H2O2|全栈】Markdown | Md 笔记到底如何使用?【前端 · HTML前置知识】

Markdown的一些杂谈 目录 Markdown的一些杂谈 前言 准备工作 认识.Md文件 为什么使用Md? 怎么使用Md? ​编辑 怎么看别人给我的Md文件? Md文件命令 切换模式 粗体、倾斜、下划线、删除线和荧光标记 分级标题 水平线 引用 无序和有序列表 ​编辑 任务清单 插入链接和图片 内嵌代码和代码块 表格 公式 其他 源代码 预

Unity协程搭配队列开发Tips弹窗模块

概述 在Unity游戏开发过程中,提示系统是提升用户体验的重要组成部分。一个设计良好的提示窗口不仅能及时传达信息给玩家,还应当做到不干扰游戏流程。本文将探讨如何使用Unity的协程(Coroutine)配合队列(Queue)数据结构来构建一个高效且可扩展的Tips弹窗模块。 技术模块介绍 1. Unity协程(Coroutines) 协程是Unity中的一种特殊函数类型,允许异步操作的实现