抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

简介

基于 IMGUI 的编辑器 UI 框架,目的是简化编辑器开发时的 UI 构建。利用特性标签进行 UI 的初始化。

对于变量,我们只需要打上对应的标签,就能够在 UI 面板上显示对应的 UI。

对于方法,我们只需要打上 Button 标签,就能够生成一个实现该方法的按钮。

同时,本框架还支持对 UI 的样式和布局进行有限的修改,同样也是通过特性实现。

UI 界面的渲染顺序按照编辑器脚本对象的声明顺序排序,布局的结构也要按照顺序声明,可以看作是以代码的形式绘制 UI。

本框架支持自定义拓展功能,具体的使用说明和拓展规则见:

原理

初始化特性

我们通过基类初始化获取 Type,然后通过 Type 获取 members 并且按照声明顺序排序,之后通过遍历,在其循环体内根据对应的特性类别进行 UI 的渲染函数初始化。

为了防止频繁反射,因此所有的 UI 都在编辑器展示的时候构造,数据的获取和存储都从反射转为委托。又因为 IMGUI 构造 UI 的方法无法在 OnGUI 以外的地方使用,因此我们在初始化时得到的是构造对应 UI 的方法函数。

特性声明

我们在定义特性的时候需要传入一个 [CallerLineNumber] int 类型的变量,该变量用于获取调用特性构造函数的行号,其中 [CallerLineNumber] 也是一个特性,用于获取调用方代码的行号。

只有通过这个方法才能自动获取到正确的声明顺序。如果你不想用这个方法,也可以在特性显示声明一个表明顺序的变量,然后手动传参,并且修改编辑器初始化函数中的排序方法。

渲染

渲染函数初始化

UI 渲染结构

为了支持布局的修改,我们就要规划好整个渲染的结构和顺序。

布局作为最外层的框架,需要把属于该布局的 UI 包裹在其中,并且可能存在布局套布局的情况。因此我们使用树结构来存储布局以及对应的 UI。

我们把 UI 的渲染函数或布局的节点存储在同一个 object 列表中,以便在渲染时按照正确的顺序渲染。

同时,我们设置一个函数委托,用于在渲染布局的时候先把外层的布局 UI 渲染好,然后我们再渲染内部的具体 UI。

UI 渲染具体过程

为了能够在 OnGUI 之外的地方使用 GUI 相关的方法,我们需要把具体的渲染行为封装为函数,之后在 OnGUI 中执行。

本框架把函数渲染分为三个部分:

  1. UI 渲染。
  2. Style 渲染。
  3. GUILayout 渲染。

UI 渲染负责生成 UI 对象,Style 渲染负责生成 GUIStyle 对象,而 GUILayout 渲染负责生成 GUILayout 数组。

最后在组合 UI 的时候把 GUIStyle 和 GUILayout 数组传入 UI 渲染函数。

例:

1
2
3
4
Action<GUIStyle, GUILayoutOption[]> label = (GUIStyle style, GUILayoutOption[] options) =>
{
GUILayout.Label(labelName(), style, options);
};

在上面的函数中,UI 渲染函数就是这个 Action 委托,Style 渲染函数在 OnGUI 执行传入 GUIStyle style ,GUILayout 渲染函数在 OnGUI 执行传入 GUILayoutOption[] options。最后我们执行完 UI 渲染函数就能够得到对应的 UI。

布局渲染函数的初始化需要传入具体的渲染方法,默认有两种,一种 NormalRender;另一种是 FlexRender 。这两种都在 BaseEditor 中定义。一般情况下只需要用到第一种,第二种是流式布局专用的。如果有其他需要也可以自己新增渲染函数。

具体的生成过程请参考项目工程和项目文档。

渲染函数执行

渲染函数在 OnGUI 中执行,分为两种情况:

  1. 当前对象为布局节点,则执行节点内的 _layout 函数。
  2. 当前对象为 UI,则执行 NormalRender
渲染执行函数
1
2
3
4
5
6
7
8
9
10
11
12
private void Render(LayoutNode node)
{
if (node._layout != null)
{
node._layout();
}
else
{
NormalRender(node)();
}

}

NormalRender 来说,遍历对象 List,如果是布局节点,则递归调用 Render 方法渲染;如果是 UI,则执行对应的渲染委托函数。

普通渲染函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/// <summary>
/// 普通渲染
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
private Action NormalRender(LayoutNode node)
{
Action action = () =>
{
foreach (var ui in node._list)
{
if (ui is LayoutNode)
{
Render((LayoutNode)ui);
}
else
{
UIListData data = (UIListData)ui;
data.action(data.style(), data.options());
}
}
};
return action;
}

项目

由于本项目代码较多,因此请移步 GitHub 查看详细代码。

更新日志

2024-05-30

  1. 修改部分说明。

2024-03-18

  1. 更新基础内容。

评论