---恢复内容开始---

仿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():

断开网络,运行程序:

 仿LOL项目开发第五天地址链接

---恢复内容结束---

仿LOL项目开发第四天的更多相关文章

  1. 仿LOL项目开发第三天

    仿LOL项目开发第二天 by草帽 昨个我们已经实现了下载功能,但是发现没有,下载的包是压缩的,没有解压开,那么Unity是识别不了的. 所以今个我们来讲讲如何实现解压文件. 还记得吗,我们在Downl ...

  2. 仿LOL项目开发第九天

    仿LOL项目开发第九天 by 草帽 OK,今天我们完全换了一种风格,抛弃了Unity3d的c#语法,我们来写写java的项目. 说到java服务器,当然有些人可能鄙视java的服务器速度太慢,但是相对 ...

  3. 仿LOL项目开发第八天

    仿LOL项目开发第八天 by 草帽 这节我们继续上节所讲的内容,上节我们初始化好了LoginWindow,当我们点击确认选择服务器按钮的时候,就发送服务器id给游戏服务器. 这里就开始涉及到客户端需要 ...

  4. 仿LOL项目开发第七天

    仿LOL项目开发第七天 by 草帽 不知不觉已经写到了第七篇这种类型的博客,但是回过头看看之前写的,发现都只能我自己能看懂. 我相信在看的童鞋云里雾里的,因为我基本上没怎么详细讲一个脚本怎么用?但是你 ...

  5. 仿LOL项目开发第六天

    仿LOL项目开发第六天 by草帽 OK,因为更新模块已经处理好了,接着开始登陆的编写.那么我们就需要状态机的管理. 所谓状态机就是在哪个状态执行那个状态的代码逻辑: 那么我们开始编写GameState ...

  6. 仿LOL项目开发第五天

    仿LOL项目开发第五天 by草帽 今天呢,我们看下能开发什么内容,首先上节我们已经讲了UI框架的搭建,上节还遗留下很多问题,比如说消息的字符是代码里面自己赋值的. 那么就比较死板,按照正常的逻辑,那些 ...

  7. 仿LOL项目开发第二天

    仿LOL项目开发第二天 by草帽 接着上节来讲,上节更新还没开始写代码逻辑,今天我们补充完整. 我们找到VersionManager脚本里面的CheckVersion方法: 首先我们想到检测版本,需要 ...

  8. 仿LOL项目开发第一天

    ---恢复内容开始--- 仿LOL项目开发第一天 by---草帽 项目源码研究群:539117825 最近看了一个类似LOL的源码,颇有心得,所以今天呢,我们就来自己开发一个类似于LOL的游戏demo ...

  9. 基于Vue的WebApp项目开发(四)

    实现新闻咨询页面 目录结构 步骤一:创建newslist.vue文件 <template> <div id="tml"> <!--使用mui框架,实现 ...

随机推荐

  1. LeetCode解题报告—— Best Time to Buy and Sell Stock

    Best Time to Buy and Sell Stock Say you have an array for which the ith element is the price of a gi ...

  2. hdu 5692(dfs序+线段树,好题)

    Snacks Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Sub ...

  3. hdu 1849(巴什博弈)

    Rabbit and Grass Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...

  4. Valid Parentheses——栈经典

    Given a string containing just the characters '(', ')', '{', '}', '[' and ']', determine if the inpu ...

  5. IEEEXtreme 10.0 - Game of Stones

    这是 meelo 原创的 IEEEXtreme极限编程大赛题解 Xtreme 10.0 - Game of Stones 题目来源 第10届IEEE极限编程大赛 https://www.hackerr ...

  6. 面试题12:打印1到最大的n位数(大数问题)

    面试题是关于n位整数并且没有限定n的取值范围,或者是输入任意大小的整数,那么这个题目就很可能需要考虑大数问题.字符串是一个简单的.有效的表示大数的方法 这题比较难,用递归表达全排列,数字每一位都可能是 ...

  7. spring boot 扩展之AutoConfigurationImportListener

    最近阅读spring boot源码时发现,发现当spring使用ConfigurationClassParser加载使用@Configuration注解类后,会使用AutoConfigurationI ...

  8. poj1321 棋盘问题(DFS)

    题目链接 http://poj.org/problem?id=1321 题意 给定一块棋盘(棋盘可能是不规则的),有k个相同棋子,将k个棋子摆放在棋盘上,使得任意两个棋子不同行,不同列,求有多少种不同 ...

  9. 接口自动化多层嵌套的json数据处理

    最近在做接口自动化测试,响应的内容大多数是多层嵌套的json数据,在对响应数据进行校验的时候,可以通过(key1.key2.key3)形式获取嵌套字典值的方法获取响应值,再和预期值比较 def get ...

  10. Disruptor Java版和.NET版的区别

    The main differences comes from the fact that .NET supports structs (value types), Java doesn't. In ...