写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 }