仿LOL项目开发第四天
---恢复内容开始---
仿LOL项目开发第四天
by草帽
上节讲了几乎所有的更新版本的逻辑,那么这节课我们来补充界面框架的搭建的讲解。
我们知道游戏中的每个界面都有自己的一个类型:比如登陆界面,创建角色界面。
既然有这么多的界面,所以呢,我们创建一个单例的UI管理器:WindowManager.cs,然后里面创建一个字典来存所有类型的界面:
using UnityEngine; using System.Collections.Generic; using Game; using Game.Common; public class WindowManager : Singleton<WindowManager> { private Dictionary<EWindowType, BaseWindow> mWidowDic; public WindowManager() { mWidowDic = new Dictionary<EWindowType, BaseWindow>(); mWidowDic[EWindowType.e_MessageWindow] = new MessageWindow(); } }
EWindowType枚举类型,定义了所有类型的UI界面,比如登陆类型等。那么,这些枚举属于公共类型,所以我们定义在公共的地方,我们创建一个DefineCommon来存放这些公共类型变量,搞个命名空间为Game.Common:
using UnityEngine; using System.Collections; namespace Game.Common { public enum EWindowType { e_LoginWindow, e_MessageWindow } }
然后WindowManager里面引用该命名空间,注意到没有,因为存放界面的字典value对应着WindowBase,它是UI界面的基类。
什么是基类,就是处理所有不同类型的界面的公共类。也就是说所有界面都有的特性都包含在这个类中。
using UnityEngine; using System.Collections; using Utility; /// <summary> /// 界面抽象基类 /// </summary> public abstract class BaseWindow { protected Transform mRoot;//UI根目录 //protected EScenesType mScenesType; //场景类型 protected string mResName; //资源名 protected bool mResident; //是否常驻 protected bool mVisible = false; //是否可见 //类对象初始化 public abstract void Init(); //类对象释放 public abstract void Realse(); //窗口控制初始化 protected abstract void InitWidget(); //窗口控件释放 protected abstract void RealseWidget(); //游戏事件注册 protected abstract void OnAddListener(); //游戏事件注消 protected abstract void OnRemoveListener(); //显示初始化 public abstract void OnEnable(); //隐藏处理 public abstract void OnDisable(); //每帧更新 public virtual void Update(float deltaTime) { } /*//取得所以场景类型 public EScenesType GetScenseType() { return mScenesType; }*/ //是否已打开 public bool IsVisible() { return mVisible; } //是否常驻 public bool IsResident() { return mResident; } //显示 public void Show() { if (mRoot == null) { if (Create()) { InitWidget();//初始化组件 } } if (mRoot && mRoot.gameObject.activeSelf == false) { mRoot.gameObject.SetActive(true); mVisible = true; OnEnable(); OnAddListener(); } } //隐藏 public void Hide() { if (mRoot && mRoot.gameObject.activeSelf == true) { OnRemoveListener(); OnDisable(); if (mResident) { mRoot.gameObject.SetActive(false); } else { RealseWidget(); Destroy(); } } mVisible = false; } //预加载 public void PreLoad() { if (mRoot == null) { if (Create()) { InitWidget(); } } } //延时删除 public void DelayDestory() { if (mRoot) { RealseWidget(); Destroy(); } } //创建窗体 private bool Create() { if (mRoot) { Debug.LogError("Window Create Error Exist!"); return false; } if (mResName == null || mResName == "") { Debug.LogError("Window Create Error ResName is empty!"); return false; } if (UnityTools.GetUICamera.transform == null) { Debug.LogError("Window Create Error GetUiCamera is empty! WindowName = " + mResName); return false; } GameObject obj = null;// LoadUiResource.LoadRes(GameMethod.GetUiCamera.transform, mResName); if (obj == null) { Debug.LogError("Window Create Error LoadRes WindowName = " + mResName); return false; } mRoot = obj.transform; mRoot.gameObject.SetActive(false);//设置为隐藏 return true; } //销毁窗体 protected void Destroy() { if (mRoot) { // LoadUiResource.DestroyLoad(mRoot.gameObject); mRoot = null; } } //取得根节点 public Transform GetRoot() { return mRoot; } }
里面封装了不同类型界面的公有的方法,比如说创建界面的资源,初始化等。
然后UnityTools里面添加GetUICamera方法:
/// <summary> /// 取得UICamera /// </summary> public static Camera GetUICamera { get { if (UICamera.currentCamera == null) { UICamera.currentCamera = GameObject.Find("UI Root").transform.FindChild("Camera").GetComponent<Camera>(); } return UICamera.currentCamera; } }
这个UI界面的管理方式我就不详细讲了,因为在共享群里面,我已经发了这个框架的研究文章。
因为UI界面是需要从Resources加载界面的Prefab的,所以这里又需要用到资源加载的框架。
我们新建一个单例脚本:ResourceManager.cs:
因为加载场景是需要在Update或者协程里面的,所以呢,ResourceManager他既然是要继承MonoBehavior的单例,所以我这里又搞了一个Mono单例的基类:UnitySingleton.cs:
public class UnitySingleton<T> : MonoBehaviour where T : Component { private static T _instance; public static T Instance { get { if (_instance == null) { _instance = FindObjectOfType(typeof(T)) as T;//如果激活的物体上找到这个脚本 //没有找到这个脚本,就自己创建一个物体,附上这个脚本 if (_instance == null) { GameObject obj = new GameObject(); //obj.hide Flags = HideFlags.DontSave; obj.hideFlags = HideFlags.HideAndDontSave;//设置物体不显示 _instance = (T)obj.AddComponent(typeof(T)); } } return _instance; } } /// <summary> /// 加载另外一个场景的时候不要销毁这个物体 /// </summary> public virtual void Awake() { DontDestroyOnLoad(this.gameObject); if (_instance == null) { _instance = this as T; } else { Destroy(gameObject); } } }
因为我们还没有涉及到ab打包,所以呢,我们这里就直接在Resources里面加载。
ResourceManager:
using UnityEngine; using System.Collections.Generic; using Game; using Game.Common; /// <summary> /// 资源加载管理器 /// </summary> public class ResourceManager : UnitySingleton<ResourceManager> { public bool UsedAssetBundle = false;//是否使用ab加载 private bool m_Init = false; private Dictionary<string, ResourceUnit> m_LoadedResourceUnit = new Dictionary<string,ResourceUnit>(); public void Init() { if (UsedAssetBundle) { } this.m_Init = true; } /// <summary> /// 加载资源 /// </summary> /// <param name="filePath"></param> /// <param name="type"></param> /// <returns></returns> public ResourceUnit LoadImmediate(string filePath,ResourceType type) { if (UsedAssetBundle) { return null; } else { Object asset = Resources.Load(filePath); ResourceUnit resource = new ResourceUnit(null,0,asset,null,type); return resource; } } public void Update() { if (!this.m_Init) { return ; } } }
这里我定义了一个bool变量:UsedAssetbundle,来判断是否是ab加载。这里因为还没有用到ab,所以我暂时先不写ab的代码。
因为资源有很多特性,所以我定义了一个ResourceUnit来管理加载的资源:
using UnityEngine; using System.Collections.Generic; using System; using Object = UnityEngine.Object; using Game.Common; public class ResourceUnit : IDisposable { private string mPath;//资源路径 private Object mAsset;//资源 private ResourceType mResourceType;//资源类型 private List<ResourceUnit> mNextLevelAssets;//用到的所有资源,ab加载时有用到 private AssetBundle mAssetBundle;//资源的ab文件 private int mAssetBundleSize;//ab文件的大小 private int mReferenceCount;//被引用的次数 internal ResourceUnit(AssetBundle assetBundle, int assetBundleSize, Object asset, string path, ResourceType resourceType/*, int allDependencesAssetSize*/) { mPath = path; mAsset = asset; mResourceType = resourceType; mNextLevelAssets = new List<ResourceUnit>(); mAssetBundle = assetBundle; mAssetBundleSize = assetBundleSize; mReferenceCount = 0; } public Object Asset { get { return mAsset; } internal set { mAsset = value; } } public ResourceType resourceType { get { return mResourceType; } } public List<ResourceUnit> NextLevelAssets { get { return mNextLevelAssets; } internal set { foreach (ResourceUnit asset in value) { mNextLevelAssets.Add(asset); } } } public AssetBundle Assetbundle { get { return mAssetBundle; } set { mAssetBundle = value; } } public int AssetBundleSize { get { return mAssetBundleSize; } } public int ReferenceCount { get { return mReferenceCount; } } public void dumpNextLevel() { string info = mPath + " the mReferenceCount : " + mReferenceCount + "\n"; foreach (ResourceUnit ru in mNextLevelAssets) { ru.dumpNextLevel(); info += ru.mPath + "\n"; } Debug.Log(info); } public void addReferenceCount() { ++mReferenceCount; foreach (ResourceUnit asset in mNextLevelAssets) { asset.addReferenceCount(); } } public void reduceReferenceCount() { --mReferenceCount; foreach (ResourceUnit asset in mNextLevelAssets) { asset.reduceReferenceCount(); } if (isCanDestory()) { //ResourcesManager.Instance.mLoadedResourceUnit.Remove(ResourceCommon.getFileName(mPath, true)); Dispose(); } } public bool isCanDestory() { return (0 == mReferenceCount); } public void Dispose() { Debug.Log("Destory " + mPath); if (null != mAssetBundle) { mAssetBundle = null; } mNextLevelAssets.Clear(); mAsset = null; } }
当然ResourceType也是枚举类型,所以定义在DefineCommon类中:
using UnityEngine; using System.Collections; namespace Game.Common { /// <summary> /// UI界面类型 /// </summary> public enum EWindowType { e_LoginWindow, e_MessageWindow } /// <summary> /// 资源类型,Asset,Prefab,Level /// </summary> public enum ResourceType { ASSET, PREFAB, LEVELASSET, LEVEL, } }
OK,现在调用ResourceManager.LoadImmediate就可以加载出资源了。但是我前面说过,单例模式不好扩展,不符合单一职责原则,所以我们自己再封装一层单一职责的类,比如界面加载,我们就定义一个LoadUIResource加载类,然后具体实现又ResourceManage里面实现。
这样也符合开闭原则,对扩展开放,对修改关闭,我们不是没有修改ResourceManager的代码,就能实现UI界面的加载。
OK,废话讲的有点多,我们就来写LoadUIResource.cs:
using UnityEngine; using System.Collections.Generic; using Game.Common; /// <summary> /// UI界面加载类 /// </summary> public class LoadUIResource /// <summary> /// 加载过的缓存字典 /// </summary> public static Dictionary<string, GameObject> m_LoadResDic = new Dictionary<string, GameObject>(); /// <summary> /// 实例化资源 /// </summary> /// <param name="parent"></param> /// <param name="path"></param> /// <returns></returns> public static GameObject LoadRes(Transform parent, string path) { if (CheckResInDic(path)) { GameObject asset = null; m_LoadResDic.TryGetValue(path, out asset); if (asset != null) { return asset; } else { m_LoadResDic.Remove(path); } } GameObject obj = null; ResourceUnit objUnit = ResourceManager.Instance.LoadImmediate(path, ResourceType.PREFAB); if (objUnit == null || objUnit.Asset == null) { Debug.LogError("加载资源失败:" + path); return null; } obj = GameObject.Instantiate(objUnit.Asset) as GameObject; obj.transform.SetParent(parent); obj.transform.localScale = Vector3.one; obj.transform.localPosition = Vector3.zero; m_LoadResDic.Add(path, obj); return obj; } /// <summary> /// 销毁资源 /// </summary> /// <param name="obj"></param> public static void DestroyLoad(GameObject obj) { if (m_LoadResDic.Count == null || obj == null) { return; } foreach (var key in m_LoadResDic.Keys) { GameObject objLoad; if (m_LoadResDic.TryGetValue(key, out objLoad) && obj == objLoad) { GameObject.DestroyImmediate(obj); m_LoadResDic.Remove(key); break; } } } /// <summary> /// 检查是否已经包含该资源 /// </summary> /// <param name="path"></param> /// <returns></returns> private static bool CheckResInDic(string path) { if (m_LoadResDic == null && m_LoadResDic.Count == 0) { return false; } return m_LoadResDic.ContainsKey(path); } }
OK,现在我们就可以加载界面了,所以我们回到WindowBase.Create代码里面:
修改代码:
写完加载界面之后,我们来写写具体的界面实现,MessageWindow这个是我们现在要用到的,所以先写这个:
因为我们消息有很多类型,所以定义一个消息枚举类型:MessageType:
/// <summary> /// 消息类型 /// </summary> public enum EMessageType { EMT_None = -1, EMT_NetTryAgain, //重试消息提示 EMT_ReConnect //重新连接 }
OK,我们现在就开始用NGUI来搭建消息UI界面:
这是我随手搭建的一个,然后制作成Prefab,保存在Resources/Guis文件夹下。那么搭建完之后,我们开始在代码里面定义组件,然后赋值:
可以看到一个消息提示框有:
1.UILabel----->Title:消息标题
2.UILabel----->Conent:消息内容
3.UIButton------>FirstButton第一个按钮
4.UIButton------>SecondButton第二个按钮
所以在MessageWindow里定义:
using UnityEngine; using System.Collections;
using System; using Game.Common; /// <summary> /// 消息UI /// </summary> public class MessageWindow : BaseWindow { private EMessageType m_eMessageType = EMessageType.EMT_None; private UILabel m_title;//消息标题 private UILabel m_content;//消息内容 private UIButton m_firstButton;//消息第一个按钮 private UIButton m_secondButton;//消息第二个按钮
private Action<bool> m_actCallBack;//委托回调 public MessageWindow() { mResName = "Guis/MessageWindow"; mResident = false; } public override void Init() { } protected override void InitWidget() { } protected override void OnAddListener() { } protected override void OnRemoveListener() { } public override void OnEnable() { } public override void Update(float deltaTime) { base.Update(deltaTime); } public override void OnDisable() { } protected override void RealseWidget() { } public override void Realse() { } public void ShowMessage(EMessageType type,Action<bool> callback=null) { //如果已经显示了,就直接返回 if (mVisible) { return; } this.m_eMessageType = type;
this.m_actCallBack = callback; Show(); //根据不同的消息类型,显示不同的提示消息 switch (this.m_eMessageType) { case EMessageType.EMT_NetTryAgain: break; case EMessageType.EMT_ReConnect: break; case EMessageType.EMT_None: break; } } }
然后先初始化各个组件,在InitWidget():
protected override void InitWidget() { this.m_title = this.mRoot.FindChild("Frame/Title").GetComponent<UILabel>(); this.m_content = this.mRoot.FindChild("Frame/Content").GetComponent<UILabel>(); this.m_firstButton = this.mRoot.FindChild("Frame/FirstButton").GetComponent<UIButton>(); this.m_secondButton = this.mRoot.FindChild("Frame/SecondButton").GetComponent<UIButton>(); EventDelegate.Add(this.m_firstButton.onClick, OnFirstBtn); EventDelegate.Add(this.m_secondButton.onClick, OnSecondBtn); }
public void OnFirstBtn() { switch (this.m_eMessageType) { //如果是重试消息的话 case EMessageType.EMT_NetTryAgain: this.m_actCallBack(true); Hide(); break; case EMessageType.EMT_ReConnect: break; case EMessageType.EMT_None: break; } } public void OnSecondBtn() { switch (this.m_eMessageType) { //如果是重试消息的话 case EMessageType.EMT_NetTryAgain: this.m_actCallBack(false); Hide(); break; case EMessageType.EMT_ReConnect: break; case EMessageType.EMT_None: break; } }
我们在NetTryAgain里面修改消息显示的效果:
public void ShowMessage(EMessageType type,Action<bool> callback = null) { //如果已经显示了,就直接返回 if (mVisible) { return; } this.m_eMessageType = type; this.m_actCallBack = callback; Show(); //根据不同的消息类型,显示不同的提示消息 switch (this.m_eMessageType) { //如果是重试消息的话 case EMessageType.EMT_NetTryAgain: this.m_firstButton.normalSprite = "image 168"; this.m_secondButton.normalSprite = "image 172"; this.m_title.text = "网路错误"; this.m_content.text = "您的网络无法连接上服务器,请检查下网络是否良好。"; break; case EMessageType.EMT_ReConnect: break; case EMessageType.EMT_None: break; } }
OK,那么怎么显示消息呢,我们可以看到在ShowMessage方法里面呢,有调用Show()方法。
所以我们想要显示消息,就得调用ShowMessage(),但是基本上呢,我们是在其他地方调用这个方法,所以如果如果直接使用实例,就耦合度非常的高。
所以我们处理了一个事件中心器,专门处理各种事件,比如我想要显示消息的时候,直接调用事件中心器里面对应的显示事件。
这个事件中心器呢,我这里不详细讲解了,你们直接粘贴复制,拿来用就行了。
EventCenter.cs:
/* * Advanced C# messenger by Ilya Suzdalnitski. V1.0 * * Based on Rod Hyde's "CSharpMessenger" and Magnus Wolffelt's "CSharpMessenger Extended". * * Features: * Prevents a MissingReferenceException because of a reference to a destroyed message handler. * Option to log all messages * Extensive error detection, preventing silent bugs * * Usage examples: 1. Messenger.AddListener<GameObject>("prop collected", PropCollected); Messenger.Broadcast<GameObject>("prop collected", prop); 2. Messenger.AddListener<float>("speed changed", SpeedChanged); Messenger.Broadcast<float>("speed changed", 0.5f); * * Messenger cleans up its evenTable automatically upon loading of a new level. * * Don't forget that the messages that should survive the cleanup, should be marked with Messenger.MarkAsPermanent(string) * */ //#define LOG_ALL_MESSAGES //#define LOG_ADD_LISTENER //#define LOG_BROADCAST_MESSAGE #define REQUIRE_LISTENER using System; using System.Collections.Generic; using UnityEngine; using Game.Common; static internal class EventCenter { //Disable the unused variable warning #pragma warning disable 0414 //Ensures that the MessengerHelper will be created automatically upon start of the game. // static private MessengerHelper mMessengerHelper = ( new GameObject("MessengerHelper") ).AddComponent< MessengerHelper >(); #pragma warning restore 0414 static public Dictionary<EGameEvent, Delegate> mEventTable = new Dictionary<EGameEvent, Delegate>(); //Message handlers that should never be removed, regardless of calling Cleanup static public List<EGameEvent> mPermanentMessages = new List<EGameEvent>(); //Marks a certain message as permanent. static public void MarkAsPermanent(EGameEvent eventType) { #if LOG_ALL_MESSAGES Debug.Log("Messenger MarkAsPermanent \t\"" + eventType + "\""); #endif mPermanentMessages.Add(eventType); } static public void Cleanup() { #if LOG_ALL_MESSAGES Debug.Log("MESSENGER Cleanup. Make sure that none of necessary listeners are removed."); #endif List<EGameEvent> messagesToRemove = new List<EGameEvent>(); foreach (KeyValuePair<EGameEvent, Delegate> pair in mEventTable) { bool wasFound = false; foreach (EGameEvent message in mPermanentMessages) { if (pair.Key == message) { wasFound = true; break; } } if (!wasFound) messagesToRemove.Add(pair.Key); } foreach (EGameEvent message in messagesToRemove) { mEventTable.Remove(message); } } static public void PrEGameEventEventTable() { Debug.Log("\t\t\t=== MESSENGER PrEGameEventEventTable ==="); foreach (KeyValuePair<EGameEvent, Delegate> pair in mEventTable) { Debug.Log("\t\t\t" + pair.Key + "\t\t" + pair.Value); } Debug.Log("\n"); } static public void OnListenerAdding(EGameEvent eventType, Delegate listenerBeingAdded) { #if LOG_ALL_MESSAGES || LOG_ADD_LISTENER Debug.Log("MESSENGER OnListenerAdding \t\"" + eventType + "\"\t{" + listenerBeingAdded.Target + " -> " + listenerBeingAdded.Method + "}"); #endif if (!mEventTable.ContainsKey(eventType)) { mEventTable.Add(eventType, null); } Delegate d = mEventTable[eventType]; if (d != null && d.GetType() != listenerBeingAdded.GetType()) { throw new ListenerException(string.Format("Attempting to add listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being added has type {2}", eventType, d.GetType().Name, listenerBeingAdded.GetType().Name)); } } static public void OnListenerRemoving(EGameEvent eventType, Delegate listenerBeingRemoved) { #if LOG_ALL_MESSAGES Debug.Log("MESSENGER OnListenerRemoving \t\"" + eventType + "\"\t{" + listenerBeingRemoved.Target + " -> " + listenerBeingRemoved.Method + "}"); #endif if (mEventTable.ContainsKey(eventType)) { Delegate d = mEventTable[eventType]; if (d == null) { throw new ListenerException(string.Format("Attempting to remove listener with for event type \"{0}\" but current listener is null.", eventType)); } else if (d.GetType() != listenerBeingRemoved.GetType()) { throw new ListenerException(string.Format("Attempting to remove listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being removed has type {2}", eventType, d.GetType().Name, listenerBeingRemoved.GetType().Name)); } } else { throw new ListenerException(string.Format("Attempting to remove listener for type \"{0}\" but Messenger doesn't know about this event type.", eventType)); } } static public void OnListenerRemoved(EGameEvent eventType) { if (mEventTable[eventType] == null) { mEventTable.Remove(eventType); } } static public void OnBroadcasting(EGameEvent eventType) { #if REQUIRE_LISTENER if (!mEventTable.ContainsKey(eventType)) { } #endif } static public BroadcastException CreateBroadcastSignatureException(EGameEvent eventType) { return new BroadcastException(string.Format("Broadcasting message \"{0}\" but listeners have a different signature than the broadcaster.", eventType)); } public class BroadcastException : Exception { public BroadcastException(string msg) : base(msg) { } } public class ListenerException : Exception { public ListenerException(string msg) : base(msg) { } } //No parameters static public void AddListener(EGameEvent eventType, Callback handler) { OnListenerAdding(eventType, handler); mEventTable[eventType] = (Callback)mEventTable[eventType] + handler; } //Single parameter static public void AddListener<T>(EGameEvent eventType, Callback<T> handler) { OnListenerAdding(eventType, handler); mEventTable[eventType] = (Callback<T>)mEventTable[eventType] + handler; } //Two parameters static public void AddListener<T, U>(EGameEvent eventType, Callback<T, U> handler) { OnListenerAdding(eventType, handler); mEventTable[eventType] = (Callback<T, U>)mEventTable[eventType] + handler; } //Three parameters static public void AddListener<T, U, V>(EGameEvent eventType, Callback<T, U, V> handler) { OnListenerAdding(eventType, handler); mEventTable[eventType] = (Callback<T, U, V>)mEventTable[eventType] + handler; } //Four parameters static public void AddListener<T, U, V, X>(EGameEvent eventType, Callback<T, U, V, X> handler) { OnListenerAdding(eventType, handler); mEventTable[eventType] = (Callback<T, U, V, X>)mEventTable[eventType] + handler; } //No parameters static public void RemoveListener(EGameEvent eventType, Callback handler) { OnListenerRemoving(eventType, handler); mEventTable[eventType] = (Callback)mEventTable[eventType] - handler; OnListenerRemoved(eventType); } //Single parameter static public void RemoveListener<T>(EGameEvent eventType, Callback<T> handler) { OnListenerRemoving(eventType, handler); mEventTable[eventType] = (Callback<T>)mEventTable[eventType] - handler; OnListenerRemoved(eventType); } //Two parameters static public void RemoveListener<T, U>(EGameEvent eventType, Callback<T, U> handler) { OnListenerRemoving(eventType, handler); mEventTable[eventType] = (Callback<T, U>)mEventTable[eventType] - handler; OnListenerRemoved(eventType); } //Three parameters static public void RemoveListener<T, U, V>(EGameEvent eventType, Callback<T, U, V> handler) { OnListenerRemoving(eventType, handler); mEventTable[eventType] = (Callback<T, U, V>)mEventTable[eventType] - handler; OnListenerRemoved(eventType); } //Four parameters static public void RemoveListener<T, U, V, X>(EGameEvent eventType, Callback<T, U, V, X> handler) { OnListenerRemoving(eventType, handler); mEventTable[eventType] = (Callback<T, U, V, X>)mEventTable[eventType] - handler; OnListenerRemoved(eventType); } //No parameters static public void Broadcast(EGameEvent eventType) { #if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE Debug.Log("MESSENGER\t" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "\t\t\tInvoking \t\"" + eventType + "\""); #endif OnBroadcasting(eventType); Delegate d; if (mEventTable.TryGetValue(eventType, out d)) { Callback callback = d as Callback; if (callback != null) { callback(); } else { throw CreateBroadcastSignatureException(eventType); } } } static public void SendEvent(CEvent evt) { Broadcast<CEvent>(evt.GetEventId(), evt); } //Single parameter static public void Broadcast<T>(EGameEvent eventType, T arg1) { #if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE Debug.Log("MESSENGER\t" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "\t\t\tInvoking \t\"" + eventType + "\""); #endif OnBroadcasting(eventType); Delegate d; if (mEventTable.TryGetValue(eventType, out d)) { Callback<T> callback = d as Callback<T>; if (callback != null) { callback(arg1); } else { throw CreateBroadcastSignatureException(eventType); } } } //Two parameters static public void Broadcast<T, U>(EGameEvent eventType, T arg1, U arg2) { #if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE Debug.Log("MESSENGER\t" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "\t\t\tInvoking \t\"" + eventType + "\""); #endif OnBroadcasting(eventType); Delegate d; if (mEventTable.TryGetValue(eventType, out d)) { Callback<T, U> callback = d as Callback<T, U>; if (callback != null) { callback(arg1, arg2); } else { throw CreateBroadcastSignatureException(eventType); } } } //Three parameters static public void Broadcast<T, U, V>(EGameEvent eventType, T arg1, U arg2, V arg3) { #if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE Debug.Log("MESSENGER\t" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "\t\t\tInvoking \t\"" + eventType + "\""); #endif OnBroadcasting(eventType); Delegate d; if (mEventTable.TryGetValue(eventType, out d)) { Callback<T, U, V> callback = d as Callback<T, U, V>; if (callback != null) { callback(arg1, arg2, arg3); } else { throw CreateBroadcastSignatureException(eventType); } } } //Four parameters static public void Broadcast<T, U, V, X>(EGameEvent eventType, T arg1, U arg2, V arg3, X arg4) { #if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE Debug.Log("MESSENGER\t" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "\t\t\tInvoking \t\"" + eventType + "\""); #endif OnBroadcasting(eventType); Delegate d; if (mEventTable.TryGetValue(eventType, out d)) { Callback<T, U, V, X> callback = d as Callback<T, U, V, X>; if (callback != null) { callback(arg1, arg2, arg3, arg4); } else { throw CreateBroadcastSignatureException(eventType); } } } } /* //This manager will ensure that the messenger's mEventTable will be cleaned up upon loading of a new level. public sealed class MessengerHelper : MonoBehaviour { void Awake () { DontDestroyOnLoad(gameObject); } //Clean up mEventTable every time a new level loads. public void OnDisable() { Messenger.Cleanup(); } } */
然后,在DefineCommon里面定义委托类型和事件类型:
public delegate void Callback(); public delegate void Callback<T>(T arg1); public delegate void Callback<T, U>(T arg1, U arg2); public delegate void Callback<T, U, V>(T arg1, U arg2, V arg3); public delegate void Callback<T, U, V, X>(T arg1, U arg2, V arg3, X arg4);
/// <summary> /// 事件类型 /// </summary> public enum EGameEvent { eGameEvent_ShowMessage //显示MessageBox }
CEvent.cs事件类:
using UnityEngine; using System.Collections.Generic; using Game.Common; public class CEvent { private EGameEvent eventId; private Dictionary<string, object> paramList; public CEvent() { paramList = new Dictionary<string, object>(); } public CEvent(EGameEvent id) { eventId = id; paramList = new Dictionary<string, object>(); } public EGameEvent GetEventId() { return eventId; } public void AddParam(string name, object value) { paramList[name] = value; } public object GetParam(string name) { if (paramList.ContainsKey(name)) { return paramList[name]; } return null; } public bool HasParam(string name) { if (paramList.ContainsKey(name)) { return true; } return false; } public int GetParamCount() { return paramList.Count; } public Dictionary<string, object> GetParamList() { return paramList; } }
OK,消息中心器处理好了,接着在MessageWindow里面注册事件,在Init()方法里面:
public override void Init() { EventCenter.AddListener<EMessageType,Action<bool>>(EGameEvent.eGameEvent_ShowMessage, ShowMessage); }
那么这个Init()是什么时候调用的,我们回到WindowManager里面,定义一个Init()方法,初始化所有的WindowsUI界面。
public void Init() { foreach (var pWindow in this.mWidowDic.Values) { pWindow.Init(); if (pWindow.IsResident()) { pWindow.PreLoad(); } } }
然后我们知道显示消息界面是在LOLGameDriver脚本里面:
checkTimeout.AsynIsNetworkTimeout((result) => { //网络良好 if (!result) { //开始更新检测 DoInit(); } else //说明网络错误 { //开始消息提示框,重试和退出 EventCenter.Broadcast<EMessageType, Action<bool>>(EGameEvent.eGameEvent_ShowMessage, EMessageType.EMT_NetTryAgain, (isOk) => { if (isOk) { TryInit();//重试 } else { Application.Quit();//退出 } }); } });
然后在Awake里面添加WindowManager.Init():
断开网络,运行程序:
---恢复内容结束---