• 仿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 + "
    ";
            foreach (ResourceUnit ru in mNextLevelAssets)
            {
                ru.dumpNextLevel();
                info += ru.mPath + "
    ";
            }
            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 	"" + 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("			=== MESSENGER PrEGameEventEventTable ===");
    
            foreach (KeyValuePair<EGameEvent, Delegate> pair in mEventTable)
            {
                Debug.Log("			" + pair.Key + "		" + pair.Value);
            }
    
            Debug.Log("
    ");
        }
    
        static public void OnListenerAdding(EGameEvent eventType, Delegate listenerBeingAdded)
        {
    #if LOG_ALL_MESSAGES || LOG_ADD_LISTENER
    		Debug.Log("MESSENGER OnListenerAdding 	"" + eventType + ""	{" + 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 	"" + eventType + ""	{" + 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	" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "			Invoking 	"" + 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	" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "			Invoking 	"" + 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	" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "			Invoking 	"" + 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	" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "			Invoking 	"" + 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	" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "			Invoking 	"" + 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项目开发第五天地址链接

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

  • 相关阅读:
    李宏毅机器学习课程---1、机器学习介绍
    尚学python课程---15、python进阶语法
    尚学python课程---14、python中级语法
    尚学python课程---13、python基础语法
    Android4.2.2由于越来越多的物理按键(frameworks)
    ym——Android之ListView性能优化
    我学cocos2d-x (两) 采用Delegate(信托)
    mac提升yosemite后php 扩展修复
    JAVA学习课第五 — IO流程(九)文件分割器合成器
    第11周项目-2.2
  • 原文地址:https://www.cnblogs.com/CaomaoUnity3d/p/5471177.html
Copyright © 2020-2023  润新知