UPL5-6——本机-托管的桥接
UPL5-6——本机-托管的桥接 本机-托管的桥接 本机-托管的桥接(Native-Managed Bridge)指的是 C# 的托管代码与 C/C++ 的本机代码之间的相互调用或通信机制 Unity 本身就是由 C++ 实现的引擎内核 而我们日常写的是 C# 脚本,它们之间必须有桥梁来通信 托管(Managed): 主要指的是 C# 代码,它的执行环境是 CLR、Mono、IL2CPP 本机(Native): 主要指的是 C/C++ 代码,它的执行环境为直接运行于 CPU(无虚拟机概念) 桥接的产生 Unity 在执行 C# 脚本时,需要让 C# 能调用到底层引擎的 C++ 函数(比如渲染、物理),这就产生了两个方向的桥接 托管 到 本机 即 C# 调用 Unity 引擎内部的 C++ 函数 比如访问 Unity 提供的 GameObject、MonoBehaviour 类中的各种属性和方法 这是通过 IL2CPP 或 Mono 的内部调用机制来桥接到 Unity 的 C++ 本机 到 托管 即 C++ 层主动调用 C# 方法,比如原生插件调用...
UPL5-5——延迟执行函数相关
UPL5-5——延迟执行函数相关 Invoke 和 InvokeRepeating 存在的问题 Unity 中自带的延迟执行函数 Invoke 和定时调用函数 InvokeRepeating 它们的使用可以非常简单的让我们达到目的,但是它们会有明显的性能和可维护性问题 它们的主要问题有以下几点: 通过字符串查找(反射)的方式调用函数,性能消耗较高 可读性差,通过字符串无法直接跳转到函数排查问题 无法传参,调用这两个方法时无法传递参数,必须用成员变量来传值 控制不便,我们想要停止他们,需要通过 CancelInvoke 方法传入字符串关闭 等等 利用协程替代 Invoke 和 InvokeRepeating 做法:利用协同程序来完成延迟执行逻辑或定时调用逻辑需求 优点:支持传参,更易管理,更加灵活 缺点:协程有额外开销 利用 UniTask 替代 Invoke 和 InvokeRepeating 做法:利用 UniTask 中的API,比如 UniTask.Delay 来完成延迟调用 优点:开销比起协同程序更低,结合 async 和 await...
UPL5-4——协程相关
UPL5-4——协程相关 协同程序的额外开销 上一章的 Update 优化策略当中提到,我们可以通过降低 Update 的执行频率以及分帧执行来提升效率 很容易联想到使用协程来实现这一目的,但是我们当时并没有提及协程 主要原因就是,使用协程其实会带来一些额外开销,与标准函数调用相比: 启动协程会带来额外的开销成本,大约是标准函数调用的 3 倍 启动协程会额外分配一些内存,用于存储当前协程的状态 由于协程中会使用 yield,那么每次执行 yield 逻辑,都会一次次的产生相同的开销成本 因此我们在使用协程时,要考虑这些成本的消耗 是否小于 它带来的好处才使用它 利用 Update 替代协程处理时间 某些基于时间的操作(如倒计时)也可以在 Update 中配合 Time.deltaTime 实现,无需协程 避免协程中逻辑过于复杂 如果协程嵌套调用、状态跳转多时,维护成本会更高,会让问题难以追踪,调试困难 因此我们要尽量避免这种情况的发生 避免大量并发的协程 如果场景中大量对象同时启动协程,会造成大量的调度开销和 GC...
UPL5-3——Update 相关
UPL5-3——Update 相关 移除空 Update 如果在脚本中定义了生命周期函数,但没有在其中写任何逻辑,他们仍然会带来额外的性能开销 因此我们要避免在脚本中写空的生命周期函数 特别是那些会每帧调用的生命周期函数 Update LateUpdate FixedUpdate OnGUI 需要在 Update 中频繁使用的组件应该缓存 在 Unity 中反复获取一个组件是一种常见错误,对于一些常用的组件,我们不应该每次都重复的去获取, 应当通过泛型获取组件的方式将其缓存下来 我们应该避免在 Update 中调用如: GetComponent 寻找组件相关API Find 寻找对象相关API 等等 降低执行频率 我们时长会在 Update 中重复执行一些逻辑,如果超出需要的频率重复调用某些代码,会造成性能的浪费 比如:怪物 AI 中,怪物需要不停的朝玩家寻路并移动,如果我们在 Update...
UPL5-2——脚本获取相关
UPL5-2——脚本获取相关 用最快方式获取组件 Unity 中获取组件得到方式主要是通过调用 GetComponent 方法,它提供了几种重载: Component GetComponent(Type type):通过类型的Type获取 T GetComponent<T>():通过泛型获取 Component GetComponent(string type):通过字符串获取 在日常开发时,如果要获取组件,一定要选中速度最快的,也就是通过泛型的方式去获取 1234567891011121314151617181920212223242526272829303132IEnumerator Test(){ uint nums = 1000000; yield return null; using (new CodeTimer("GetComponent(string type)", nums)) { for (int i = 0; i < nums; i++) ...
UPL5-1——计时判断代码执行效率
UPL5-1——计时判断代码执行效率 本章代码关键字 1234567Time.realtimeSinceStartup // 得到Unity程序从启动以来的时间Stopwatch // 用于高精度计时的工具类,可以精确地测量代码执行的时间Stopwatch.StartNew() // 该方法会返回一个Stopwatch对象,并开始计时stopwatch.Stop() // 该方法通过计时对象调用,将停止计时stopwatch.ElapsedMilliseconds // 通过计时对象调用,获取耗时时间(毫秒),返回类型为 long,不使用与小于 1ms 的时间获取stopwatch.Elapsed.TotalMilliseconds // 该成员通过计时对象调用,获取耗时时间(毫秒),返回类型为 double,可以精确到微秒级stopwatch.Restart() ...
UPL5——脚本性能优化
UPL5——脚本性能优化 计时判断代码执行效率 脚本获取相关 Update 相关 协程相关 延迟执行函数相关 GameObject 相关 本机-托管的桥接 GameObject 优化建议 Transform 相关 容器选择 复杂计算结果缓存 禁用未使用的脚本和对象 资源加载(反序列化)优化和预渲染
UPL4-4——排除其他应用程序带来的问题
UPL4-4——排除其他应用程序带来的问题 排除其他应用程序带来的问题 在 Unity 中进行性能调试或问题排查时,“排除其他应用程序带来的问题”指的是: 要确保你在分析性能、内存、输入等数据时,不受到 Unity 编辑器之外的其他程序的干扰或影响 可能带来影响的因素: 系统资源争用带来的问题 其他后台应用可能会占用 CPU、GPU、内存、磁盘 IO、网络带宽等,导致 游戏运行帧率降低、卡顿、系统调度不及时导致 Profiler 数据不准确、系统内存吃紧,但可能是别的应用程序占用的 输入系统干扰 某些应用程序可能劫持键盘或鼠标输入,导致 Unity 中输入响应迟缓,鼠标位置偏移或滞后等 音视频干扰 其它程序播放音频可能干扰 Unity Profiler 中的音频模块, 视频播放软件也会抢占 GPU 解码资源,影响 Profiler 中的 GPU 模块或视频模块 部分音频设备驱动会导致 Unity 中音频播放异常 文件访问干扰 后台杀毒软件、网盘同步软件可能访问或锁定 Unity 资源文件 导致文件导入异常或 Profiler...
UPL4-4——垂直同步带来的等待问题
UPL4-4——垂直同步带来的等待问题 本章代码关键字 1Application.targetFrameRate // 帧率限制 垂直同步的作用 在未开启垂直同步的情况下,游戏可能在显示器未完成当前帧的刷新时,就输出下一帧图像, 导致屏幕的 上下部分显示的是不同的画面内容,这就是“画面撕裂”。 垂直同步会强制显卡等待显示器刷新完成后再提交下一帧图像,从而避免撕裂。 但是它可能带来一些副作用 输入延迟增加 游戏被迫等待显示器信号,延迟图像输出,会让操作感觉迟钝 帧率下降 如果帧率无法维持在刷新率水平,垂直同步会强行降档到下一个可整除值(如从 60 → 30FPS),造成明显卡顿 因此在使用垂直同步时应根据实际情况而定: 对图像撕裂特别敏感的游戏(如第三人称 3D、赛车等) 游戏帧率远高于显示器刷新率,容易画面撕裂 对输入延迟不特别敏感的项目 垂直同步影响帧率具体分析 显示器通常以固定的频率刷新画面(如 60Hz 表示每秒刷新 60 次,每帧...
UPL4-3——Debug 的性能消耗
UPL4-3——Debug 的性能消耗 Debug 相关 API 带来的性能消耗 Unity 中 Debug 公共类中提供了日志打印的相关API,比如: Debug.Log Debug.LogError Debug.LogWarning 等等 他们可以用于在 Unity 的控制台中输出打印信息,帮助我们进行项目调试,但是这些API会带来额外的性能开销 特别是在最终的发布版本中,尽量都不要再使用 Debug 相关API,避免给我们的最终项目带来额外的开销,影响项目性能表现 如何更好的使用 Debug 相关API 我们可以对 Debug 进行二次封装 可以让我们更加方便的开关 Debug 相关API的使用 尽量不影响我们的开发效率 123456789101112131415161718192021222324252627282930using UnityEngine;public static class DebugUtil{ /// <summary> /// 是否是调试模式,可以在发布后修改为false,避免发布后输出Debug信息...
