本文介绍: 大家好,这里是七七,今天又来更新Unity脚本优化篇了,话不多说,直接主题

大家好,这里是七七,今天又来更新Unity脚本优化篇了,话不多说,直接主题

一、注意缓存Transform的变化

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

然而,这也意味着使用localPositionlocalRotationlocalScale的相关成本相对较小,因为这些值直接存储给定的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有意以这种方式我们展示的一个属性

原文地址:https://blog.csdn.net/m0_63024355/article/details/134620921

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_39556.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注