介绍
由于VR游戏的UI相对来说比较复杂,普通的UGUI并不能满足要求,所以下面我们自定义一套更适合VR的UI框架,以便于开发和管理。
需求
- UI 画布(Canvas)统一管理(记录、提供显隐功能)。
- UI 事件管理。
类图
核心框架类:
- UI窗口类UIWindow: 所有UI窗口的基类,可以代表所有窗口(概念集成,以层次化方式管理类),定义所有窗口共有行为(显隐)。
- UI管理类UIManager:管理(记录、禁用、查找)窗口,定义所有窗口的共有行为(获取监听器)。
- UI事件监听器类UIEventListener:提供当前UI所有事件(带事件参数类)。
功能控制类:
- 游戏控制器GameController:负责处理游戏流程,例如游戏开始前显示主窗口。
工具类:
- 单例功能类MonoSingleton:提供单例模式,继承此类后可以实现单例。
- 变换组件助手类TransformHelper:提供一些变换组件的帮助方法。
应用类:
- UI主窗口类UIMainWindow:附加到主窗口中,负责处理主窗口逻辑。
UI结构
- 根物体 UIManager
- 窗口 xxxWindow : UIWindow
- 窗口 xxxWindow : UIWindow
类开发
核心框架类
- UI窗口类UIWindow: 所有UI窗口的基类,可以代表所有窗口(概念集成,以层次化方式管理类),定义所有窗口共有行为(显隐)。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using VRTK;
using y7play.Common;
namespace y7play.VR.UGUI.Framework
{
public class UIWindow : MonoBehaviour
{
private CanvasGroup canvasGroup;
private VRTK_UICanvas uICanvas;
private Dictionary<string, UIEventListener> uiEventDIC;
private void Awake()
{
canvasGroup = GetComponent<CanvasGroup>();
uICanvas = GetComponent<VRTK_UICanvas>();
uiEventDIC = new Dictionary<string, UIEventListener>();
}
public void SetVisable(bool state, float delay = 0)
{
StartCoroutine(SetVisibleDelay(state, delay));
}
private IEnumerator SetVisibleDelay(bool state, float delay)
{
yield return new WaitForSeconds(delay);
canvasGroup.alpha = state ? 1 : 0;
uICanvas.enabled = state;
}
public UIEventListener GetUIEventListener(string name)
{
if (!uiEventDIC.ContainsKey(name))
{
Transform tf = transform.FindChildByName(name);
UIEventListener uIEvent = UIEventListener.GetListener(tf);
uiEventDIC.Add(name, uIEvent);
}
return uiEventDIC[name];
}
}
}
- UI管理类UIManager:管理(记录、禁用、查找)窗口,定义所有窗口的共有行为(获取监听器)。
using System.Collections.Generic;
namespace y7play.VR.UGUI.Framework
{
public class UIManager : MonoSingleton<UIManager>
{
Dictionary<string, UIWindow> uiWindowDIC;
public override void Init()
{
base.Init();
uiWindowDIC = new Dictionary<string, UIWindow>();
UIWindow[] uiWindowArr = FindObjectsOfType<UIWindow>();
for (int i = 0; i < uiWindowArr.Length; i++)
{
uiWindowArr[i].SetVisable(false);
AddWindow(uiWindowArr[i]);
}
}
public void AddWindow(UIWindow window)
{
uiWindowDIC.Add(window.GetType().Name, window);
}
public T GetWindow<T>() where T : class
{
string key = typeof(T).Name;
if (!uiWindowDIC.ContainsKey(key)) return null;
return uiWindowDIC[key] as T;
}
}
}
- UI事件监听器类UIEventListener:提供当前UI所有事件(带事件参数类)。
using UnityEngine;
using UnityEngine.EventSystems;
namespace y7play.VR.UGUI.Framework
{
public delegate void PointerEventHandler(PointerEventData eventData);
public class UIEventListener : MonoBehaviour, IPointerDownHandler, IPointerClickHandler, IPointerUpHandler
{
public event PointerEventHandler PointerClick;
public event PointerEventHandler PointerDown;
public event PointerEventHandler PointerUp;
public static UIEventListener GetListener(Transform tf)
{
UIEventListener uiEvent = tf.GetComponent<UIEventListener>();
if (uiEvent == null) uiEvent = tf.gameObject.AddComponent<UIEventListener>();
return uiEvent;
}
public void OnPointerClick(PointerEventData eventData)
{
PointerClick?.Invoke(eventData);
}
public void OnPointerDown(PointerEventData eventData)
{
PointerDown?.Invoke(eventData);
}
public void OnPointerUp(PointerEventData eventData)
{
PointerUp?.Invoke(eventData);
}
}
}
功能控制类
- 游戏控制器GameController:负责处理游戏流程,例如游戏开始前显示主窗口。
using y7play.VR.UGUI;
using y7play.VR.UGUI.Framework;
namespace y7play
{
public class GameController : MonoSingleton<GameController>
{
private void Start()
{
UIManager.Instance.GetWindow<UIMainWindow>().SetVisable(true);
}
public void GameStart()
{
UIManager.Instance.GetWindow<UIMainWindow>().SetVisable(false);
}
}
}
工具类
- 单例功能类MonoSingleton:提供单例模式,继承此类后可以实现单例。
using UnityEngine;
namespace y7play
{
public class MonoSingleton<T> : MonoBehaviour where T : MonoSingleton<T>
{
private static T instance;
public static T Instance
{
get
{
if (instance == null)
{
instance = FindObjectOfType<T>();
if (instance == null)
{
new GameObject("Singleton of " + typeof(T)).AddComponent<T>();
}
else
{
instance.Init();
}
}
return instance;
}
}
protected void Awake()
{
if (instance == null)
{
instance = this as T;
Init();
}
}
public virtual void Init()
{
}
}
}
- 变换组件助手类TransformHelper:提供一些变换组件的帮助方法。
using UnityEngine;
namespace y7play.Common
{
public static class TransformHelper
{
public static Transform FindChildByName(this Transform cuurentTF, string childName)
{
Transform child = cuurentTF.Find(childName);
if (child != null) return child;
for (int i = 0; i < cuurentTF.childCount; i++)
{
child = FindChildByName(cuurentTF.GetChild(i), childName);
if (child != null) return child;
}
return null;
}
}
}
应用类
- UI主窗口类UIMainWindow:附加到主窗口中,负责处理主窗口逻辑。
using UnityEngine.EventSystems;
namespace y7play.VR.UGUI
{
public class UIMainWindow : y7play.VR.UGUI.Framework.UIWindow
{
private void Start()
{
GetUIEventListener("ButtonGameStart").PointerClick += OnPointerClick;
}
private void OnPointerClick(PointerEventData eventData)
{
print(eventData.pointerPress);
GameController.Instance.GameStart();
}
private void OnGameStartButtonClick()
{
GameController.Instance.GameStart();
}
}
}
使用方法
- 定义UIXXXWindow类,继承自UIWindow,负责处理该窗口逻辑。通过GetUIEventListener获取需要交互的UI元素。
- 通过UIManager.Instance.GetWindow<窗口类型>().方法()访问窗口的成员。
进一步改良
实际上对于一个大型项目来说,单个页面的层级结构可能会十分复杂且庞大,要尽可能的降低查询复杂度。可以将业务按模块(场景)划分,每个模块提供单独的配置文件,用于跟前端UI层级对应。以保证当项目整体层级需要改变时能够通过修改配置文件的方式进行统一修改。不同的场景应该提供不同的默认查找路径,提供默认的查抄方法,并另外提供其他模块的查找方法(这个方法应该尽量不对业务开发人员开放,避免程序健壮性受到影响)。不同的场景应该提供动态加载的功能,在场景切换时进行统一的加载和卸载,以保证合理的内存占用。
更多内容请查看总目录【Unity】Unity学习笔记目录整理
|