您的位置:首页 > 财经 > 产业 > 《Unity3D高级编程 主程手记》第四章 用户界面(五) 快速构建一个简单易用的 UI 框架

《Unity3D高级编程 主程手记》第四章 用户界面(五) 快速构建一个简单易用的 UI 框架

2024/11/17 22:42:13 来源:https://blog.csdn.net/renxi0/article/details/140959717  浏览:    关键词:《Unity3D高级编程 主程手记》第四章 用户界面(五) 快速构建一个简单易用的 UI 框架

从宏观角度看 UI 框架

        一个项目中拥有众多 UI,每个界面有很多组件;有不少界面是通用的;也有不少界面是父子关系;界面上的按钮需要有一个处理输入的句柄。基于这些思考,可以编写处统一的管理类、基类和输入事件响应机制。

1、管理类

        我们需要用一个单实例来管理所有的 UI,让它们有统一的接口进行以上的操作,创建UI管理类是最好的选择,我们可以命名它为 UIManager,这个名字符合它代表的功能。

具体作用:

  • 创建 UI
  • 查找现有的 UI
  • 销毁 UI
  • 完成 UI 的统一接口调用和调配工作

存储内容:

  • UI 实例
  • UI 常用变量,比如屏幕的适配标准大小、Camara 等。

        UIManager 是 UI 的管理员,统筹管理 UI 问题。其中涉及的管理包括上下层 UI 切换、不同的加载方式(预加载 UI 、销毁和隐藏)等,其源码如下。

public class ScreenManager : CSingleton<ScreenManager>
{protected Transform _transform = null;private Dictionary<string, UIScreenBase> _DicScreens = new Dictionary<string, UIScreenBase>();// 关闭所有界面public void CloseAll(){...}// 是否UI正打开public bool IsShow(string screenID){...}// 关闭界面public void CloseScreen(UIScreenBase screen){...}// 创建所有界面public T CreateMenu<T>() where T : UIScreenBase{...}// 找出某个界面public T FindMenu<T>() where T : UIScreenBase{...}...
}

 2、基类

        项目中有很多界面,这些界面都有一定的共性。共性产生统一特征的接口,如Init、Open和Close等。

        继承基类可使管理比较方便,比如上面提到的 UIManager 里的 UI 实例可以统一使用基类的方式存储。UIScreenBase

public abstract class UIScreenBase : MonoBehaviour
{protected bool mInitialized = false;protected UIState mState = UIState.None;public UIState State { get { return mState; } }public delegate void OnScreenHandlerEventHandler(UIScreenBase screen);public event OnScreenHandlerEventHandler onCloseScreen;// 初始化protected virtual void Init(){	mInitialized = true;}//打开public virtual void Open() {}//关闭public virtual void Close() {}
}

        每个界面都继承自 UI 基类,每个界面成为扩展界面功能的一个类实体,可以自主定义自己的功能性的接口,同时还会受到管理类的统一调配。 

3、输入事件响应机制

        Unity3D 的 UGUI 输入事件响应机制建立通常有两种,一种是继承型,一种是绑定型。

继承型

        事件先响应到基类,再由基类反应给父类,由父类做处理,这样 UI 既可以得到对输入事件的响应,也可以自行修改自己需要的逻辑。

绑定型

        在对输入事件响应之前,为 UI 元素绑定一个事件响应的组件。

        编写一个绑定型事件类 UIEvent,当某个 UI 元素需要输入事件回调时,对这个物体绑定一个 UIEvent,并且对 UIEvent 里需要的相关响应事件进行赋值或注册操作函数。当输入事件响应时,由 UIEvent 来区分输入的是什么类型的事件,再分别调用响应到具体函数。

共同点:都需要与UI元素关联

区别:继承型融入在了各种组件内,而绑定型以独立的组件形式体现出来的

/// <summary>
/// UI 事件
/// </summary>
public class UI_Event : UnityEngine.EventSystems.EventTrigger
{protected const float CLICK_INTERVAL_TIME = 0.2f; //const click interval timeprotected const float CLICK_INTERVAL_POS = 2; //const click interval pospublic delegate void PointerEventDelegate ( PointerEventData eventData , UI_Event ev);public delegate void BaseEventDelegate ( BaseEventData eventData , UI_Event ev);public delegate void AxisEventDelegate ( AxisEventData eventData , UI_Event ev);public Dictionary<string,object> mArg = new Dictionary<string,object>();public BaseEventDelegate onDeselect = null;public PointerEventDelegate onBeginDrag = null;public PointerEventDelegate onDrag = null;public PointerEventDelegate onEndDrag = null;public PointerEventDelegate onDrop = null;public AxisEventDelegate onMove = null;public PointerEventDelegate onClick = null;public PointerEventDelegate onDown = null;public PointerEventDelegate onEnter = null;public PointerEventDelegate onExit = null;public PointerEventDelegate onUp = null;public PointerEventDelegate onScroll = null;public BaseEventDelegate onSelect = null;public BaseEventDelegate onUpdateSelect = null;public BaseEventDelegate onCancel = null;public PointerEventDelegate onInitializePotentialDrag = null;public BaseEventDelegate onSubmit = null;private static PointerEventData mPointData = null;// 设置参数public void SetData(string key , object val){mArg[key] = val;}// 获取参数public D GetData<D>(string key){if(mArg.ContainsKey(key)){return (D)mArg[key];}return default(D);}...public static UI_Event Get(GameObject go){UI_Event listener = go.GetComponent<UI_Event>();if (listener == null) listener = go.AddComponent<UI_Event>();return listener;}public override void OnBeginDrag( PointerEventData eventData ) { ... }public override void OnDrag( PointerEventData eventData ) { ... }public override void OnEndDrag( PointerEventData eventData ) { ... }public override void OnDrop( PointerEventData eventData ) { ... }public override void OnMove( AxisEventData eventData ) { ... }public override void OnPointerClick(PointerEventData eventData){...if(onClick != null){onClick(eventData , this);}...}public override void OnPointerDown (PointerEventData eventData) { ... }public override void OnPointerEnter (PointerEventData eventData) { ... }public override void OnPointerExit (PointerEventData eventData) { ... }public override void OnPointerUp (PointerEventData eventData) { ... }public override void OnScroll( PointerEventData eventData ) { ... }
}

        以上代码只把事件响应最重要的部分,其余还包括组件的挂在、事件的调用及参数的设置等。

        最好事先把 UI_Event 挂载到 GameObject 节点上,这样可节省 AddComponent 的消耗,如果要在按钮响应时加入参数,则可再使用 SetData 来设置当前节点的回调参数。

        为什么要这么做?

  1. 统一管理所有事件句柄
  2. 更加方便地使用输入事件句柄
  3. 更方便地设置参数

4、自定义组件

(1)UI 动画组件

  • 首先它需要依赖 Unity3D 的 Animator 组件 [RequireComponent (typeof(Animator))]
  • 其次它要有播放(Play)接口用来播放指定动画,这里 Play 的参数包括动画名、播放完毕后的回调函数委托等。

  • 再次在 public 变量中需要 AutoPlay 这个参数,这样美术人员就可以在 Unity3D 界面上设置自动播放而无需程序调用了。

  • 最后需要在自动播放时选择指定的动画名和是否循环播放,以及循环播放间隔。

(2)按钮播放音效组件

(3)UI 元素跟随 3D 物体组件

        比如游戏中的血条、场景中建筑物头上的标志等。通过不断地计算 3D 物体在屏幕中的位置来确定 UI 位置,当前位置不同时再进行更改以避免不必要的移动。

(4)无限滚动页面组件

        比如背包界面。用一个自定义的无线滚动页面组价来替换原来的模式。

(5)其他组件

        包括数字飘字组件、计数组件、下拉框组件等。

        编写自定义的 UI 组件的目标就是,增加更多通用的组件,减少重复劳动,让程序员在编写 UI 界面时更加快捷、高效,同时也可提升 UI 的运行效率。拥有属于自己的一套自定义套件,对项目来说,也是一件非常有价值和高效的事。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com