游戏UI管理的一点思考:如何优雅地处理复杂的窗口层级

如果做了纹理压缩和资源释放,纹理其实占不了太多内存。我做过实验,一个空的Empty2D的工程打小游戏去微信做云测试都有快600M内存

节点缓存不缓存都可以,我们是缓存的。

挺全面的,不过窗口注册我习惯写分包的公共模块里,去走公共单例,注册窗口配置,好处就是维护比较统一,也支持风格化配置
prefab,都是以 uiXx 或者 xxUI 结尾,创建即会生成模板配置代码,刚开始用 windowXx 嫌弃太长了,都用 UIMgr,强迫症了都

节点是缓存的,假如该节点引用了A,B,C三个预制体。
那A,B,C预制体销毁之前需要把这个节点先找出来销毁吧?
或者思路是先找合适的节点销毁,再检查节点关联的预制体引用计数然后销毁?

我的也差不多,但是窗口分的更细一些

view 类保存节点以及预制体信息的,真正释放的时候调view类的destroy方法就可以连带卸载啊。ui 的cache 保存的也是view类的对象。

我们每个ui界面都是有一个view类,其中创建并保持节点,控件binding,预制体信息等等,还有控件使用。而ui界面本身不会挂组件代码,都通过这个类进行逻辑。

然后,一个ui界面,引用的预制体是它界面本身的预制体,我们会卸载这个部分。如果有小组件可能会引用小组件的预制体,但小组件的预制体我们不会卸载。

最近刚好在做一个新项目。趁着新项目的机会,重新调整了 UI 管理器的架构。
其中弹出非全屏界面时,统一增加一个半透明黑色遮罩。
我以前的做法是每个 UI 界面配置一个变量。管理器根据该变量自动创建一个遮罩。这个做法有一个不好的地方就是,如果连续 多个半透明遮罩出现的时候,黑色的部分会叠加得越来越黑。虽然不是非常影响体验。但总觉得差了那么点意思。

这次改成老宫的做法。只有一个半透明黑色遮罩。根据界面是否全屏来调整层级和透明度。
整体思路:
1、UI 分层架构,半透明遮罩只会出现在 UI 层;
初始化的时候创建一个黑色半透明遮罩


2、打开一个非全屏的 SettingUI 层界面时。通过 ViewType 标记界面全屏样式:

如果是新增 UI 层的界面,那么调用增加和移除函数(后续由半透明遮罩层根据新的界面去调整自身层级和透明度):

3、由半透明遮罩层根据新的界面去调整自身层级和透明度:

最后效果:


这就是我对 UI 层黑色半透明遮罩动态调层的实现方案。

1赞

分享下我的实现方式。把所有的弹窗放到同一个父节点中,挂个脚本到父节点上,这个脚本中有一个字符串数组,数组中存放的是弹窗的名字,然后这个脚本监听节点的添加,当有节点添加的时候,获取所有弹窗的名字,然后根据字符串数组中弹窗的索引信息进行排序,最后重新setSiblingIndex

那弹窗索引是怎么来的呢,提前定义好的吗

对啊,自己填到数组里面,例如数组是[“A”,"C“,“B”],那么B一定盖住C,C一定盖住A

前面跟你一样,所有弹窗都在同一个父节点下,打开页面会自动设置层级还有遮罩,关闭也会自动调整。弹窗自带窗口属性

image

你这个配置方式有不好的地方,就是每个类都要写路径,不是好的方案

所以我自己的方案是通过 ccclass 装饰器注册的类名去拿预制体(约定预制体和类名保持一致。最后脚本自动解析预制体生成ID→路径映射表)。
好处:
1、整体能减少很多配置;
2、移动类文件和预制体文件时,基本不会带来调整的工作量。
3、不存在预制体路径拼错的问题

收藏一下 :grinning:

如果是基于3x,这个可以自动生成的,模板写好就行,

你预制体的路径和脚本是一起的吗?

不是,但是预制体和脚本的父文件夹一样名字,然后通过预制体所在的bundle和父文件夹+预制体名就可以获取到了。比如主页home,bundle/home/home.prefab,scripts/home/home.ts。类似于这种

懂了,强制路径的前置结构一样。限制有点大啊,不过找界面倒是容易

结构定好就行,相当于规范嘛~
方便很多

主界面(Base层)
  → 打开背包(Normal层,Normal类型) - 主界面不受影响
    → 点击道具打开详情(Normal层,CloseOne类型) - 背包被关闭
      → 点击购买弹出确认框(Popup层,HideAll类型) - 详情被隐藏
        → 点击取消 - 确认框关闭,详情自动恢复显示
        → 点击确定 - 确认框关闭,详情恢复显示并刷新

有个地方不明白,看了下代码,hideAll的处理Group好像只隐藏同组下的窗口,所以当确认弹出的时候,为什么详情会被隐藏呢?

image