1.Avoid Find() and SendMessage() at runtime

SendMessage() 方法和 GameObject.Find() 相关的一系列方法都是开销非常大的。SendMessage()函数调用的耗时大约是一个普通函数调用的2000倍,GameObject.Find() 则和场景的复杂度相关,场景越复杂,调用自然越慢。有时候在场景的初始化时,比如Awake和Start函数中调用Find函数是可以理解的,但是即使是这样,也应该是用来获取一定已经存在于这个场景的objects并且场景内的物体最好越少越好。在runtime运行时调用这些函数,都将会带来很大的开销,从而有可能会引起帧率下降。

依赖Find()和SendMessage()函数是架构设计代码设计中一个典型不给力的表现, 这通常是初学者经常犯的错,尽管Unity已经在文档中一遍又一遍的建议大家避免使用这些函数。

尽可能的不使用这些函数甚至打破了我们之前的原则:只有需要优化的时候再优化,不要过度的提前优化。为了避免使用这些函数,我们应该在代码的原型阶段就设计好。

让我们举个栗子,下边的是一个简单的EnemyManagerComponent类,它的作用是存储一个GameObject的List,用来表示游戏中的敌人,并且提供一个KillAll()方法在需要的时候去销毁他们

之后我们会在Scene中放置一个GameObject并且加入这个脚本,把这个物体叫做EnemyManager。

下边的代码会通过Prefab生成一些敌人,并且通知EnemyManager它们的存在

在循环体内初始化数据和调用函数是一个很危险的行为,这会有可能带来很差的性能表现,但我们调用的是开销很大的函数例如Find()时,我们应该尽可能的寻找方法减少调用次数。因此,一个优化点就是用一个本地变量用于保存,将函数调用提到循环外边。另一个很重要的优化是用GetComponet()代替SendMessage(),这开销会小很多。优化后的代码如下:

有很多种方法都能给这个小节提出的问题带来优化,每一种有各自的优缺点:

1.用已经存在的Object保存引用

2.Static 类

3.单例

4.全局的消息系统

2.Assigning references to preexisting objects

一个简单的解决 interobject communication 问题的方法是使用Unity内置的序列化系统可以解决。也就是俗称的在Hierarchy里直接拖拽GameObject或者Prefab到面板对应变量上。但是public属性违反了类的封装原则,这是很危险的,因此可以使用[SerializeField]这个attribute(特性),这样可以使得private 和 protect的属性也能在Inspector中序列化。比如下边的例子:

但是要注意的是拖拽的操作有可能拖拽不合适类型的物体或者忘记处理而变成null。还有就是Unity不能序列化static 和 readonly修饰的变量和属性等。

3.Static Classes

虽然Static Classes有不方便调试,不便于修改和扩展功能(在系统中到处直接引用)等等缺点,但是却是目前非常简便的一种解决方案。单例模式是一种非常普遍常用的设计模式,它保证内存中同一类型只有一个实例。单例模式在处理重度的数据传输,比如读取文件下载解析等时非常适合。单例未必需要是全局的,它们最重要的特性是只有一个实例。单例最简单的一种实现方法就是通过C#的Static Class(静态类)。

把上一小节中的例子改成用Static Class实现,代码如下:

Static Class中所有的方法,属性等都必须是static类型,Static Class中的字段可以直接初始化,也可以通过构造函数初始化。

Static Class的缺点是没办法和Unity中的Inspector window结合,也就是没法像Monobehaviours一样使用,有时候就得写一个匹配的辅助类来帮忙:

尽管有这些缺点,但是使用Static Classes这个方法也要比使用Find()和SendMessage()强的多。

4.Singleton Components

前一节提到过,Static Class无法和Unity一起顺利工作,不能使用MonoBehaviour的各种特性也不能在运行时在Inspector window中看到,从而难于调试。因此实现一个基于MonoBehaviour的单例是一个不错的解决方案:

最简单的使用方式如下边代码所示:

这个方案的缺点是要注意有可能DontDestroyOnLoad()永远不会被调用到,最好是在子类的Awake防范重调用下。当然在有些时候在切换场景时销毁再重新创建也是不错的选择,一切都根据使用场景决定。

另外要注意的是OnDestroy的危险,比如观察者模式,很多物体的取消注册时机都写在了OnDestroy函数里,但是Unity并不保证OnDestroy的时序,因此有可能当某个物体调用自己的OnDestroy时,调用到了已经销毁的单例,这有可能带来致命的错误。

最好的解决方案就是永远不要在OnDestroy函数里调用单例,但是如果非要使用,解决方案如下:

第一步:我们需要加个标志,用来跟踪单例是否存活

第二步:要提供一个途径来获取单例是否存活的状态

最后,任何在Destroy函数里调用单例的地方都要先去验证

这个单例方案也使用到了Find方法,但是只是在初始化时调用一次,因此还可以接受。但是它的初始化时机可能并不是在场景的初始化时,而是在第一次使用时,因此有可能在那个时机会给性能带来影响。因此也可以在场景的初始化时调用单例来保证其在场景初始化时就初始化好。

另外一个缺点就是如果以后我们想改掉这个单例模式,变成可能有多个实例或者想把它变得更模块化,代码的改动量将会非常大。

Unity 2017 Game Optimization 读书笔记(4)Scripting Strategies Part 4相关推荐

  1. Unity 2017 Game Optimization 读书笔记(1)Scripting Strategies Part 1

    1.Obtain Components using the fastest method Unity有多种Getcomponet的方法: GetComponent(string), GetCompon ...

  2. Unity 2017 Game Optimization 读书笔记 Scripting Strategies Part 5

    一. Disable unused scripts and objects 场景中激活的物体或者脚本越多,开销越大.对于很多并没有产生作用的脚本和物体,可以隐藏掉从而提升性能,比如FPS游戏中视野外的 ...

  3. Unity 2017 Game Optimization 读书笔记(3)Scripting Strategies Part 3

    1.Avoid retrieving string properties from GameObjects 通常来讲,从C#的object中获取string 属性没有额外的内存开销,但是从Unity中 ...

  4. Unity 2017 Game Optimization 读书笔记(2)Scripting Strategies Part 2

    1. Share calculation output 和上一个Tip很像,可以缓存计算结果或者各种信息,避免多次重复的计算,例如在场景里查找一个物体,从文件读取数据,解析Json等等. 容易忽略的点 ...

  5. Unity 2017 Game Optimization 读书笔记 Dynamic Graphics (6)

    1. Use less texture data 这条优化技巧非常直接,减少texture的数据量,减少分辨率或者降低位数,虽然可能会降低渲染质量.但是通常使用16-bit textures并不会明显 ...

  6. Unity 2017 Game Optimization 读书笔记 Dynamic Graphics (5) Shader优化

    Shader optimization Fill Rate和 Memory Bandwidth开销最大的地方就是Fragment Shader.开销多大取决于Fragment Shader的复杂程度: ...

  7. Unity 2017 Game Optimization 读书笔记 Dynamic Graphics (4)

    Optimizing Unity UI 本章讲探讨一些能够提升UGUI性能的优化方法. 1.Use more Canvases 一个Canvas的主要任务就是管理它层级下的所有UI元素,并且通过Dra ...

  8. Unity 2017 Game Optimization 读书笔记 Dynamic Graphics (3)

    Rendering performance enhancements Enable/Disable GPU Skinning 开启GPU Skinning可以减轻CPU或GPU中Front End部分 ...

  9. Unity 2017 Game Optimization 读书笔记 The Benefits of Batching

    batching(合批) 和大量的描述一个3D物体的数据有关系,比如meshes,verices,edges,UV coordinates 以及其他不同类型的数据.在Unity中谈论batching, ...

最新文章

  1. 清华大学唐杰老师组:自监督学习最新研究进展
  2. Java中四则运算的那些坑
  3. 皮一皮:皇上,他在下毒!
  4. UVA11549计算器谜题
  5. Java 程序死锁问题原理及解决方案
  6. 添加功能与测试点总结
  7. 模板类 Template Classes 以及模板类编译时的处理
  8. SQLServer优化二
  9. javascript-引入-函数的定义与使用-多值传参-ao对象
  10. MACOS-Can't-connect-to-local-MySQL-server-through-socket-'/tmp/mysql.sock'
  11. 花书+吴恩达深度学习(二)非线性激活函数(ReLU, maxout, sigmoid, tanh)
  12. javascript 语言国际化
  13. 【OpenCV学习笔记】【函数学习】三(cvGetCaptureProperty函数)
  14. avalon2学习教程14动画使用
  15. 三菱modbusRTU通讯实例_PLC编程实例 | 讲解食品和药品成型案例程序!
  16. ExtJS EditorGridPanel 示例之JSON格式Store前后台增删改查
  17. 马蜂窝火车票系统服务化改造初探
  18. 重学前端第一天——HTML结构和常见的HTML元素
  19. Google搜索命令语法
  20. 解决word中Mathtype按钮灰色问题(亲测有效)

热门文章

  1. python加载模型文件进行图片分类_tensorflow通过模型文件,使用tensorboard查看其模型图Graph方式...
  2. free mybatis 不生效_2019BATJ面试题汇总详解:MyBatis+MySQL+Spring+Redis+多线程
  3. php中的自定义函数与c语言有什么区别,php与c语言的不同点是什么?
  4. java什么内部类_Java的内部类学习
  5. [蓝桥杯][算法提高VIP]项链(dfs)
  6. django 怎么加权限 静态资源目录_Django:settings中关于static静态文件目录的设置...
  7. php escape undfine,PHP中处理 undefined variable的方法 | Soo Smart!
  8. linux 中断奶乱码,科学断奶经历:早中晚三次母乳,一个月内循序渐进自然断奶...
  9. 制作html5谭木记页面,北斗区域地理配套练习答案
  10. 深度学习之生成对抗网络(4)GAN变种