Unity UI事件管理系统设计
来源:互联网 发布:中化江苏公司 知乎 编辑:程序博客网 时间:2024/05/23 01:56
UI框架的设计是任何游戏都要做的事情,其中事件管理器(EventManager)是比较常用的UI与逻辑分离的方法,通过注册、绑定、分发事件来控制UI界面或者游戏场景的逻辑处理。之前做cocos游戏写过c++版本、lua版本的事件管理器,Unity大同小异,但是也有很多特殊的地方,这边我记录下设计过程。
特别提醒,如果习惯使用当前比较成熟的Unity MVC、SingleIoc、UIFrame等UI框架的同学就不要探究这篇笔记了,所有的UI框架都是为了让项目开发高内聚,低耦合,维护和迭代效率高为目的的,习惯用一个就可以了,如果有跟我一样非得自己写框架用的才爽的同学可以来了解下,可能里面的想法对您有用。
首先我的UI事件管理系统必须满足以下需求:
1、UI与逻辑分离
这是最基本需求,我不希望以后游戏更换UI要到处修改逻辑,我想要的是一个UI界面负责展示,一个UIManager来定义一些UI变化的逻辑,UI的变化由事件驱动,所以一个UI界面+一个UIManager+一个事件分发器就可以了,以后换UI只要改UI界面就可以了,这对比较复杂的项目非常重要,千万不要UI和逻辑揉到一起,尤其是Unity这种组件化的开发引擎,有时喜欢偷个懒,比如一个按钮Button点击触发界面A的变化,然后就把A拖拽到Button所在对象中作为组件,然后调用A的方法,这种做法一定不能有,后期维护成本会很高。
UI与逻辑分离的完美设计模式可能就是MVC了,但是要严格按照MVC设计模式做游戏我感觉不是很合适,游戏的模型太灵活多变,版本变动也很大,如果有MVC严格设计游戏UI,可能简单问题复杂化了(之前确实有同事用MVC做游戏,代码量巨大,看着头疼脑热),总之,我不选择MVC,但是上面我说的UI+UiManager+EventManager基本上也满足了UI与逻辑的分离,够用就可以了。
2、所有按钮的回调入口要统一
一个按钮写一个脚本肯定不是正常人的思路,怎么让按钮的回调入口统一呢,我写了一个TriggerEventManager模块作为EventManager的附属模块,就是一个分发事件的脚本,所有按钮只要是用到事件系统的(有些按钮可能很简单,功能是点一下隐去一个界面,直接在Inspector面板的onClik中把这个界面的Alpha改为0就好了,不需要事件系统)全部调用这个脚本接口即可,从此不需要到处找Button脚本了。
public class _TriggerEventManager : MonoBehaviour {private _EventManager _eventManager;void Start(){_eventManager = GetComponent<_EventManager>() as _EventManager;}/// <summary>/// 无参事件/// </summary>/// <param name="eventKey">Event key.</param>public void TriggerEventFromNoParam(string eventKey){_eventManager.__TriggerEvent(eventKey);}
3、对某一个事件可以多个目标响应(类似于观察者模式)
界面A和界面B可能在我按下Button的时候都有相应,那我的EventManager中对事件的要求就是“一对多”的关系,一个事件会注册N个回调代理函数
/// <summary>/// 定义代理方法,接受层,接到消息后的回调函数/// 回调函数的参数,可为空/// </summary>public delegate void EventHandle(object __PARAM = null);/// <summary>/// 定义事件字典,记录所有分发中的事件/// 支持一个事件多个目标响应/// </summary>public Dictionary<string,List<EventHandle>> __EVENTDIC;
4、支持UGUI系统的onClick Inspector界面动态绑定,同时也支持代码直接调用
Unity是高度组件化的游戏编程引擎,无论我们习惯用NGUI、UGUI甚至GUI做界面,按钮的回调设计方法有很多种,直接在Inspector中绑定onClick脚本、使用sendMessage、EventLisener等等,我的想法是我的UI管理模块既要可以拖拽绑定onClick,也要可以代码直接调用,这个地方开始是比较纠结,EventManager做成单例?无法拖拽到onClick上面了,而且事件管理器我不希望一个游戏工程只有一个(原因是如果所有的界面事件都要注册到一个地方,不方便多人开发,每个人都要动这个文 件,没办法做版本控制),做成MonoBehivour挂载到场景中,还得先find它再调用它的接口,麻烦一些也耗性能。总之权衡了一下,还是选择了后者,心中还是会默念"后面如果能不继承MonoBehavior就不继承它,保证最后一次"。
综上,我做了一个UIEventSystem预设体
将UIEventSystem拖入场景负责当前场景的所有UI事件管理,也可以拖动多个,多人开发各自定义事件,也不容易冲突掉。
下面就是怎么注册和事件消息和分发消息了,比如我注册一个"startgame"的消息
随便点击一个按钮,触发开始游戏
整个注册和分发流程结束,下面我们来响应这个事件,上面说过我习惯每个UI界面写一个UIManager,用来控制UI界面或者跟UI界面相关的逻辑,那么UiManager同样也是事件的观察者(它只负责观察和响应,不能分发事件,这个很重要,各司其职才能让你的事件系统不会乱成一张网)
[MonoHeader("装备的基础界面,控制界面UI的变化")]public class EquipBasePanelManager : MonoBehaviour {private _EventManager _eventManager;// Use this for initializationvoid Start () {_eventManager = GameObject.Find("UIEventSystem").GetComponent<_EventManager>() as _EventManager;//绑定事件_eventManager.__AttachEvent("startgame",startGameEventHandle);}/// <summary>/// 开始游戏/// </summary>/// <param name="o">O.</param>public void startGameEventHandle(object o){SceneManager.LoadScene("running");}}
上面是无参回调方法,后面的代码也支持有参回调,奉上所有代码
//// _EventManager.cs// EndlessRunner//// Created by jiabl on 09/27/2017.////using System.Collections;using System.Collections.Generic;using UnityEngine;using MiniJSONV;/// <summary>/// 事件基类管理器/// 所有的事件(尤其是UI交互事件都必须用管理器写事件,防止后期游戏复杂度增加维护混乱)/// 目的:所有的界面与逻辑分离/// 基类负责所有的事件分发处理逻辑/// </summary>[MonoHeader("事件管理器,在当前场景生命周期有效,和_ButtonEventManager脚本一起构成由按键触发的事件控制器 的预设体")]public class _EventManager : MonoBehaviour {/// <summary>/// 消息列表,每一个事件对应一个枚举类型的消息,枚举类型设计为泛型,方便每个场景都有单独的事件消息层,防止多人开发冲突/// </summary>[Header("消息类型列表,请定义不能重复的消息key")]public string[] __EVENTMSG;/// <summary>/// 定义代理方法,接受层,接到消息后的回调函数/// 保留第一个消息参数,为了更方便的注册到统一入口函数中,第二个参数是回调函数的参数,可为空/// </summary>public delegate void EventHandle(object __PARAM = null);/// <summary>/// 定义事件字典,记录所有分发中的事件/// 支持一个事件多个目标响应/// </summary>public Dictionary<string,List<EventHandle>> __EVENTDIC;void Awake(){__EVENTDIC = new Dictionary<string,List<EventHandle>>();//将事件放入字典中管理__AddEvent();}/// <summary>/// 注册事件/// </summary>private void __AddEvent(){if(0 == __EVENTMSG.Length)Debug.LogWarning("_EVENTMSG is empty,please define add msg key as a array!!");foreach(string __MSG in __EVENTMSG){__AddDelegate(__MSG);}}/// <summary>/// 将事件放入字典中管理/// </summary>/// <param name="_eventKey">Event key.</param>private void __AddDelegate(string __MSG){if(__EVENTDIC.ContainsKey(__MSG)){}else{__EVENTDIC.Add(__MSG,new List<EventHandle>());}}/// <summary>/// 触发一个事件/// </summary>/// <param name="_eventKey">Event key.</param>/// <param name="param">Parameter.</param>public void __TriggerEvent(string __MSG,object __param = null){if(__EVENTDIC.ContainsKey(__MSG)){foreach(EventHandle __handle in __EVENTDIC[__MSG]){__handle(__param);}}else{Debug.LogError(__MSG + " is undefine from function: _EventBaseManager::_TriigerEvent");}}/// <summary>/// 事件绑定/// 事件的接收层要调用这个方法,当然也可以使用下面的批量绑定方法,不需要每一个事件写一个函数/// </summary>public void __AttachEvent(string __MSG, EventHandle __eventHandle){if (__EVENTDIC.ContainsKey(__MSG)){__EVENTDIC[__MSG].Add(__eventHandle);}else{Debug.LogError(__MSG + " is undefine from function: _EventBaseManager::_AttachEvent");}}/// <summary>/// 批量导入,所有的事件处理函数入口是唯一的/// </summary>/// <param name="__eventHandle">Event handle.</param>public void __BatchAttachEvent(EventHandle __eventHandle){foreach(string __MSG in __EVENTMSG){__AttachEvent(__MSG,__eventHandle);}}/// <summary>/// 去除事件绑定/// </summary>public void __DetachEvent(string __MSG, EventHandle __eventHandle){if (__EVENTDIC.ContainsKey(__MSG)){__EVENTDIC[__MSG].Remove(__eventHandle);}else{Debug.LogError(__MSG + " is undefine from function: _EventBaseManager::_DetachEvent");}}/// <summary>/// 销毁管理器,当用代码(单例模式)控制事件分发的时候,结束使用请销毁(因为它不会自动销毁)/// </summary>public void __Destroy(){MonoBehaviour.Destroy(this.gameObject);}}
//// _ButtonEvent.cs// EndlessRunner//// Created by jiabl on 09/28/2017.////using System.Collections;using System.Collections.Generic;using UnityEngine;using MiniJSONV;[MonoHeader("分发事件管理器,可以挂在到Button的on Click中使用,也可以直接调用里面的事件分发方法"+"示例\n void OnClick(){\n \ttriggerEventManager.TriggerEventFromNoParam(\"startgame\")\n }\n void OnClick(){\n \ttriggerEventManager.TriggerEventFromJsonParam(\"{\\\"key\\\":\\\"startgame\\\",param:\\\"1\\\"}\")\n }")]public class _TriggerEventManager : MonoBehaviour {private _EventManager _eventManager;void Start(){_eventManager = GetComponent<_EventManager>() as _EventManager;}/// <summary>/// 通过传递json参数,发送带有参数的事件/// 例如:button的onClik方法参数设置:{"key":"sttttt", "param":"d" }/// </summary>public void TriggerEventFromJsonParam(string paramsJson){//判断是否json格式if(GameTools.getInstance().isJson(paramsJson)){Dictionary<string, object> response = (Dictionary<string, object>)Json.Deserialize(paramsJson);if (response.ContainsKey("key")){if(response.ContainsKey("param")){_eventManager.__TriggerEvent((string)response["key"],(string)response["param"]);}else{_eventManager.__TriggerEvent((string)response["key"]);}}else{Debug.Assert(true,"TriigerEventFromJsonParam Func param error: no 'key' inside the json => " + paramsJson);}}else{Debug.Assert(true,"AllButtonEvent Func: TriigerEvent param is not a json string => " + paramsJson);}}/// <summary>/// 无参事件/// </summary>/// <param name="eventKey">Event key.</param>public void TriggerEventFromNoParam(string eventKey){_eventManager.__TriggerEvent(eventKey);}}
- Unity UI事件管理系统设计
- Unity 新UI事件系统(EventSystem) Demo
- Unity背包系统(二)背包UI设计
- Unity UI(十二):UI的事件系统
- Unity游戏UI框架(八):监听事件系统
- Unity新UI系统
- Unity UI系统--总览
- Unity UI 系统--Canvas
- 【Unity】UI系统
- Unity Ui点击事件实现
- 【UI系统】全面理解 Unity UI 系统
- Unity UI层级管理框架
- Unity 5事件系统
- Unity事件系统
- Unity UGUI事件系统
- Unity事件系统
- 浅谈UI设计之"企业工作管理系统"
- 全面理解 Unity UI 系统
- 利用阿里大于(大鱼)平台进行发送手机验证码
- 【Spring】基于IntelliJ IDEA搭建Maven
- CSDN博客导出工具
- [深度好文]想成为一个高效的Web开发者吗?来看看大牛分享的经验吧~外加一些自己的理解
- 中文分词取得重大突破
- Unity UI事件管理系统设计
- Git之忽略文件(ignore file)
- Android 播放声音
- 【poj 1365】 Prime Land 【Central Europe 1997】
- Unity -- AssetBundle简介
- 她只用一个方法,就把英语拿下了!
- 【工作】大三程序猿之(社招)找工作有感
- 脚本超时sql查询条数上亿原因
- 神秘的程序员们头像包第五发(有妹子!)