Unity 极简UI框架
汐夜 人气:1写ui的时候一般追求控制逻辑和显示逻辑分离,经典的类似于MVC,其余大多都是这个模式的衍生,实际上书写的时候M是在整个游戏的底层,我更倾向于将它称之为D(Data)而不是M(Model),而C(Ctrl)负责接收用户的各类UI事件,例如点击,滑动,还有其他游戏逻辑板块发过来的事件或消息,处理这些消息并更新V(View)当中的各类显示数据,这里更新数据的方式可以抽象为两种:
1.外部事件触发View更新,这时不用在意底层数据更新,因为在刷新View之前这些改变的数据可以在其他逻辑版块中直接更新完。
2.UI内部点击,滑动等事件触发View更新,这种情况下有可能需要更新底层数据,但最好不要直接修改和调用,而是选择向外部发送事件和消息的方式来告知外部需要更新数据。
无论是上面两种情况中的哪一种,都不是View直接参与外部逻辑联系,而是借助中间的Ctrl来联系,Ctrl中处理UI与外部对接的所有逻辑,并能够及时的更新View。
再来分析下Ctrl,我们发现Ctrl的控制流程是可以固定下来,抽象如下:
1.进入一个View界面之前,得到View组件,初始化View中各个元素的状态
2.播放一段进入动画,例如淡入
3.进入动画播放完成后,对View中的一些元素添加事件侦听,或对外部的一些事件添加侦听
4.当侦听中的事件触发后,可以选择是否对View更新,或向外部发送事件,消息
5.同样的,离开时播放一段动画,例如淡出
6.离开动画播放完成后,移除所有事件侦听,载入一个新的View或场景
定义Ctrl基类:
1 using UnityEngine; 2 using UnityEngine.Events; 3 using UnityEngine.SceneManagement; 4 5 [RequireComponent(typeof(Canvas))] 6 public class HudBase : MonoBehaviour 7 { 8 public GameObject Root; 9 protected Canvas Canvas; 10 private void Awake() 11 { 12 Canvas = GetComponent<Canvas>(); 13 } 14 15 private void Start() 16 { 17 InitState(); 18 Enter(() => AddListeners()); 19 } 20 21 private void OnDestroy() => RemoveListeners(); 22 23 protected virtual void InitState() { } 24 25 protected virtual void AddListeners() { } 26 27 protected virtual void RemoveListeners() { } 28 29 protected void Enter(UnityAction complete) => Canvas.FadeIn(Root, () => complete()); 30 31 protected void ExitTo(string sceneName) => Canvas.FadeOut(Root, () => SceneManager.LoadScene(sceneName)); 32 33 protected T GetViweComponent<T>() where T : HudView => GetComponent<T>(); 34 35 protected void UpdateView<T>(T t) where T : HudView => t.Refresh(); 36 }
View基类:
1 using UnityEngine; 2 3 public class HudView : MonoBehaviour 4 { 5 public virtual void Refresh() { } 6 }
View只有一个自带的更新视图的通用方法,数据来源则直接取游戏底层即可,能够从Ctrl中直接调用View视图的更新。
其他通用的UI方法则全部写在一个统一的地方,例如淡入淡出的函数,向外部发送事件的,侦听事件等,这里统一写成了Canvas的扩展方法,便于在基类中也方便直接调用:
1 using System.Collections.Generic; 2 using UnityEngine; 3 using UnityEngine.UI; 4 using UnityEngine.Events; 5 using DG.Tweening; 6 7 public static class HudHelper 8 { 9 //UI 10 public static void FadeIn(this Canvas canvas, GameObject target, TweenCallback action) 11 { 12 var cg = target.GetOrAddComponent<CanvasGroup>(); 13 cg.alpha = 0; 14 cg.DOFade(1, .3f).OnComplete(action); 15 } 16 17 public static void FadeOut(this Canvas canvas, GameObject target, TweenCallback action) 18 { 19 var cg = target.GetOrAddComponent<CanvasGroup>(); 20 cg.DOFade(0, .3f).OnComplete(action); 21 } 22 23 public static void SendEvent<T>(this Canvas canvas, T e) where T : GameEvent => EventManager.QueueEvent(e); 24 25 public static void AddListener<T>(this Canvas canvas, EventManager.EventDelegate<T> del) where T : GameEvent => EventManager.AddListener(del); 26 27 public static void RemoveListener<T>(this Canvas canvas, EventManager.EventDelegate<T> del) where T : GameEvent => EventManager.RemoveListener(del); 28 29 public static void ButtonListAddListener(this Canvas canvas, List<Button> buttons, UnityAction action) 30 { 31 foreach (var bt in buttons) 32 { 33 bt.onClick.AddListener(action); 34 } 35 } 36 37 public static void ButtonListRemoveListener(this Canvas canvas, List<Button> buttons) 38 { 39 foreach (var bt in buttons) 40 { 41 bt.onClick.RemoveAllListeners(); 42 } 43 } 44 }
关于事件队列可以详细见之前的随笔:
https://www.cnblogs.com/koshio0219/p/11209191.html
具体的用法如下:(Ctrl)
1 using UnityEngine; 2 3 public class MapCanvasCtrl : HudBase 4 { 5 private MapCanvasView View; 6 public GameObject UnderPanel; 7 8 protected override void InitState() 9 { 10 View = GetViweComponent<MapCanvasView>(); 11 UnderPanel.SetActive(false); 12 UpdateView(View); 13 } 14 15 protected override void AddListeners() 16 { 17 View.Back.onClick.AddListener(() => ExitTo("S_Main")); 18 19 Canvas.ButtonListAddListener(View.TaskPoints, () => 20 { 21 UnderPanel.SetActive(true); 22 Canvas.FadeIn(UnderPanel, () => View.HitOut.onClick.AddListener(() => ExitTo("S_DemoBattle"))); 23 }); 24 } 25 26 protected override void RemoveListeners() 27 { 28 View.HitOut.onClick.RemoveAllListeners(); 29 Canvas.ButtonListRemoveListener(View.TaskPoints); 30 } 31 }
只需要重写以上三个方法即可。
(View):
1 using System.Collections.Generic; 2 using UnityEngine.UI; 3 using TMPro; 4 5 public class MapCanvasView : HudView 6 { 7 public TextMeshProUGUI Resource; 8 public TextMeshProUGUI Difficulty; 9 public TextMeshProUGUI Reward; 10 11 public Button HitOut; 12 public Button Back; 13 14 public List<Button> TaskPoints = new List<Button>(); 15 16 public override void Refresh() 17 { 18 //这里更新视图 19 } 20 }
加载全部内容