Unity UI 框架

开源地址: 

GitHub - NRatel/NRFramework.UI: 基于 Unity UGUI 的 UI 开发框架基于 Unity UGUI 的 UI 开发框架. Contribute to NRatel/NRFramework.UI development by creating an account on GitHub.icon-default.png?t=N7T8https://github.com/NRatel/NRFramework.UI

一些相关的思考:

https://blog.csdn.net/NRatel/article/details/127931997icon-default.png?t=N7T8https://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上的组件。

快捷键更改(2023/10/22):
NRUITools/SetAsUIOpElement (快捷键 alt + 1)
NRUITools/RemoveOpElement (快捷键 alt + 2)

 ⑶、可点击 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(支持灵活设置背景、焦点、返回键按下类型,但最好通过增加类型的方式解决)

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

增加1个类型预制(2023年7月6日):

ModalWindow:HasBg = true; BgShowType = HalfAlphaBlack;  BgClickEventType = false; GetFocusType = true;

 思维导图大图原图

六、Panel 其他配置

1、Panel 厚度配置

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

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

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

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

  • 23
    点赞
  • 87
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NRatel

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值