跳转至

COMMERCIAL UI FRAMEWORK

ZMUI Framework

Mono 分离式、高性能、自动化的商业级 UI 解决方案

将窗口生命周期、组件绑定与代码生成收拢进稳定的开发管线,以更少的重复代码交付更可靠、更易维护的 Unity 界面。

框架介绍 返回首页 GitHub

框架介绍

ZMUI框架是一款Mono分离式,高性能、自动化、商业级UI框架。

1.框架特点:

  • ​ 完全脱离MonoBehaviour的生命周期控制,自定义窗口弹出管线中的任意生命周期。
  • ​ 采用内存映射的方式设计框架,自动完成脚本和对象的关联绑定。
  • ​ 所有脚本、生命周期、组件字段声明、组件查找代码、组件绑定操作、组件事件绑定、组件方法声明等,全由ZMUI框架自动化系统完成,开发者无需编写反复无聊的作业,一键完成。
  • ​ 框架具有卓越的性能表现,框架从设计层,优化每一处影响性能的细节,把性能问题,扼杀在摇篮之中。
  • ​ 框架采用V+D的方式分离交互逻辑和组件持有脚本,使得交互脚本代码更加简洁、舒适、单一。V即View脚本,D即数据组件脚本。
  • ​ 框架属于MVC中的V,纯View框架,能够轻松接入替换自己项目的MVC,若需游戏代码架构,轻移至ZMGC....

2.框架提供成熟稳定的功能系统:

Mono分离式UI管理系统

脱离 Mono 的分离式架构,以内存映射方式设计框架,彻底解决窗口生命周期不受控制的问题。从窗口生成到销毁,所有流程都掌握在开发者手中,可自定义任意阶段的生命周期接口。即使窗口不挂载任何脚本,也能正常执行弹出、销毁及生命周期调用。

堆栈系统

成熟的堆栈管理。首页堆栈弹出、顺序控制、半路压入等,兼容各种情况,全局任意地方皆可使用

遮罩系统

完善遮罩的处理。提供单遮、叠遮模式两种模式,无缝切换

层级系统

负责框架中UI的层级管理,清晰透明, 解决UI叠加,特效模型穿插问题

高性能系统

负责Untiy UGUI相关极致的性能优化,彻底解决UI卡顿、不流畅的问题

自动化系统

提供自动生成组件查找代码、组件自动拖拽赋值绑定、字段自动生成、方法自动声明、事件接口声明、UI组件事件绑定、脚本生成等一条龙服务

高效率系统

提供预制体管理系统,预制体模板拖拽、Editor编译器、Editor针对框架特殊优化、提升开发效率

1.快速入门


1.1 获得ZMUI框架

ZMUI目前是以Git库为长期维护地址的

Example

https://github.com/ZMteacher/ZMUIFrameWork
https://github.com/ZMteacher/ZMUIFrameWork
https://www.yxtown.com/my/course/13

1.2 初始化UI框架

void OnAwake()
{
    //初始化框架
    UIModule.Instance.Initlizate();
}
注意:请在使用 UIModule 中的任何接口前调用。

    public void Initialize()
    {
        mUICamera = GameObject.Find("UICamera").GetComponent<Camera>();
        mUIRoot = GameObject.Find("UIRoot").transform;
        mWindowConfig = Resources.Load<WindowConfig>("WindowConfig");
        //在手机上不会触发调用
#if UNITY_EDITOR
        mWindowConfig.GeneratorWindowConfig();
#endif
    }

初始化接口前置条件

在调用初始化接口前要保证场景中存在UICamera和UIRoot节点。当然,你也可以对这些节点进行重命名。 但要保证,框架初始化时能够准确寻找到对应节点。

1.3 核心配置

配置中心是 ZMUI 自动化系统的基础。代码生成、组件解析、脚本输出、命名空间以及窗口运行时加载路径,都由这里的配置共同驱动。

通过 Unity 顶部菜单打开:

ZM → ZMUI Setting

ZMUI Setting 配置中心,包含遮罩模式、代码生成、生成路径、预制体路径和命名空间设置

ZMUI Setting 配置中心。点击图片可查看完整尺寸。

预制体路径是运行时必填项

必须在“预制体路径(Prefab Paths)”中配置窗口 Prefab 所在目录,并点击右上角“保存设置”。

ZMUI 会根据这些目录扫描窗口预制体,并自动计算、生成每个窗口的运行时加载路径。如果目录缺失、填写错误或修改后没有保存,PopUpWindow<T>() 即使能够正常编译,也无法找到与窗口类型对应的 Prefab,最终导致窗口加载失败。

配置项说明

配置页 作用 是否影响运行时
遮罩模式(Mask System) 设置窗口遮罩的生成与交互策略
代码生成(Code Generator) 设置组件解析方式及 Find / Bind 代码生成策略 主要影响 Editor 自动化
生成路径(Script Paths) 设置窗口脚本、数据组件脚本等代码的输出目录 影响代码生成
预制体路径(Prefab Paths) 声明窗口 Prefab 的扫描目录,生成窗口类型与加载路径映射 是,且为关键配置
命名空间(Namespaces) 统一生成脚本所使用的命名空间 影响代码生成与项目组织

推荐配置顺序

  1. 预制体路径 中添加所有窗口 Prefab 的根目录。
  2. 生成路径 中设置窗口逻辑脚本和数据组件脚本的输出位置。
  3. 选择组件解析方式:节点名称解析或 Tag 标签解析。
  4. 选择代码生成方式:推荐使用组件绑定(Bind)。
  5. 根据项目规范填写命名空间及遮罩策略。
  6. 点击右上角 保存设置,再执行窗口脚本生成。

组件自动解析

截图中的默认名称解析规则使用 [ComponentType]fieldName

[Text]title          → public Text titleText
[Button]confirm     → public Button confirmButton
[Image]icon         → public Image iconImage
[ScrollRect]list    → public ScrollRect listScrollRect

选择 组件绑定(Bind) 后,框架会生成序列化字段并在 Editor 阶段完成组件赋值,运行时不需要反复执行 Transform.Find,更适合窗口层级复杂或性能敏感的商业项目。

预制体路径与窗口加载的关系

Prefab Paths 中配置窗口目录
框架扫描目录中的 Window Prefab
生成 Window 类型 → Prefab 加载路径映射
PopUpWindow<T>() 查询 T 对应路径
加载 Prefab、创建窗口并执行生命周期

窗口加载失败时优先检查

  1. 目标窗口 Prefab 是否位于已配置的 Prefab Paths 目录中。
  2. 修改配置后是否点击了“保存设置”。
  3. Prefab 名称、窗口类型名称与生成配置是否一致。
  4. 移动或重命名 Prefab 后,是否重新生成了窗口配置。
  5. 初始化时加载的 WindowConfig 是否为最新版本。

配置完成

初始化、预制体路径与代码生成策略保存完成后,即可使用 ZMUI 自动生成窗口脚本,并通过窗口 API 正常加载和弹出 Prefab。


2.功能使用

2.1 窗口创建流程

1.框架提供了窗口模板供开发者进行使用。

模板路径: Assets\ZMPackages\ZMUI\Resources\Temp\TempWindow.prefab

2.使用方式:

在模板的基础上搭建界面,并修改模板窗口名称,制作成一个新的窗口即可。

3.节点介绍:

UIMask:窗口统一的遮罩层。

UIContent:窗口动画缩放节点,也是UI组件存放的父节点,若某节点不想参加窗口缩放动画,可移至与UIContent同级存放。


2.2 窗口脚本自动生成流程


1.ZMUI框架提供了自动化脚本生成工具。

以1.3配置窗口默认配置为例:

​ 1.在场景中选中TempWindow窗口节点

​ 2.Shift+B:生成 TempWindowDataComponent 组件绑定脚本。(该脚本只用来存储窗口组件,再次生成会重新覆盖)。

​ 3.Shift+V:生成 TempWindow 窗口交互脚本。(该脚本用来编写UI交互逻辑,再次生成会以递增的形式追加代码,不会覆盖原有代码)。

Image title

注意

在按下 Shift+B 生成脚本后,可能会出现找不到组件监听事件的报错。继续执行操作 3,生成对应的组件事件方法即可解决。

因此生成代码时,应按照上述步骤 2、3 的顺序操作。步骤 3 依赖步骤 2 产生的数据,两者是一套完整的生成流水线,不可跳过。

2.3 窗口使用API


ZMUI框架中,所有的窗口操作都在 UIModule 脚本中,下面给大家列一下常用的接口。

窗口弹出

public void Example()
{
    //弹出窗口时需要通过泛型指定要弹出的窗口
    UIModule.Instance.PopUpWindow<T>();

    //例:1
    UIModule.Instance.PopUpWindow<HallWindow>();
    //例:2
    HallWindow window = UIModule.Instance.PopUpWindow<HallWindow>();
    window.OnExampleButtonClick();
}

窗口隐藏

public void Example()
{
    //隐藏窗口框架提供2个重载接口
    UIModule.Instance.HideWindow<T>();

    //例:1
    UIModule.Instance.HideWindow<HallWindow>();
    //例:2
    UIModule.Instance.HideWindow("HallWindow");
}

窗口获取

public void Example()
{
    //如果你想要获取某一个已经显示的窗口,可以这样做
    UIModule.Instance.GetWindow<T>();

    //例:1
    UIModule.Instance.GetWindow<HallWindow>().OnExampleButtonClick();
    //例:2
    HallWindow window = UIModule.Instance.GetWindow<HallWindow>();
    window.OnExampleButtonClick();
}

窗口销毁

public void Example()
{
    //如果你想要销毁指定窗口,可以这样做
    UIModule.Instance.DestroyWindow<T>();

    //例:1
    UIModule.Instance.DestroyWindow<HallWindow>();

    //如果你想要销毁所有窗口,可以这样做

    //例:2
    UIModule.Instance.DestroyAllWindow();

    //例:3 过滤的销毁窗口列表
    List<string> filterWindow = new List<string> { "HallWindow" };
    UIModule.Instance.DestroyAllWindow(filterWindow);
}

窗口压栈

public void Example()
{
    //1.如果你想要在压栈的同时立即弹出栈内的弹窗,可以这样做
    UIModule.Instance.PushAndPopStackWindow<HallWindow>();

    //2.如果你只想简单压栈一个弹窗,并不进行弹出,可以这样做
    UIModule.Instance.PushWindowToStack<HallWindow>();

    //3.如果你想要栈内的弹窗主动弹出,可以这样做
    UIModule.Instance.StartPopFirstStackWindow();
}

3.系统介绍

3.1 遮罩系统

1

3.2 层级系统

1.ZMUI框架对层级的设计和定义如下:

  1. 一级窗口 Order in Layer 渲染层级为 0-99 区间(一级窗口相当于各个场景的主页面,如登录页、大厅主页、战斗主页窗口)。
  2. 二级窗口 Order in Layer 渲染层级为 100-199 区间(二级窗口是点击一级窗口按钮后,覆盖在一级窗口上的其他窗口,如商城、工会、背包、排行榜、个人信息页面等)。
  3. 三级窗口 Order in Layer 渲染层级为 200-299 区间(三级窗口是点击二级窗口按钮后,覆盖在二级窗口上的其他窗口,如商城确认购买弹窗、背包确认使用或删除弹窗等)。
  4. 以此类推,具体需要几级,根据项目复杂度决定。 但一级和二级窗口的定义不建议更改,否则会影响框架使用体验,并增加后续窗口交互中的层级问题。

每一个窗口制作完成后,都需要明确它所属的窗口级别,并根据不同级别设置对应的渲染层级。


2.框架采用OVC的设计方式,即一个窗口一个Canvas。

原因:

常见做法:UIRoot1 UIRoot2 UIRoot3 UIRoot4 , 每个Root节点下存放不同层级的窗口,这种做法至少也需要4-6个Canvas。

单独Canvas的做法:经过多个项目测试,最多同时显示的窗口也不会超过5个,基本在2-4左右,所以Canvas带来的缺点基本就可以不用担心了。

优点:

1.独立性:

  • 每个 Canvas 是独立的,可以单独控制其渲染顺序、事件处理和层级。
  • 可以更容易地启用或禁用某个特定窗口,而不会影响其他 UI 元素。

2.性能优化:

  • 由于每个窗口都是独立的,故实现了窗口隔离,在A窗口进行重绘时并不会影响到B窗口,从而实现简单的动静分离提升性能。
  • 可以对每个 Canvas 进行单独的优化,例如调整其 Render Mode 和 Pixel Perfect 设置。
  • 窗口被重绘的原因是很多的,而独立的Canvas窗口能够做到单独的重绘,而相较于一个Canvas下存放多个窗口,若其中某一个窗口触发了重绘,就会导致整个Canvas下的所有元素进行重绘,该Canvas下的节点越多,性能压力越大。
  • Canvas分离能最大化的减少网格重建带来的性能消耗

3.易于管理:

  • 每个窗口有自己的 Canvas,可以在场景中更直观地看到和管理各个 UI 组件,和调整层级。
  • 便于组织和维护代码,因为每个窗口的逻辑可以封装在一个单独的脚本中。
  • 在处理窗口与窗口之间穿插特效、模型、这种设计方式能以及其简单的操作实现UI与模型和特效之间的穿插渲染。

4.灵活的布局:

  • 可以更灵活地控制每个窗口的位置和大小,不受其他窗口的影响。
  • 可以使用不同的 Canvas Scaler 设置来适应不同的屏幕尺寸和分辨率。

5.事件处理:

  • 每个 Canvas 有自己的事件系统,可以独立处理输入事件,避免事件冲突。
  • 可以更容易地实现复杂的交互逻辑,例如模态对话框。

缺点:

  • Canvas 增加 Draw Call 的原因是不同 Canvas 之间无法合批,因此会产生额外的 Draw Call。但场景中同时打开的窗口通常不会超过 5 个,这部分损耗与常见的多窗口父节点方案基本相当。

注意

窗口层级管理系统能够尽量减少窗口交互中的层级冲突,以及 UI、特效和模型相互穿插或遮挡的问题。

所以尽量遵循此设计,否则你会增加在遇到复杂场景时的显示问题。

3.3 自动化系统

自动化系统旨在解决日常开发中重复且繁琐的工作。 主要功能包括:

  • 自动生成包含生命周期、组件事件函数的窗口脚本 (以递增的形式追加并生成最新代码,不会覆盖已编写逻辑)。
  • 自动生成组件查找脚本。
  • 自动生成组件绑定脚本。
  • 自动挂载到对应窗口。
  • 自动生成组件字段。
  • 自动生成组件事件监听代码。
  • 自动拖拽组件进行引用持有。
  • 自动计算窗口加载路径,新建窗口无需添加任何配置即可直接弹出使用。

1.自动化系统提供两套脚本生成方式,和两套组件解析方式。

​ 脚本生成类型:

​ 1.组件查找脚本 (生成基于Find查找代码的窗口交互脚本)

​ 2.组件绑定脚本 (生成自动拖拽组件的窗口交互脚本,并对其字段进行拖拽赋值)

2.自动化系统提供两种窗口组件解析方式:

​ 组件解析类型:

​ 1.节点名称:

​ 格式:需要以 [组件或脚本类名]+字段名称 的格式设置节点名称,如:[Button]Login,解析完成后会自动生成 public Button loginButton; 字段。

​ 2.节点标签

​ 格式:需要在节点的 Inspector 面板中的 Tag 属性 设置节点需要解析的组件。代码生成时会读取 Tag+节点名称 并生成对应字段。例如,将 Login 节点的 Tag 设置为 Button,解析完成后会自动生成 public Button loginButton; 字段。

详细介绍和GIF使用演示,点我跳转:

3.4 高性能系统

  • 进行动静分离拆分Canvas,把频繁移动的放到动态Canvas下,一方面可以降低静态元素的更新频率,另一方面可以减小重建时涉及到的Mesh大小(重建是以Canvas为单位进行的)

  • 避免频繁销毁和创建界面。

  • 避免频繁使用 SetActive 控制界面,可以使用 CanvasGroup 代替。

  • 尽可能的避免使用Unity自带的OutLine组件,该组件会使得文本的Mesh增加4倍,导致UI重建开销明显增大

  • 尽可能的避免使用Unity自带的Shadow组件,该组件同样会使得Mesh增加顶点增多。

  • 谨慎使用Text的Best Fit选项,代价很高,增加额外生成时间。(禁Best Fit)

  • Mask换Rect Mask 2D或者网上的Mask组件。mask以内和以外把UI分割成两个“世界”,依次计算两个“世界”的drawcall,然后再相加。所有能不用就不用。

  • 对于不需要响应点击的 Image、Text 等组件,应关闭 Raycast Target。发生点击时,UGUI 会通过 Canvas 上的 Graphic Raycaster 遍历 UI 元素并检查 Raycast Target,关闭无用的射线检测可以减少性能开销。

  • 图集必须规范。避免一个界面同时使用多个图集中的图片。Draw Call 数量受 Batch(批处理)影响,而图片通常按图集参与批处理;如果相邻的两张 Image 不属于同一图集,就可能产生两个 Batch。

  • 注意图文交叉造成的合批中断。例如,Image1 → Image2 → Text 可能产生两个 Draw Call;改为 Image1 → Text → Image2 后,Text 可能打断两个 Image 的合批,从而产生三个 Draw Call。Unity 2020 及以上版本对此有所优化,但仍需注意非图集图片带来的影响。

  • 避免Text RichText 属性,当Text组件勾选了Rich Text的时候,其实每次文本显示或赋值时都会先判断一下是否拥有这个属性,然后做正则匹配。如果频繁赋值,也是一笔不小的开销。

  • 无用的Image以及Sprite为空的Image会打断合批,要谨慎使用。

  • 如果界面不显示了,该界面的一切周期函数比如Update一定要停掉

3.5 高效率系统

5

框架 Runtime API

UIModule 核心类

UIModule 是 ZMUI 的窗口管理类,用来初始化 UI 框架,管理窗口的弹出、隐藏、销毁、创建、入栈、出栈等一系列操作,是 Runtime 的核心类。

Initialize

Initialize 是初始化 ZMUI 框架的接口,它决定了整个 UI 框架是否可用,需要在程序启动时调用。

    public void Initialize()
    {
        mUICamera = GameObject.Find("UICamera").GetComponent<Camera>();
        mUIRoot = GameObject.Find("UIRoot").transform;
        mWindowConfig = Resources.Load<WindowConfig>("WindowConfig");
        //在手机上不会触发调用
#if UNITY_EDITOR
        mWindowConfig.GeneratorWindowConfig();
#endif
    }

初始化接口前置条件

在调用初始化接口前要保证场景中存在UICamera和UIRoot节点。当然,你也可以对这些节点进行重命名。 但要保证,框架初始化时能够准确寻找到对应节点。

void PopUpWindow < T > ( );

窗口弹出接口,通过框架生成的所有窗口,都可以使用此API进行窗口的弹出。

参数 作用
泛型T 表示要弹出的窗口类型

示例

public void Example()
{
    //例:1
    UIModule.Instance.PopUpWindow<HallWindow>();
}

PreLoadWindow

T PreLoadWindow < T > ( );

窗口预加载接口,只加载物体,提升PopUpWindow时的性能和流畅度,不调用生命周期,但窗口依赖的资源会加载至内存中。

参数 作用
泛型T 表示要预加载的窗口类型
返回值 描述
T 预加载成功:返回获取到的窗口脚本,与传入的泛型T相等。 预加载失败:返回 Null

示例

public void Example()
{
    //例:1
    UIModule.Instance.PreLoadWindow<HallWindow>();
}

GetWindow

T GetWindow < T > ( );

获取已经显示的窗口,若窗口不在显示中,则返回Null。

参数 作用
泛型T 表示要获取的窗口类型
返回值 描述
T 获取成功:返回获取到的窗口脚本,与传入的泛型T相等。 获取失败:返回 Null

示例

public void Example()
{
    //例:1
    UIModule.Instance.GetWindow<HallWindow>().OnExampleButtonClick();

    //例:2
    HallWindow window = UIModule.Instance.GetWindow<HallWindow>();
    window.OnExampleButtonClick();
}

HideWindow

void HideWindow < T > ( );

隐藏指定的窗口,若窗口显示中,则直接隐藏。若窗口不在显示中,则进行跳过,无异常。

注:框架的窗口隐藏和显示均是通过CanvasGroup实现,目的是为了降低通过SetActive隐藏触发的重绘造成的性能影响。

参数 作用
泛型T 表示要隐藏的窗口类型

示例

public void Example()
{
    //例:1
    UIModule.Instance.HideWindow<HallWindow>();
}

DestroyWindow

void DestroyWindow < T > ( );

销毁指定的窗口,若窗口存在,则直接把窗口物体Destroy掉。若窗口不在显示中,则进行跳过,无异常。

参数 作用
泛型T 表示要销毁的窗口类型

示例

public void Example()
{
    //例:1
    UIModule.Instance.DestroyWindow<HallWindow>();
}

DestroyAllWindow

void DestroyAllWindow(List filterlist = null)

销毁所有窗口,包括显示中和隐藏中的窗口。

参数 作用
filterlist 表示在销毁的过程中要过滤掉哪些窗口,可以通过这个字段保留某些窗口不被销毁

示例

public void Example()
{
    //例:1
    UIModule.Instance.DestroyAllWindow();

    //例:2 过滤的销毁窗口列表
    List<string> filterWindow = new List<string> { "HallWindow" };
    UIModule.Instance.DestroyAllWindow(filterWindow);
}

PushWindowToStack

void PushWindowToStack< T >(Action< WindowBase > popCallBack = null,bool allowRepeat=true,bool pushToStackTop = false) where T : WindowBase, new()

压栈一个窗口,并约束T只能为继承WindowBase的类,且可以通过New的方式主动创建出来。

参数 作用
popCallBack 堆栈中的弹窗出栈的回调,可在这个接口中根据返回的 WindowBase 进行强转为指定窗口脚本后,调用其接口
allowRepeat 同一个窗口若允许压栈多个,此字段设置为true,若只允许压栈一个,此字段设置为false
pushToStackTop 某一个窗口如果想要进行插入操作直接压到栈顶,优先弹出,此字段设置为true。默认按先后顺序压到栈尾

示例

    public void Example()
    {
        //压栈第一个窗口
        UIModule.Instance.PushWindowToStack<LoginWindow>();
        //压栈第二个窗口
        UIModule.Instance.PushWindowToStack<HallWindow>((winBase) => {
            //该窗口出栈显示时刻触发
            HallWindow window = winBase as HallWindow;
            window.OnAgentButtonClick();
        },false,true);
        //开始出栈
        UIModule.Instance.StartPopFirstStackWindow();
    }

StartPopFirstStackWindow

void StartPopFirstStackWindow()

开始弹出堆栈弹窗,若堆栈中有弹窗,则立即弹出。

示例

    public void Example()
    {
        //开始出栈
        UIModule.Instance.StartPopFirstStackWindow();
    }

PushAndPopStackWindow

void PushAndPopStackWindow< T >(Action popCallBack = null,bool allowRepeat=true,bool pushToStackTop = false) where T : WindowBase, new()

压栈并立即弹出栈内第一个压栈的弹窗,若期间该接口在次触发,则只做压栈处理。

参数 作用
popCallBack 堆栈中的弹窗出栈的回调,可在这个接口中根据返回的 WindowBase 进行强转为指定窗口脚本后,调用其接口
allowRepeat 同一个窗口若允许压栈多个,此字段设置为true,若只允许压栈一个,此字段设置为false
pushToStackTop 某一个窗口如果想要进行插入操作直接压到栈顶,优先弹出,此字段设置为true。默认按先后顺序压到栈尾

示例

    public void Example()
    {
        //压栈并立即弹出HallWindow
         UIModule.Instance.PushAndPopStackWindow<HallWindow>((winBase) => {
            //该窗口出栈显示时刻触发
            HallWindow window = winBase as HallWindow;
            window.OnAgentButtonClick();
        },false,true);

        //压栈第二个个窗口 这个窗口会在HallWindow关闭后自动弹出
        UIModule.Instance.PushAndPopStackWindow<LoginWindow>();
    }

PopNextStackWindow

void PopNextStackWindow(WindowBase windowBase)

内部方法:自动弹出栈内的下一个弹窗。

若当前弹窗是通过堆栈弹出的弹出,在关闭时会触发此接口。否则会执行跳过逻辑。

参数 作用
windowBase 当前关闭的窗口的基类

ClearStackWindows

void ClearStackWindows()

清理栈内所有待弹出的弹窗。

示例

    public void Example()
    {
        UIModule.Instance.ClearStackWindows();
    }

重要属性

bool mSmartShowHide = true;

智能显隐开关。 默认开启,建议开启,因为开启可以提升性能,而关闭和开启从表现上无区别。

若应用主打市场是中高端市场,可以选择关闭,关闭后在使用流程上则轻松很多。

智能显隐

智能显隐:主要用来优化窗口叠加时被遮挡的窗口参与渲染计算,导致帧率降低的问题。

显隐规则:由程序设定某个窗口是否为全屏窗口。(全屏窗口设定方式:在窗口的OnAwake接口中设定该窗口是否为全屏窗口如 FullScreenWindow=true)

1.智能隐藏:当FullScreenWindow=true的全屏窗口打开时,框架会自动通过伪隐藏的方式隐藏所有被当前全屏窗口遮挡住的窗口,避免这些看不到的窗口参与渲染运算, 从而提高性能。

2.智能显示:当FullScreenWindow=true的全屏窗口关闭时,框架会自动找到上一个伪隐藏的窗口把其设置为可见状态,若上一个窗口为非全屏窗口,框架则会找上上个窗口进行显示, 以此类推进行循环,直到找到全屏窗口则停止智能显示流程。

注意:通过智能显隐进行伪隐藏的窗口在逻辑上仍属于显示中的窗口,可以通过GetWindow获取到该窗口。但是在表现上该窗口为不可见窗口,故称之为伪隐藏。

智能显隐逻辑与(打开当前窗口时隐藏其他所有窗口相似)但本质上有非常大的区别,

1.通过智能显隐设置为不可见的窗口属于伪隐藏窗口,在逻辑上属于显示中的窗口。

2.通过智能显隐设置为不可见的窗口可以通过关闭当前窗口,自动恢复当前窗口之前的窗口的显示。

3.通过智能显隐设置为不可见的窗口不会触发UGUI重绘、不会参与渲染计算、不会影响帧率。

4.程序只需要通过FullScreenWindow=true在窗口OnAwake函数中指定哪些窗口为全屏窗口即可,智能显隐的所有逻辑均由框架自动维护处理。

FullScreenWindow=true 设置全屏窗口示例:

示例

    public void OnAwake()
    {
        FullScreenWindow = true;
        dataCompt = gameObject.GetComponent<HallWindowDataComponent>();
        dataCompt.InitComponent(this);
        base.OnAwake();
    }

WindowBase

WindowBase是框架中所有窗口的基类,所有窗口都会继承WindowBase。 WindowBase封装了一些窗口通用的函数和属性,提供最基础的服务。

具有明显且简单意义的接口和字段不会出现在此文档中。

属性相关

mDisableAnim

是否禁用动画,默认开启。 若禁用,窗口在弹出时则不会做对应的缩放动画表现。


InitializeBaseComponent

void InitializeBaseComponent()

初始化框架基础数据和组件的接口,在OnAwake中自动调用。

注意

该接口中会主动查询并获取UIMask和UIContent节点,要保证这个两个节点存在,框架模板自动支持,强烈建议使用框架模板进行制作窗口。


OnAwake

override void OnAwake()

窗口"首次显示"时调用的接口,与MonoBehaviour的Awake作用一致。

由框架内部自动调用,子类可重写实现。


OnShow

override void OnShow()

窗口"每次显示"时都会调用的接口,包含首次打开。与MonoBehaviour的OnEnable作用一致。

由框架内部自动调用,子类可重写实现。

另外在WindowBase中的OnShow接口中,会调用ShowAnimation做窗口打开的动画表现逻辑,如果你想修改窗口的弹出动画

可以在WindowBase中的ShowAnimation接口中修改原有逻辑,从而实现统一窗口表现动画。


OnUpdate

override void OnUpdate()

窗口"显示期间"每帧都会调用的接口,与MonoBehaviour的Update作用一致。

框架为保证性能,默认不实现此接口。如果需要使用,可在子类中实现,并在 Awake 中把 Update 字段设置为 true


OnHide

override void OnHide()

窗口"每次隐藏"时调用的接口,与MonoBehaviour的OnDisable作用一致。

由框架内部自动调用,子类可重写实现。


OnDestroy

override void OnDestroy()

窗口被"销毁"时调用的接口,与MonoBehaviour的OnDestroy作用一致。

由框架内部自动调用,子类可重写实现。


ShowAnimation

override void ShowAnimation()

窗口弹出时DoTween动画的表现逻辑。

由框架OnShow接口自动调用,若不需要可以移除调用或修改弹出逻辑。


HideAnimation

override void HideAnimation()

窗口隐藏时DoTween动画的表现逻辑。

由框架OnHide接口自动调用,若不需要可以移除调用或修改弹出逻辑。


SetVisible

override void SetVisible(bool isVisble)

窗口被框架设置为隐藏时触发的隐藏逻辑,非特殊情况下,不允许调用。

参数 作用
isVisble 表示窗口是否可见

SetMaskVisible

override void SetMaskVisible(bool isVisble)

窗口UIMask遮罩被框架设置为隐藏时触发的隐藏逻辑,非特殊情况下,不允许调用。 一般会随着窗口同时可见和不可见。

参数 作用
isVisble 表示UIMask遮罩是否可见

PseudoHidden

virtual void PseudoHidden(int value)

窗口伪隐藏时触发的函数。 当前窗口被其他全屏窗口完全遮挡住时,被遮挡住的窗口会进入伪隐藏来提升性能和帧率。

但需要在全屏弹窗OnAwake中设置FullScreenWindow=true,标记某个窗口是全屏窗口。

参数 作用
value 0:表示窗口被伪隐藏。1:表示窗口从伪隐藏中恢复正常显示。

示例

    //FullScreenWindows 使用参考
    public override void OnAwake()
    {
        FullScreenWindow = true;
        mDisableAnim = true;
        dataCompt = gameObject.GetComponent<HallWindowDataComponent>();
        dataCompt.InitComponent(this);
        base.OnAwake();
    }

    //PseudoHidden接口实现参考
    public override void PseudoHidden(int value)
    {
        base.PseudoHidden(value);
    }

AddButtonClickListener

void AddButtonClickListener(Button btn, UnityAction action)

窗口在首次弹出由框架统一添加按钮事件的函数,自动化系统会自动生成按钮事件和按钮事件监听代码,无需手动调用。

特殊情况若需要手动调用请参考example。

参数 作用
btn 表示是哪个按钮要添加事件
action 表示按钮添加的监听回调

示例

    public void Example()
    {
        AddButtonClickListener(dataCompt.ShopButton,OnShopButtonClick);
    }

AddToggleClickListener

void AddToggleClickListener(Toggle toggle, UnityAction action)

窗口在首次弹出由框架统一添加Toggle事件的函数,自动化系统会自动生成Toggle事件和Toggle事件监听代码,无需手动调用。

特殊情况若需要手动调用请参考example。

参数 作用
btn 表示是哪个Toggle要添加事件
action 表示Toggle添加的监听回调

示例

    public void Example()
    {
        AddToggleClickListener(dataCompt.MusicToggle,OnMusicToggleChange);
    }

AddInputFieldListener

void AddInputFieldListener(InputField input, UnityAction onChangeAction, UnityAction endAction)

窗口在首次弹出由框架统一添加InputField事件的函数,自动化系统会自动生成InputField事件和InputField事件监听代码,无需手动调用。

特殊情况若需要手动调用请参考example。

参数 作用
input 表示是哪个InputField要添加事件
onChangeAction 表示InputField添加的输入改变监听回调
endAction 表示InputField添加的输入结束监听回调

示例

    public void Example()
    {
        AddInputFieldListener(dataCompt.NameInputField,OnNameInputChange,OnNameInputEnd);
    }

RemoveAllButtonListener

void RemoveAllButtonListener()

移除所有已添加的按钮事件,窗口在销毁时由框架自动触发,无需手动调用。


RemoveAllToggleListener

void RemoveAllToggleListener()

移除所有已添加的Toggle事件,窗口在销毁时由框架自动触发,无需手动调用。


RemoveAllInputListener

void RemoveAllInputListener()

移除所有已添加的InputField事件,窗口在销毁时由框架自动触发,无需手动调用。


WindowConfig

脚本介绍

WindowConfig 是框架中自动计算所有窗口加载路径的配置类。它继承自 ScriptableObject,在计算路径的同时还具备配置存储功能。

主要的目的是为了实现窗口加载路径的自动化,彻底解决每创建一个窗口,就需要到某个配置文件中去配置一下窗口的名字和加载路径这个繁琐的工作。

注意

前提是需要在 Unity 顶部菜单栏 ZM/ZMUI Setting 中配置预制体存放路径。


GeneratorWindowConfig

void GeneratorWindowConfig()

根据预制体存储位置,计算所有窗口基于 Assets 的加载路径。 如果你使用的是Resources加载方式,计算路径时需要进行修改为基于Resources的加载路径。

示例

    public void Example()
    {
        //在手机上不会触发调用
        #if UNITY_EDITOR
                mWindowConfig.GeneratorWindowConfig();
        #endif
    }

注意

需要在初始框架时调用,且禁止在手机上进行调用,否则会产生性能消耗。


GetWindowData

WindowData GetWindowData(string wndName)

根据窗口的名称,获取窗口的加载路径。

参数 作用
wndName 窗口名称

示例

    public void Example()
    {
        ZMAsset.Instantiate(mWindowConfig.GetWindowData(path).path, mUIRoot)
    }