Unity控制物体显示与隐藏方法及优化总结

Tandre的碎碎念:我开始用Unity做项目接触最早的接口就是Unity的GameObject的SetActive接口,当时甚至只用这一个接口就解决了一个项目中所有关于显示与隐藏的问题,但实际上它的问题很多,很多时候需要尽量避免使用或避免直接使用,这篇博客就记录下关于显示与隐藏的优化问题。

SetActive接口

可以说是最常用的控制显示与隐藏的接口了,但往往并不推荐使用

示例代码
GameObject go
go.SetActive(true);
go.SetActive(false);
问题

1.会导致网格重建,触发Unity的垃圾回收机制(GC),总之很影响性能
2.引起OnEnable和OnDisable的调用,会有一定的消耗

优化方案

因为对于该接口的GC问题很难解决,所以只能尽量的避免。

SetActive在底层会先对物体进行判断是否处于Active状态,而SetActive这个接口在调用时本身就是在C#层调用底层,所以为避免不必要的底层判断,可以在C#层对物体的Active状态进行判断。示例代码如下:

static public class MyTools
{
    public static void MSetActive(this GameObject go, bool State)
    {
        if (go)
        {
            if (go.activeSelf != State)
            {
                go.SetActive(State);
            }
        }
    }
}

写完这样一个方法后,在后续需要设置显示隐藏时只需要调用这个方法

GameObject go;
go.MSetActive(true);
UI的显示隐藏

因为UI的显示与隐藏是最经常面对的需求之一,所以需要单独写一下

SetActive()

首先使用SetActive这个接口,它在使用时会遍历这个物体下所有继承MonoBehaviour的脚本,并执行脚本上相应的OnEnable和OnDisable脚本,所以基本相当于对每个子物体都执行了一遍SetActive方法

如果使用该接口也可以借助上面自定义的方法(MSetActive())减少一定的不必要性能消耗。

Canvas group组件(推荐使用)

通过在父物体上挂载CanvasGroup组件,然后控制它的Alppa的1/0来控制显示/隐藏,相对SetActive的性能相差并不大,两者都不会触发DrawCall,但CanvadGroup组件不会执行子节点的Awake方法,而SetActive(true)会执行Awake()方法
使用CanvasGroup显示隐藏物体的方法示例:

public static void MSetActive(this CanvasGroup cgrop,bool active)
{
    if (cgrop!=null)
    {
        if (active)
        {
            cgrop.alpha = 1;
            cgrop.interactable = true;
            cgrop.blocksRaycasts = true;
        }
        else
        {
            cgrop.alpha = 0;
            cgrop.interactable = false;
            cgrop.blocksRaycasts = false;
        }
    }
}

其中的Alpha表示透明度,可用于UI淡入淡出的控制,Interactable表示是否禁用输入交互,BlockRaycasts是否禁用射线检测,IgnoreParentGroups是否忽略父级的CanvasGroup。这些属性将作用于该组件及其所有子物体,所以利用该组件可以较便捷的实现很多实用的效果。

其他的显示与隐藏方法
缩小Scale

采用缩小Scale的方法,即:

transform.localScale=Vector3.zero;

这种方法可以避免SetActive带来的很多性能消耗,但当Scale改回原大小时会触发DrawCall带来相对更多的性能消耗。

移动物体

通过更改物体位置(Position),将物体移动到屏幕外,使相机不渲染此物体,缺点在于,更改物体位置会导致它和它的所有子物体重新进行位置计算。

设置MeshRenderer状态

需要物体带有MeshRenderer组件,相当于将物体隐身,并不会影响物体的脚本运行,物体的碰撞体也依然存在。示例如下:

gameObject.GetComponent<MeshRenderer>().enabled = true;
gameObject.GetComponent<MeshRenderer>().enabled = false;
设置Shader透明度

这种方法与上一个类似,但需要的是材质球上的shader存在可以控制透明度的属性,示例如下:

material.SetColor("_Color", Color.white);
修改物体Layer

将物体的layer设置在相机的CulingMask之外,使相机不渲染此物体,示例如下:

gameObject.layer=LayerMask.NameToLayer("isDisabled");

这里layer的名字需要自行根据需要添加或使用

结语

对于UI的显示与隐藏相对好的方式是使用CanverGroup,但其更好的点在于组件上属性的控制而并非对显示隐藏的优化上面。对于物体显示与隐藏,为避免网格重建和大量GC消耗,优化方向便是想办法让其不被相机渲染,或尽可能减少不必要的计算。

————————————————分割线(2024年1月26日更新)——————————————————

没想到会有这么多收藏和阅读,写这篇文章时项目经验还很少(当然现在依然刚入门状态),多少感觉有点误人子弟,惭愧啊。
从个人项目经验来说,对于小型项目其实没必要考虑太多这类细节优化问题,使用基本的MSetActive()甚至只使用SetActive()也可以,避免业务逻辑混乱问题,尤其是使用修改缩放、位置等会更明显(在此感谢评论区大佬的建议)。对于UI来说,使用CanvasGroup是个很好的选择,但是对于快速开发和迭代来说一定程度上会降低Debug效率,使用模型的meshRenderer也是类似的情况,所以可以据项目实施阶段及实际情况进行考虑。
刚学习是深信一句鸡汤,大概意思是:最终让你和其它人拉开差距的往往是一些对细节处理的掌握程度。
现在觉着,不断进阶技术总没错,但实际开发还是要具体问题具体分析。

  • 16
    点赞
  • 69
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值