Unity优化——脚本优化策略3

简介: Unity优化——脚本优化策略3

一、注意缓存Transform的变化


Transform组件只存储与其父组件相关的数据。这意味着访问和修改Transform组件的position、rotation和scale属性会导致大量未预料到的矩阵乘法计算,从而通过其父Transform为对象生成正确的Transform表示。对象在Hierarchy窗口中的位置越深,确定最终结果需要进行的计算就越多。


然而,这也意味着使用localPosition、localRotation和localScale的相关成本相对较小,因为这些值直接存储在给定的Transform中,可以进行检索,不需要任何额外的矩阵算法。因此,应该尽可能使用这些本地属性值。


遗憾的是,将数学计算从世界空间更改为本地空间,会使原本很简单的问题变得过于复杂,因此进行这样的更改会破坏实现方案,并引入大量以外的bug。有时,为了更容易地解决复杂的3D数学问题,牺牲一点性能是值得的。


不断更改Transform组件属性的另一个问题是,也会向组件(如Collider、RigidbodyLight和Camera)发送内部通知,这些组件也必须进行处理,因为物理和渲染系统都需要知道Transform的值,并相应地更新。


题外话:如上文所述,由于内存中Transform的充足,这些内部通知的速度在Unity5.4中得到了极大的提高,但我们仍需了解它们的成本。


在复杂的事件链中,在同一帧中多提提换Transform组件的属性是很常见的。每次发生这种情况时,都会触发内部消息,即使它们发生在同一帧甚至同一个函数调用期间。因此,应该尽量减少修改Transform属性的次数,方法是将它们缓存在一个成员变量中,只在帧的末尾提交它们。


二、避免在运行时使用Find()和SendMessage()方法


众所周知,SendMessage()方法和GameObject.Find()方法非常昂贵,应该不惜一切代价避免使用。在有些时候,如场景初始化期间调用Find()是可以原谅的,例如在Awake()或Start()回调期间。即使在这种情况下,它也只能用于获取已经确定存在于场景中的对象,以及只有少量GameObjects的场景。无论如何,在运行时使用这两种方法进行对象间通信会产生非常明显的开销,还可能丢帧。


可以采取多尔方法来解决这个问题,体量比较大,以后找个专题说说。


三、禁止未使用的脚本和对象


场景有时会变得非常繁忙,特别是构建大型的、开放的世界时,在Update()回调这种,调用代码的对象越多,它的伸缩性就越差,游戏也就越慢。然而,如果许多正在处理的内容在玩家的视野之外,或者只是太远而显得不重要,就完全不必要处理它们。这种可能不适合建立模拟大型城市的游戏,因为必须总是处理整个仿真。但它通常适用于第一人称和赛车游戏:因为玩家活动在开阔的区域,而非可视对象可以临时禁用,而不会对游戏过程产生任何明显的影响。下面来介绍两种禁用方式:


3.1通过可见性禁用对象


有时,希望组件或GameObject在不可见时禁用。Unity带有内置的渲染功能,以避免渲染对玩家的相机视图不可见的对象, 避免渲染隐藏在其他对象后面的对象,但这些只是渲染层的优化。它不会影响在CPU上执行任务的组件,比如AI脚本、用户界面和游戏逻辑。我们必须自己控制这种行为。


解决这个问题的一个好方法是使用OnBecameVisible()和OnBecameInvisible()回调。顾名思义,这些回调方法是在可渲染对象对于场景中的任何相机变得可见或不可见时调用的。此外,当一个场景中有多个摄像机时,只有当对象对任何一个相机可见,以及对所有相机不可见时,才会分别调用这两个回调。这意味着上述回调将在预期的正确时间调用;


由于可见性回调必须与渲染管线通信,因此GameObject必须附加上一个可渲染的组件,例如MeshRenderer或SkinnedMeshRenderer。必须确保希望接受可见性回调的组件也与可渲染对象连接在同一个GameObject上,而不是连接到其父或子GameObject上,否则它们也不会调用。


提示:要注意,Unity还计算Scene窗口中对 OnBecameVisible()和OnBecameInvisible()回调隐藏的摄像头数。如果发现在播放模式测试期间,这些方法没有被正确调用,请确保将Scene窗口的摄像机背对所有对象,或完全禁用Scene窗口


为了使用可见性回调开启/禁用独立组件,需要添加下述方法:

 void OnBecameVisible() { enabled = true; }
 void OnBecameInvisible() { enabled = false; }


为了开启/禁用Component所附加的整个GameObject,可以下面的方式实现方法:

void OnBecameVisible() { gameObject.SetActive(true); }
void OnBecameInvisible() { gameObject.SetActive(false); }


不过,请注意,禁用包含可渲染对象的GameObject或它的父对象之一,就不可能调用OnBecameVisible(),因为现在摄像机没有图形表示来查看和触发回调。应该将组件放在一个子GameObject上,并让脚本禁用它,使可渲染的对象始终可见。


3.2通过距离禁用对象


在其他情况下,如果组件或GameObject离玩家够远,以至于看不见,就可以禁用它们。原神中的原魔就是在玩家走进后才会出现的。


下面的代码是一个简单的协程,定期检查与给定目标的总距离,太远就禁用自己

 [SerializeField] GmeObject _target;
    [SerializeField] float _maxDistance;
    [SerializeField] int _coroutineDelay;
    void Start()
    {
        StartCoroutine(DisableAtADistance());
    }
 
    IEnumerator DisableAtADistance()
    {
        while (1)
        {
            float distSqrd = (transform.position - _target.transform.position).sqrMagnitude;
            if (distSqrd < _maxDistance * _maxDistance)
            {
                enabled = true;
            }
            else
            {
                enabled = false;
            }
            for (int i = 0; i < _coroutineDelay; i++)
            {
                yield return new WaitForEndOfFrame();
            }
        }
 
    }


四、使用距离平方而不是距离


可以肯定地说,CPU比较擅长将浮点数相乘,但不擅长计算它们的平方根。每次使用magnitude属性或Distance()方法要求Vector3计算距离时,都会要求它执行平方根计算,与许多其它类型的向量数学计算相比,这会消耗大量的CPU开销。


然而,Vector3类也提供了sqrMagnitude属性,它提供了同样可作为距离的结果,只是该值时平方。这意味着如果也将需要比较的距离进行平方,就可以执行基本相同的比较,而不需要昂贵的平方根计算。


用这两种方式的结果几乎相同,原因是浮点精度。可能会失去一些使用平方根的精度,因为该值调整为具有不同密度的可表示数字区域;它可以准确地落在一个更精确的可表示数字区域,更有可能落在一个精度较低的数字区域上。结果,比较并不完全相同,但是,在在大多数情况下,它非常接近,不会引起注意,对于这种方式替换的每条指令,性能收益可能相当可观。


如果这个小的精度损失不重要,那么应该考虑这个性能技巧。然而,如果精度是非常重要的,就可能要忽略这个技巧。


注意,此技术可用于任何平方根计算。而不只是用于距离。这是最常见的示例,它揭示了Vector3类的sqrMagnitude属性的中亚行。这是Unity Technologies有意以这种方式向我们展示的一个属性。


相关文章
|
7月前
|
大数据 API 图形学
Unity优化——批处理的优势
Unity优化——批处理的优势
215 0
|
7月前
|
存储 人工智能 Java
Unity优化——脚本优化策略4
Unity优化——脚本优化策略4
120 0
|
5月前
|
存储 设计模式 监控
运用Unity Profiler定位内存泄漏并实施对象池管理优化内存使用
【7月更文第10天】在Unity游戏开发中,内存管理是至关重要的一个环节。内存泄漏不仅会导致游戏运行缓慢、卡顿,严重时甚至会引发崩溃。Unity Profiler作为一个强大的性能分析工具,能够帮助开发者深入理解应用程序的内存使用情况,从而定位并解决内存泄漏问题。同时,通过实施对象池管理策略,可以显著优化内存使用,提高游戏性能。本文将结合代码示例,详细介绍如何利用Unity Profiler定位内存泄漏,并实施对象池来优化内存使用。
346 0
|
3月前
|
设计模式 存储 人工智能
深度解析Unity游戏开发:从零构建可扩展与可维护的游戏架构,让你的游戏项目在模块化设计、脚本对象运用及状态模式处理中焕发新生,实现高效迭代与团队协作的完美平衡之路
【9月更文挑战第1天】游戏开发中的架构设计是项目成功的关键。良好的架构能提升开发效率并确保项目的长期可维护性和可扩展性。在使用Unity引擎时,合理的架构尤为重要。本文探讨了如何在Unity中实现可扩展且易维护的游戏架构,包括模块化设计、使用脚本对象管理数据、应用设计模式(如状态模式)及采用MVC/MVVM架构模式。通过这些方法,可以显著提高开发效率和游戏质量。例如,模块化设计将游戏拆分为独立模块。
213 3
|
4月前
|
开发者 图形学 iOS开发
掌握Unity的跨平台部署与发布秘籍,让你的游戏作品在多个平台上大放异彩——从基础设置到高级优化,深入解析一站式游戏开发解决方案的每一个细节,带你领略高效发布流程的魅力所在
【8月更文挑战第31天】跨平台游戏开发是当今游戏产业的热点,尤其在移动设备普及的背景下更为重要。作为领先的游戏开发引擎,Unity以其卓越的跨平台支持能力脱颖而出,能够将游戏轻松部署至iOS、Android、PC、Mac、Web及游戏主机等多个平台。本文通过杂文形式探讨Unity在各平台的部署与发布策略,并提供具体实例,涵盖项目设置、性能优化、打包流程及发布前准备等关键环节,助力开发者充分利用Unity的强大功能,实现多平台游戏开发。
125 0
|
4月前
|
图形学 C# 开发者
全面掌握Unity游戏开发核心技术:C#脚本编程从入门到精通——详解生命周期方法、事件处理与面向对象设计,助你打造高效稳定的互动娱乐体验
【8月更文挑战第31天】Unity 是一款强大的游戏开发平台,支持多种编程语言,其中 C# 最为常用。本文介绍 C# 在 Unity 中的应用,涵盖脚本生命周期、常用函数、事件处理及面向对象编程等核心概念。通过具体示例,展示如何编写有效的 C# 脚本,包括 Start、Update 和 LateUpdate 等生命周期方法,以及碰撞检测和类继承等高级技巧,帮助开发者掌握 Unity 脚本编程基础,提升游戏开发效率。
100 0
|
4月前
|
开发者 图形学 UED
深度解析Unity游戏开发中的性能瓶颈与优化方案:从资源管理到代码执行,全方位提升你的游戏流畅度,让玩家体验飞跃性的顺滑——不止是技巧,更是艺术的追求
【8月更文挑战第31天】《Unity性能优化实战:让你的游戏流畅如飞》详细介绍了Unity游戏性能优化的关键技巧,涵盖资源管理、代码优化、场景管理和内存管理等方面。通过具体示例,如纹理打包、异步加载、协程使用及LOD技术,帮助开发者打造高效流畅的游戏体验。文中提供了实用代码片段,助力减少内存消耗、提升渲染效率,确保游戏运行丝滑顺畅。性能优化是一个持续过程,需不断测试调整以达最佳效果。
104 0
|
6月前
|
人工智能 图形学
【unity小技巧】使用动画状态机脚本实现一个简单3d敌人AI功能
【unity小技巧】使用动画状态机脚本实现一个简单3d敌人AI功能
65 0
|
6月前
|
人工智能 定位技术 图形学
【Unity小技巧】一个脚本实现控制3D远程/近战敌人AI
【Unity小技巧】一个脚本实现控制3D远程/近战敌人AI
59 0
|
6月前
|
自然语言处理 图形学
【unity实战】一个通用的FPS枪支不同武器射击控制脚本
【unity实战】一个通用的FPS枪支不同武器射击控制脚本
106 0