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

断开网络,运行程序:

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

时间: 2024-10-25 20:22:58

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

仿LOL项目开发第三天

仿LOL项目开发第二天 by草帽 昨个我们已经实现了下载功能,但是发现没有,下载的包是压缩的,没有解压开,那么Unity是识别不了的. 所以今个我们来讲讲如何实现解压文件. 还记得吗,我们在DownloadTask里面添加了一个完成下载后的一个解压委托,我们还没有实现,那么,我们就去解决他. 回到VersionManager的DownloadPackageList方法里面,在OnDownloadFinished委托里面,添加解压缩的代码. 之前讲过类的单一职责,所以不可能在VersionMana

仿LOL项目开发第二天

仿LOL项目开发第二天 by草帽 接着上节来讲,上节更新还没开始写代码逻辑,今天我们补充完整. 我们找到VersionManager脚本里面的CheckVersion方法: 首先我们想到检测版本,需要从服务器下载信息,那么肯定要提前检测下网络是否良好,并比较版本信息. 所以,我们写个BeforeCheck方法: /// <summary> /// 检测网络状况并对照版本信息是否一致 /// </summary> /// <param name="AsynResult

仿LOL项目开发第六天

仿LOL项目开发第六天 by草帽 OK,因为更新模块已经处理好了,接着开始登陆的编写.那么我们就需要状态机的管理. 所谓状态机就是在哪个状态执行那个状态的代码逻辑: 那么我们开始编写GameStateManager来管理: 我们先在DefineCommon里面定义游戏状态类型: /// <summary> /// 游戏状态 /// </summary> public enum GameStateType { GS_Continue,//中间状态,也就是说介于两个状态转换的中间状态

团队项目开发第四天

1.昨天干了什么? 终于更新完了SdK Manager,重新开始做任务了. 2.今天准备干什么? 希望今天能顺利的做出我们的首页页面,包括logo和导航栏的四个分块,fighting! 3.遇到什么困难?

四:DRF项目开发的准备

一: 虚拟环境virtualenv 如果在一台电脑上, 想开发多个不同的项目, 需要用到同一个包的不同版本, 如果使用上面的命令, 在同一个目录下安装或者更新, 新版本会覆盖以前的版本, 其它的项目就无法运行了.而公司里面往往会存在多个项目的情况,所以需要使用虚拟环境,把当前项目所需要用到的模块及其版本进行隔离包装到一个虚拟环境中使用. 1 安装 pip install virtualenv pip install virtualenv-clone pip install virtualenvw

[Openwrt 项目开发笔记]:Samba服务&amp;vsFTP服务(四)

在上一节中,我们讲述了如何在路由器上挂载U盘,以及如何通过改造U盘提升路由器的存储和数据处理能力.通过增加USB挂载,为我们后续进行智能家居构建提供了保障. 在本节中,我将简单介绍如何在路由器上建立网络文件共享服务. 一.为何要建立文件共享服务: 在智能家居网络中,一个很重要的需求就是安全.当一个智能家居方案部署之后,用户往往最先考虑的也是安全问题.那么怎样进行安全管理呢?一种常用的解决方法是实时的监控传感器以及家电的状态,一旦发生异常,通过邮件.短信甚至打电话的方式通知用户.当用户接收到异常之

iOS项目开发实战——学会使用TableView列表控件(四)plist读取与Section显示

文本将会实现把数据存储到plist文件里.然后在程序中进行读取.在TableView控件中依据不同的类别显示Section. 有关TableView 的其它实现,请參考<iOS项目开发实战--学会使用TableView列表控件(一)><iOS项目开发实战--学会使用TableView列表控件(二)><iOS项目开发实战--学会使用TableView列表控件(三)了解Section>. (1)新建一个Property List文件,这个也就是plist文件.我取名为dat

【小程序源码案例】微信小程序项目开发案例分享

作者:web小二本文标签: 微信小程序 小程序源码案例 小程序项目小程序的开发,并不是适合所有公司,我今天跟大家分享小程序方面的教程,主要是供大家学习使用.学习这种东西,有时候则是单纯的喜欢,没有任何目的,很单纯的为了好玩,记得很早之前学flash,没有想法,就是觉得好玩,纯娱乐爱好而已.到后来玩视频剪辑也是出于同样的原因,不图钱财名利,只是图自己个人爱好娱乐. 但是,学习,有时候则是需要有明确目的,特别是关系到自己吃饭问题的时候,你就需要非常有目的去学习,并且还需要制定好学习的计划与目标,希望

Hive项目开发环境搭建(Eclipse\MyEclipse + Maven)

写在前面的话 可详细参考,一定得去看 HBase 开发环境搭建(Eclipse\MyEclipse + Maven) Zookeeper项目开发环境搭建(Eclipse\MyEclipse + Maven) 我这里,相信,能看此博客的朋友,想必是有一定基础的了.我前期写了大量的基础性博文.可以去补下基础. 步骤一:File  ->  New  -> Project   ->  Maven Project 步骤二:自行设置,待会创建的myHBase工程,放在哪个目录下. 步骤三: 步骤四: