---恢复内容开始---
仿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():
断开网络,运行程序:
---恢复内容结束---