[Unity 3D] Unity 3D 性能优化(二)

IsAlive

U3D的粒子系统脚本接口相信很多人都用过,ParticleSyetem类的一系列接口都有一个bool类型的参数——withChildren,通过这个参数可以直接将相同的判断或者操作应用到一整个通过Transform父子关系树关联起来的ParticleSystem实例集合上。然而,但凡方便的功能,里面就必然有性能陷阱……

以IsAlive这个接口为例(用来判断粒子系统是否所有粒子都已经消亡,一般用在非loop的例子发射器上),看看U3D里是如何实现这个接口的:

[csharp] view
plain
copy

  1. public bool IsAlive()  
  2. {  
  3.     bool withChildren = true;  
  4.     return this.IsAlive(withChildren);  
  5. }  

[csharp] view
plain
copy

  1. public bool IsAlive(bool withChildren)  
  2. {  
  3.     if (withChildren)  
  4.     {  
  5.         ParticleSystem[] particleSystems = ParticleSystem.GetParticleSystems(this);  
  6.         ParticleSystem[] array = particleSystems;  
  7.         for (int i = 0; i < array.Length; i++)  
  8.         {  
  9.             ParticleSystem particleSystem = array[i];  
  10.             if (particleSystem.Internal_IsAlive())  
  11.             {  
  12.                 return true;  
  13.             }  
  14.         }  
  15.         return false;  
  16.     }  
  17.     return this.Internal_IsAlive();  
  18. }  

可以看到,如果传递的withChildren参数为true,那么函数会先尝试调用GetParticleSystems(this)来获取包括下级gameObject在内的所有能找得到的粒子系统组件,然后对这些粒子系统组件依次再调用IsAlive判断。而如果withChildren为false,就仅仅会判断自身。那么自然,开销大小与否,关键就在GetParticleSystems的实现上了。

[csharp] view
plain
copy

  1. internal static ParticleSystem[] GetParticleSystems(ParticleSystem root)  
  2. {  
  3.     if (!root)  
  4.     {  
  5.         return null;  
  6.     }  
  7.     List<ParticleSystem> list = new List<ParticleSystem>();  
  8.     list.Add(root);  
  9.     ParticleSystem.GetDirectParticleSystemChildrenRecursive(root.transform, list);  
  10.     return list.ToArray();  
  11. }  

[csharp] view
plain
copy

  1. private static void GetDirectParticleSystemChildrenRecursive(Transform transform, List<ParticleSystem> particleSystems)  
  2. {  
  3.     foreach (Transform transform2 in transform)  
  4.     {  
  5.         ParticleSystem component = transform2.gameObject.GetComponent<ParticleSystem>();  
  6.         if (component != null)  
  7.         {  
  8.             particleSystems.Add(component);  
  9.             ParticleSystem.GetDirectParticleSystemChildrenRecursive(transform2, particleSystems);  
  10.         }  
  11.     }  
  12. }  

U3D对获取所有下级gameObject实例上的粒子系统组件使用了递归的方式,并在递归结束后返回结果列表时做了一次列表元素复制(List.ToArray()),并且在获取粒子系统组件的时候用的是transform2.gameObject.GetComponent<ParticleSystem>(),而不是transform2.GetComponent<ParticleSystem>(),从上一篇文章里我们已经用实验证实了,前一种方式开销更大。看到这里,我们心里大概已经有谱了,那就是——效率绝对不会高到哪里去,影响性能的地方太多了……还是设计一个小实验来看看这种情况下应该用什么样的方式更好吧:

设计实验——一个两层结构,一个父gameObject挂载一个ParticleSystem组件,两个子gameObject分别挂载一个PariticleSystem组件,采用两种不同的方式对这个组合判断IsAlive各8×1024×1024次。

方案一,直接对父gameObject上的PariticleSystem调用IsAlive(true);

方案二,在循环前,先用GetComponentsInChildren将所有的PariticleSystem存入一个List,循环中对这个List做遍历,对List里每一个ParticleSystem调用IsAlive(false);

实验结果——方案一约3900ms,方案二约65ms。

结果对比很明显。其实,U3D提供的这个接口的意义在于,当不是需要进行那么频繁的调用时,可以用IsAlive(true)来省掉手动获取所有子粒子系统的过程,让代码简洁一些,虽然U3D目前对这个接口的实现有的地方还值得斟酌。ParticleSystem提供的这一族接口(IsAlive只是其中之一,此外还有Play,Pause,Stop等等),如果使用频率不是很高,比如仅仅是初始化或者销毁的时候做一次性调用,那么即便是withChildren参数是true也没有什么大不了的,还能少些很多代码,何乐而不为;但如果需要频繁调用,比如每帧都对粒子系统集合判断IsAlive,这种情况下,一定不能懒惰,该写的东西还是要写的。另外值得注意的一点,IsAlive这一族接口的无参形式,是默认withChildren为true的,使用的时候可别搞错了。

PS,留意一下GetDirectParticleSystemRecursive的实现方式,你会发现它有一个递归条件,就是节点上必须要有PariticleSystem组件,在递归过程中,一旦发现某个节点上没有ParticleSystem组件时,父子关系树上的这一枝就算遍历到头了,再往下即便是还有ParticleSystem存在也会被忽略。因此,如果你面对的ParticleSystem集合就恰好存在这样的断层,那最好还是自己勤快一点,自己动手用GetComonentsInChildren来查找所有的粒子系统组件。

时间: 2024-09-21 00:16:18

[Unity 3D] Unity 3D 性能优化(二)的相关文章

mybatis性能优化二之多对多查询:用一次请求解决n次请求查询

<resultMap type="com.cn.vo.Teacher" id="teacher"> <id property="id" column="id" javaType="int" jdbcType="INTEGER" /> <result property="name" column="name" javaT

AngularJS 性能优化二三事 【已翻译100%】

在过去的两年中,angularjs 的使用率有了很大的提升.简单的双向绑定是促使其流行的主要原因之一,但与此同时,它导致了在一个应用中出现了大量的 watcher.在后台,angularJS 会执行所谓的 "脏值检查".之所以要做脏值检查目的是为了检查在 "scope" 中的数据是否发生了变化;框架必须周期性的检测数据是否发生了变化- 脏值检查会每 40ms 更新一次.. 想想看.. 如果它需要检查超多数据的时候; Crap!! 让我们来看看如何改善 angular

HTTP/2 与 WEB 性能优化(三)

在连续写了两篇关于「HTTP/2 与 WEB 性能优化」的文章后,今天来写这个系列的最后一篇.在正式开始之前,我们先来简单回顾下之前两篇文章: 「HTTP/2 与 WEB 性能优化(一)」的结论是:HTTP/2 的 Server Push 机制,可以让重要的 JS.CSS 等资源尽快加载,从而不再需要 HTTP/1 中「将重要资源内联在页面头部」的优化方案了. 「HTTP/2 与 WEB 性能优化(二)」的结论是:HTTP/2 支持了多路复用,HTTP 连接变得十分廉价,之前为了节省连接数所采用

【原创】Python 之快速性能优化(第二部分)

本文为翻译,原文地址:< Quick Python Performance Optimization: Part II >  This is the Part II of Quick Python Performance Optimizations. 本文是 Python 性能优化二两发的第二部分.  11. Use Map, Reduce and Filter instead of for loop where ever possible. 11. 尽可能使用 Map,Reduce 和 Fi

JVM性能优化系列

JVM性能优化(一)JVM技术入门 JVM 性能优化 (二)  编译器 JVM性能优化(三)垃圾收集 JVM性能优化(四)并发压缩GC JVM性能优化(五)可扩展性 转载自 并发编程网 - ifeve.com

Mysql数据库性能优化三(分表、增量备份、还原)_MsSql

接上篇Mysql数据库性能优化二 对表进行水平划分           如果一个表的记录数太多了,比如上千万条,而且需要经常检索,那么我们就有必要化整为零了.如果我拆成100个表,那么每个表只有10万条记录.当然这需要数据在逻辑上可以划分.一个好的划分依据,有利于程序的简单实现,也可以充分利用水平分表的优势.比如系统界面上只提供按月查询的功能,那么把表按月拆分成12个,每个查询只查询一个表就够了.如果非要按照地域来分,即使把表拆的再小,查询还是要联合所有表来查,还不如不拆了.所以一个好的拆分依据

Mysql数据库性能优化三(分表、增量备份、还原)

接上篇Mysql数据库性能优化二 对表进行水平划分 如果一个表的记录数太多了,比如上千万条,而且需要经常检索,那么我们就有必要化整为零了.如果我拆成100个表,那么每个表只有10万条记录.当然这需要数据在逻辑上可以划分.一个好的划分依据,有利于程序的简单实现,也可以充分利用水平分表的优势.比如系统界面上只提供按月查询的功能,那么把表按月拆分成12个,每个查询只查询一个表就够了.如果非要按照地域来分,即使把表拆的再小,查询还是要联合所有表来查,还不如不拆了.所以一个好的拆分依据是 最重要的.关键字

Asp.net性能优化总结(二)

asp.net|性能|优化 Asp.net性能优化总结 一.使用存储过程: 性能方面:存储过程提供了许多标准sql语言中所没有的高级特性.其传递参数和执行逻辑表达式的功能,有助于应用程序设计者处理复杂任务.另外,存储过程存储在本地服务器上,减少了执行该过程所需的网络传输宽带和执行时间.(存储过程已经对sql语句进行了预编译,所以其执行速度比在程序里执行sql语句快很多) 程序结构方面:从程序的可扩展性看,使用存储过程会对程序以后的修改带来方便.比如数据库的结构改变了,只需修改相对应的存储结构,和

秋色园QBlog技术原理解析:性能优化篇:字节、缓存、并发(十二)

文章回顾: 1: 秋色园QBlog技术原理解析:开篇:整体认识(一) --介绍整体文件夹和文件的作用 2: 秋色园QBlog技术原理解析:认识整站处理流程(二) --介绍秋色园业务处理流程 3: 秋色园QBlog技术原理解析:UrlRewrite之无后缀URL原理(三) --介绍如何实现无后缀URL 4: 秋色园QBlog技术原理解析:UrlRewrite之URL重定向体系(四) --介绍URL如何定位到处理程序 5: 秋色园QBlog技术原理解析:Module之页面基类设计(五) --介绍创建

Jquery 学习笔记(二)jQuery性能优化指南

Jquery 学习笔记(二) -jQuery性能优化指南 2009年11月30日 一 作者:   邦畿千里   1,总是从ID选择器开始继承 在jQuery中最快的选择器是ID选择器,因为它直接来自于JavaScript的getElementById()方法. 例如有一段HTML代码: <div id="content"> <form method="post" action="#"> <h2>交通信号灯<