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

仿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. UAF漏洞学习

    产生原因: UAF漏洞的成因是一块堆内存被释放了之后又被使用.又被使用指的是:指针存在(悬垂指针被引用).这个引用的结果是不可预测的,因为不知道会发生什么.由于大多数的堆内存其实都是C++对象,所以利 ...

  2. CVE-2010-3971 CSS内存破坏漏洞分析

    看了仙果版主的议题演讲,其中提到cve-2010-3971是一个浏览器漏洞利用中的里程碑.于是找来POC,尝试分析一下. 1.漏洞重现 XP SP3+ie6.0环境 poc如下: poc.htm &l ...

  3. Asp.net vNext 学习之路(一)

    概述 asp.net vNext 也叫 asp.net 5.0,意思是微软推出的下一个版本的asp.net.可以说是微软对asp.net的一个比较重大的重新设计, asp.net vNext是一 个比 ...

  4. Cookie机制和Session机制

    1. cookie 1. Cookie 是在HTTP协议下,服务器或脚本可以维护客户工作站上信息的一种方式.Cookie 是由 Web服务器保存在用户浏览器(客户端)上的小文本文件(内容通常经过加密) ...

  5. 【LOJ】#2349. 「JOI 2017/2018 决赛」团子制作

    题解 有意思的一个dp,我们对G计数,发现如果不在同一条对角线上的G肯定不会互相影响,所以我们对于每一条对角线dp dp的方式是枚举这个G以什么方式放,横着还是竖着,还是不放 代码 #include ...

  6. XML和HTML的区别

    1.很多新手程序员总是会问HTML和XML有什么区别,接下来就解释一下: 什么是XML? XML 指可扩展标记语言(EXtensible Markup Language). XML 是一种很像HTML ...

  7. 湖南大学ACM程序设计新生杯大赛(同步赛)G - The heap of socks

    题目描述 BSD is a lazy boy. He doesn't want to wash his socks, but he will have a data structure called ...

  8. Java反射机制demo(一)—实例化Class对象,并获得其他类包名和类型

    Java反射机制demo(一)——实例化Class对象,通过对象获得包名和类型 使用Java中的 本地类作为实验对象,避免自定义的类带来的一些不便. public static void main(S ...

  9. Unity 2D游戏开发教程之使用脚本实现游戏逻辑

    Unity 2D游戏开发教程之使用脚本实现游戏逻辑 使用脚本实现游戏逻辑 通过上一节的操作,我们不仅创建了精灵的动画,还设置了动画的过渡条件,最终使得精灵得以按照我们的意愿,进入我们所指定的动画状态. ...

  10. c++源文件后缀名问题

    VC里用cpp作后缀名, 在GCC里默认采用C.cc.cxx作为后缀名 .cpp, .h (VS file).cc, .h (GCC file)   C中: 头文件后缀名: .h 源文件后缀名: .c ...