草庐IT

Unity UI 框架

NRatel 2023-07-21 原文

开源地址: 

GitHub - NRatel/NRFramework.UI: 基于 Unity UGUI 的 UI 开发框架基于 Unity UGUI 的 UI 开发框架. Contribute to NRatel/NRFramework.UI development by creating an account on GitHub.https://github.com/NRatel/NRFramework.UI

一些相关的思考:

https://blog.csdn.net/NRatel/article/details/127931997https://blog.csdn.net/NRatel/article/details/127931997

一、需求/功能要点

二、类结构设定

1、UIPanel 和 UIWidget 继承自 UIView,UIView 聚合 UIWidget。

2、UIRoot 提供 Panel 的创建、关闭、销毁、设置显隐等接口,并对当前层的层级进行管理。

public T CreatePanel<T>(string panelId, string prefabPath, int sortingOrder) where T : UIPanel {}

// 不指定 sortingOrder(使自增)
public T CreatePanel<T>(string panelId, string prefabPath) where T : UIPanel {}

// 以类型名作为id
public T CreatePanel<T>(string prefabPath, int sortingOrder) where T : UIPanel {}

// 以类型名作为id 且 不指定 sortingOrder(使自增)
public T CreatePanel<T>(string prefabPath) where T : UIPanel {}

public void ClosePanel(string panelId, Action onFinish = null) {}

public void ClosePanel<T>(Action onFinish = null) where T : UIPanel {}

public void DestroyPanel(string panelId) {}

public void DestroyPanel<T>() where T : UIPanel {}

public void SetPanelVisible(string panelId, bool visible) {}

public void SetPanelVisible<T>(bool visible) where T : UIPanel {}

3、UIView 中定义了界面创建基本过程模板,并提供 Widget 的创建、销毁接口。

public T CreateWidget<T>(string widgetId, Transform parentTransform, string prefabPath) {}

public T CreateWidget<T>(string widgetId, Transform parentTransform, UIWidgetBehaviour widgetBehaviour) {}

//以类型名作为id
public T CreateWidget<T>(RectTransform parentTransform, string prefabPath) where T : UIWidget {}

//以类型名作为id
public T CreateWidget<T>(Transform parentTransform, UIWidgetBehaviour widgetBehaviour) {}

//不改变自身父物体
public T CreateWidget<T>(string widgetId, UIWidgetBehaviour widgetBehaviour) {}

//以类型名作为id 且 不改变自身父物体
public T CreateWidget<T>(UIWidgetBehaviour widgetBehaviour)

public void DestroyWidget(string widgetId) {}

public void DestroyWidget<T>() where T : UIWidget {}

3、UIPanel 中维护自身 显示状态 和 动画状态(重要,管理不好状态,后期可能出现各种异步冲突问题,状态也是系统(如引导)随时操作UI的基础)、并提供操作自身的接口 和 子类可重写的打开/关闭动画接口(比如,播放动画时可将Widget考虑进去)。

public enum UIPanelShowState { Initing, Refreshing, Idle, Hidden, /* Destroyed */ }

public enum UIPanelAnimState { Opening, Idle, Closing, Closed }


protected void CloseSelf(Action onFinish = null) {}

protected void DestroySelf() {}

protected void SetSelfVisible(bool visible) {}


protected virtual void PlayOpenAnim(Action onFinish = null)

protected virtual void PlayCloseAnim(Action onFinish = null)

4、UIManager 中管理整体 Panel,包括背景处理、焦点管理、返回键回退逻辑。提供 创建 UIRoot 、Panel 筛选、组件反射查找(从root开始)等的接口。

public UIRoot CreateRoot(string rootId, int startOrder, int endOrder) {}


public List<UIPanel> FilterPanels(Func<UIPanel, bool> filterFunc = null) {}


public int FindPanelComponent<T>(string rootId, string panelId, string compDefine, out T comp) where T : Component {}

public int FindWidgetComponent<T>(string rootId, string panelId, string[] widgetIds, string compDefine, out T comp) where T : Component {}

public int FindComponentByPath<T>(string path, string compDefine, out T comp) where T : Component {}

(如下图可知,这种结构符合面向对象思想,Widget可深层嵌套,从而适合大规模系统)

---------------------------------------- NRatel 割 ----------------------------------------

额外重要说明:

⑴、所有的 UIPanel 都将创建于同一个 Canvas 下(便于直观了解层级关系)。

⑵、可以按照实际需求创建出若干个UIRoot,独立管理各自层级。

⑶、创建 UIPanel 或 UIWidget 时,可指定 Id,或默认将类型名作为 Id,可指定 Id 的好处是:①,索引更容易;②,相同预设且相同逻辑的界面可重复打开。

⑷、创建 UIPanel 或 UIWidget 时,可通过 prefabPath 创建。需要在 UIView 的 Create 方法中改用自封装的资源加载接口。

⑸、创建 UIWidget 时,允许为一个已存在的 UIWidgetBehaviour 动态绑定逻辑,这意味着支持UI预设嵌套,保证了UI预设的复用性。例如(游戏中每个确认框拥有相同的标题栏):

 ⑹、创建 UIPanel 时,可使 SortingOrder 自增(默认),也可直接指定(比如,系统很小,可将所有界面枚举出来,直接配出每个界面的Order)。

⑺、框架不提供 UIPanel 和 UIWidget 的初始化/刷新接口,用户可以自己按需定义。需要注意的是,如果其初始化/刷新 过程是异步的,需要自己维护好显示状态。若是同步的,则不用管(默认是 Idle)。

三、界面配置结构

1、UIView 关联的 UIVeiwBehaviour 继承自 MonoBehaviour,处理元素收集。

2、UIPanel 关联的 UIVeiwBehaviour 继承自 UIVeiwBehaviour,处理 Panel 配置。

3、UIWidget 关联的 UIWidgetBehaviour 继承自 UIVeiwBehaviour,处理 Widget 配置。

四、元素收集与代码生成

1、可同时收集一个 GameObject 上的多个组件。

(经常出现:需要对一个 GameObject 上的多个组件同时操作的情况,如:一个按钮需要操作其显示内容(Image组件)、还要操作其按钮点击事件(Button组件))

2、收集到的元素在 Hierarchy 上显示(方便观察修改)(可配置为关闭)。

3、提供多种操作方式:

⑴、(手动添加)拖拽 Hierarchy上的 GameObject 到 UIPanelBehaviour/UIWidgetBehaviour的 Inspector 上,然后从右侧下拉列表中选择想要收集的组件。

 

 ⑵、(自动推测添加)在 GameObejct 的右键菜单中,

选择 NRUITools/SetAsUIOpElement (快捷键 alt + s)将推测出此GameObejct上一个最可能操作的组件加入操作元素列表。推测优先级为:可交互组件 > 布局相关组件 > 自定义需要添加的组件(可修改) > 图形组件 > RectTransform 或 Transform(保底)

选择 NRUITools/RemoveOpElement (快捷键 alt + r)将移除已收集的此GameObejct上的组件。

 ⑶、可点击 Inspector 上的 “+” 新增、“-” 删除、“清理图标”全部删除、“刷新图标”整理(删除无效的行并合并相同 GameObejct 对应的行)。

4、代码生成

⑴、注意点

①、需要先在 EditorSetting 中设置 uiPrefabRootDir(预设所在根路径)、generatedBaseUIRootDir(基类生成根路径)、generatedTempUIRootDir(快捷业务模板类生成根路径)。

②、路径配置均相对于 Application.dataPath。

③、代码相对于其生成根路径的子路径为:预设相对于uiPrefabRootDir的子路径,若预设不在配置的 uiPrefabRootDir 下,不允许生成。

④、应该在非运行时生成代码(报错检查)。

⑤、生成代码时,将先执行一次整理(删除无效的行并合并相同 GameObejct 对应的行)。

⑵、点击 Inspector 上的 ExportBase 按钮,可生成预设对应的基类。

其中,包含 组件定义、组件可交互事件绑定、组件可交互事件取消绑定、组件定义置null。

组件命名规则为:m_FormatedGoName_CompShortName。

FormatedGoName 将 GoName 去除特殊字符(只保留英文数字下划线),并将其首字母大写。若 FormatedGoName 重复,将在导出时报错提示。

CompShortName 为缩短后的组件名称,可在 UIEditorUtility 的 GetCompShortName 中定义映射关系。

示例如下:


using UnityEngine;
using UnityEngine.UI;
using TMPro;
using NRFramework;

public class UIPanelHomeBase : UIPanel
{
	protected TextMeshProUGUI m_Desc_TMPText;
	protected Image m_Bg_Image;
	protected Button m_BtnAddIcon_Button;
	protected Image m_BtnAddIcon_Image;
	protected RectTransform m_IconRoot_RectTransform;
	protected Button m_BtnTestFindComp_Button;

    protected override void OnBindCompsAndEvents() 
    {
		m_Desc_TMPText = (TextMeshProUGUI)viewBehaviour.GetComponentByIndexs(0, 0);
		m_Bg_Image = (Image)viewBehaviour.GetComponentByIndexs(1, 0);
		m_BtnAddIcon_Button = (Button)viewBehaviour.GetComponentByIndexs(2, 0);
		m_BtnAddIcon_Image = (Image)viewBehaviour.GetComponentByIndexs(2, 1);
		m_IconRoot_RectTransform = (RectTransform)viewBehaviour.GetComponentByIndexs(3, 0);
		m_BtnTestFindComp_Button = (Button)viewBehaviour.GetComponentByIndexs(4, 0);

		BindEvent(m_BtnAddIcon_Button);
		BindEvent(m_BtnTestFindComp_Button);
	}

    protected override void OnUnbindCompsAndEvents() 
    {
		UnbindEvent(m_BtnAddIcon_Button);
		UnbindEvent(m_BtnTestFindComp_Button);

		m_Desc_TMPText = null;
		m_Bg_Image = null;
		m_BtnAddIcon_Button = null;
		m_BtnAddIcon_Image = null;
		m_IconRoot_RectTransform = null;
		m_BtnTestFindComp_Button = null;
	}
}

⑶、点击 Inspector 上的 ExportTemp 按钮,可生成预设对应的快捷业务模板类。

生成的代码文件名及其类名将自动拼上 _Temp,以避免意外覆盖已写代码。

其中主要包含,类的生命周期(若不需要,均可删除)。

示例如下:

using System;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using NRFramework;

public class UIPanelHome_Temp : UIPanelHomeBase
{
    protected override void OnCreating() { }

    protected override void OnCreated() { }

    protected override void OnClicked(Button button) { }

    protected override void OnValueChanged(Toggle toggle, bool value) { }

    protected override void OnValueChanged(Dropdown dropdown, int value) { }

    protected override void OnValueChanged(TMP_Dropdown tmpDropdown, int value) { }

    protected override void OnValueChanged(InputField inputField, string value) { }

    protected override void OnValueChanged(TMP_InputField tmpInputField, string value) { }

    protected override void OnValueChanged(Slider slider, float value) { }

    protected override void OnValueChanged(Scrollbar scrollbar, float value) { }

    protected override void OnValueChanged(ScrollRect scrollRect, Vector2 value) { }
    
    protected override void OnVisibleChanged(bool visible) { }
    
    protected override void OnFocusChanged(bool got) { }

    //protected override void OnBackgroundClicked() { }

    //protected override void OnEscButtonPressed() { }

    protected override void OnDestroying() { }

    protected override void OnDestroyed() { }
}

五、Panel 基本配置分析与设定

从三个“要素” 上分析变量,预定义出五种需求对应的类型。

可在 UIPanelBehaviour 上进行配置。

需要注意:框架对 UIPanel 的处理始终针对 “要素”变量,而非类型。

类型只用作快捷配置出一组自己需要的要素变量。
其中,背景和焦点的配置将随类型完全固定,返回键按下的配置会随类型默认一个最可能的、但是可改(因为其本身的需求可能会比较复杂)。

类型可根据实际需求自行扩展。

-----------------------------------------------------------------

1、三个要素:

⑴、背景:界面打开时自动创建/获取一个全局唯一的RawImage,挂入界面根节点下 FirstSibling。

⑵、焦点:准确地说是“运行焦点”。界面失去焦点时,可选择性地“挂起”(暂停内部耗时Update类操作),并在重新获得焦点时恢复,以此优化。另外,还可在获得焦点时做一些事件触发,比如拍脸弹窗等。

⑶、返回键按下:安卓 Google play 推荐要求处理返回键逻辑,通常需要关闭顶层界面,但有很多例外。

2、五种类型需求(截止目前我用到、想到的):

【铺垫/场景型/全屏界面、一级功能界面等】:有背景(透明、阻挡下方事件、点击背景不响应);获得焦点;返回键按下时可能关闭自身/不响应/自定义

【叠加/局部类型界面(如Home主菜单、较重界面拆分界面等】:无背景;与下方可获得焦点的界面共同获得焦点;返回键按下时不响应/不检测/自定义

【弹出型功能界面、确认框等】:有背景(黑色半透、阻挡下方事件、点击背景默认关闭自身);获得焦点;返回键按下时大概率关闭自身,可能自定义

【浮动功能气泡(如聊天气泡)、Toast等】:无背景、不抢夺焦点;返回键按下时大概率不检测

【网络转圈等待、引导界面等】:有背景(黑色半透、阻挡下方事件、点击背景不响应)、不抢夺焦点;返回键按下时大概率不响应

3、变量定义

HasBg 是否有背景(Bool)

        BgShowType 背景展示类型(Enum)(若有背景)

                Alpha 透明(Color(0, 0, 0, 0))

                HalfAlphaBlack 半透黑色(Color(0, 0, 0, 0.7))

                CustomColor 自定义颜色(Color)

                CustomTexture 自定义贴图(Texture)(不一定需要,暂不实现)

                BlurryScreenshot 模糊屏幕快照(Texture)(不一定需要,暂不实现)

                ?...(可增加预制类型)

        BgClickEventType 背景点击事件类型(Enum)(若有背景)

                PassThrough 不阻挡、穿透

                DontRespone 阻挡但不响应

                CloseSelf 关闭自身

                Custom 触发自定义回调

GetFocusType 获得焦点类型(Bool)

        DontGet 不抢夺焦点

        Get 获得焦点

        GetWithOthers 与下方可获得焦点的界面共同获得焦点

EscPressEventType 返回键按下事件类型(Enum)

        DoneCheck 不检测、跳过

        DontRespone 检测但不响应

        CloseSelf 关闭自身

        Custom 触发自定义回调

4、类型预制 (对应类型需求) (固定背景) (固定焦点) (返回键按下可改)

Underlay(铺垫、全屏的):HasBg = true; BgShowType = Alpha; BgClickEventType = DontRespone; GetFocusType = Get;

Overlay(叠加、局部的):HasBg = false; GetFocusType = GetWithOthers

Window(窗体):HasBg = true; BgShowType = HalfAlphaBlack; BgClickEventType = CloseSelf; GetFocusType = Get

Float(浮动):HasBg = false; GetFocusType = DontGet

System(系统、非业务的):HasBg = true; BgShowType = HalfAlphaBlack;  BgClickEventType = false; GetFocusType = false;

?...(可增加预制类型)

Custom(支持灵活设置背景、焦点、返回键按下类型,但最好通过增加类型的方式解决)

 思维导图大图原图

六、Panel 其他配置

1、Panel 厚度配置

框架将为每个 Panel 自动添加一个Canvas,以供排序、避免所有UI在同一个Canvas下重建耗时、添加3D特效等。排序时默认每个Panel 的厚度为 10,若不够,可以自行修改。

2、Panel 打开/关闭动画配置

当 UIPanelBehavior 统计物体上存在有效的 Animator 组件组件时,打开/关闭动画的配置将被激活,此时,可以设定 Panel 在打开/关闭时 AutoPlay(自动播放动画) 或 ControlBySelf(由自己在代码中控制播放)。

对于 “Animator 是否有效” 可以在 UIPanelBehavior 中添加更复杂的检查,比如:
Animator 组件存在、是否启用、是否有Controller资源、内容是否符合要求(有open、close动画,有跳转条件等...)

有关Unity UI 框架的更多相关文章

  1. TimeSformer:抛弃CNN的Transformer视频理解框架 - 2

    Transformers开始在视频识别领域的“猪突猛进”,各种改进和魔改层出不穷。由此作者将开启VideoTransformer系列的讲解,本篇主要介绍了FBAI团队的TimeSformer,这也是第一篇使用纯Transformer结构在视频识别上的文章。如果觉得有用,就请点赞、收藏、关注!paper:https://arxiv.org/abs/2102.05095code(offical):https://github.com/facebookresearch/TimeSformeraccept:ICML2021author:FacebookAI一、前言Transformers(VIT)在图

  2. ruby - sinatra 框架的 MVC 模式 - 2

    我想开始使用“Sinatra”框架进行编码,但我找不到该框架的“MVC”模式。是“MVC-Sinatra”模式或框架吗? 最佳答案 您可能想查看Padrino这是一个围绕Sinatra构建的框架,可为您的项目提供更“类似Rails”的感觉,但没有那么多隐藏的魔法。这是使用Sinatra可以做什么的一个很好的例子。虽然如果您需要开始使用这很好,但我个人建议您将它用作学习工具,以对您来说最有意义的方式使用Sinatra构建您自己的应用程序。写一些测试/期望,写一些代码,通过测试-重复:)至于ORM,你还应该结帐Sequel其中(imho

  3. ruby-on-rails - 正确了解 Rails 框架的最佳方式是什么? - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭10年前。我一直在Rails上做两个项目,它们运行良好,但在这个过程中重新发明了轮子,自来水(和热水)和止痛药,正如我随后了解到的那样,这些已经存在于框架中。那么基本上,正确了解框架中所有智能部分的最佳方法是什么,这将节省时间而不是自己构建已经实现的功能?从第1页开始阅读文档?是否有公开所有内容的特定示例应用程序?一个特定的开源项目?所有的rails交通?还是完全

  4. ruby - 自动将院子文档框架添加到现有的 Rails 遗留代码中 - 2

    关闭。这个问题不符合StackOverflowguidelines.它目前不接受答案。我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。关闭4年前。Improvethisquestion我希望能够将模板化的YARD文档样式注释插入到我现有的Rails遗留应用程序中。目前它的评论很少。我想要具有指定参数的类header和方法header(通过从我假定的方法签名中提取)和返回值的占位符。在PHP代码中,我有一些工具可以检查代码并在适当的位置创建插入到代码中的文档header注释。在带有Ducktyping等的Ruby中,我确信诸如@params等类型之类

  5. ruby-on-rails - 具有六边形架构和 DCI 模式的框架和数据库适配器 - 2

    我尝试用Ruby设计一个基于Web的应用程序。我开发了一个简单的核心应用程序,在没有框架和数据库的情况下在六边形架构中实现DCI范例。核心六边形中有小六边形和网络,数据库,日志等适配器。每个六边形都在没有数据库和框架的情况下自行运行。在这种方法中,我如何提供与数据库模型和实体类的关系作为独立于数据库的关系。我想在将来将框架从Rails更改为Sinatra或数据库。事实上,我如何在这个核心Hexagon中实现完全隔离的rails和mongodb的数据库适配器或框架适配器。有什么想法吗? 最佳答案 ROM呢?(Ruby对象映射器)。还有

  6. python - Ruby 是否有相当于 Python 的扭曲框架作为网络抽象层? - 2

    据我了解,Python的扭曲框架为网络通信提供了更高级别的抽象(?)。我正在寻找在Rails应用程序中使用与twisted等效的Ruby。 最佳答案 看看EventMachine.它不像Twisted那样广泛,但它是围绕事件驱动网络编程的相同概念构建的。 关于python-Ruby是否有相当于Python的扭曲框架作为网络抽象层?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/9

  7. ruby-on-rails - 使用 Rails 以外的 Ruby 框架是否有任何潜在的缺点? - 2

    我想使用比Rails(Sinatra/Ramaze/Camping)更轻的框架,但我担心这样做我将无法使用许多以插件形式为Rails定制的共享库.这是一个主要问题,还是这些插件中的大多数都可以跨不同的Ruby框架使用?使用Ruby框架而不是Rails是否还有其他潜在的缺点? 最佳答案 您仍然可以使用gems在你提到的所有框架中,很多东西都是可重用的。想要交换一个新的ORM,没问题。想要一个花哨的shmacy语法高亮,没问题。Rails一直在大力插入摆脱旧的插件模型,转而使用gems。如果其他框架之一符合您的需求,最好使用它。请记住,

  8. ruby - 应该 validate_format_of 。 not_with 在框架中有问题(或者在我的理解中) - 2

    我将以下代码放入RSpec测试中:it{shouldvalidate_format_of(:email).not_with('test@test')}并设置实际的类:validates:email,:presence=>true,:format=>/\b[A-Z0-9._%-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}\b/i当我运行测试时,我得到:失败:1)用户失败/错误:它{应该validate_format_of(:email).not_with('test@test')}当电子邮件设置为“test@test”时,预期错误包括“can'tbeblank”,得到错误

  9. ruby-on-rails - Rails 使用了哪些测试框架? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭9年前。ImprovethisquestionRails使用了哪些单元测试框架?我正在阅读一本书(PragmaticProgrammersAgileDev.withRails),其中展示了如何在Rails中进行单元测试。这本书向我展示了默认的Rails测试套件(Test::Unit的子类)。这是Rails社区中使用的主要测试框架吗?我在执行常规ruby​​时使用RSpec,我也希望能够在Rails中使用它(如果不是太麻烦的话)?

  10. ruby - rspec模拟与其他模拟框架的优缺点是什么? - 2

    我看过很多过时的播客,其中提到摩卡是我想安装的一个宝石,因为它确实比rspec更好模仿。我有一种感觉,rspec开发人员已经意识到这一点,并从那时起改进了他们的模拟。但是,在默认的spec_helper.rb文件中,我看到三个模拟框架的一些注释掉的代码存根mochaflexmockrr向任何能给我一个像样答案的人投赞成票,就这些框架中至少一个的利弊与rspec自己的模仿框架进行比较。如果你能给我一个关于这三个问题的详细说明,我会接受你的回答。 最佳答案 真的,这只是口味的问题。看一看语法,看看什么最适合你。当然,使用rspec的内置

随机推荐