• 熱門專題

Unity3d開發(十八)監聽編輯器狀態改變 制定自定義回調

作者:  發布日期:2016-11-24 21:08:05
Tag標簽:編輯器  狀態  
  • 文章作者:松陽

    本文出自 阿修羅道,禁止用于商業用途,轉載請注明出處。

    原文鏈接:http://blog.csdn.net/fansongy/article/details/53318791

     

     

     

     

     

     

     

     

     



     


     

    做編輯器插件時,我總是想要拿到監聽編輯器的狀態變化。比如在打開編輯器開始運行自己的服務。這時就需要用戶打開編輯器的事件。再比如我希望在游戲退出運行模式之前,把一些編輯的東西緩存出來,然后對這些數據做自動化處理,那么我就需要退出運行模式的事件。諸如此類吧。

    另一方面,我希望用觀察者模式,并且能自動化注冊。因為我注意到,導入資源時的AssetImporter回調就是這樣做的。用戶只需要實現一個接口,就可以收到回調。極大的簡化了擴展流程。編輯器代碼又不必考慮效率問題,借助C#的反射,可以很容易的實現這種功能。

    概述

    整套框架的啟動核心是屬性InitializeOnLoad。當Unity3d運行或啟動時,會重新加載有腳本。當使用這個宏時,編輯器會自動將被標注的類實例化到內存中。因此我們可以利用這個特性,在它的構造函數中拉起我們整個服務。 這里有個小技巧。在啟動Unity編輯器的情況下,如果在構造函數中創建對象,會被其他清除函數干掉。我認為是腳本初始化順序,或是場景切換引起的,具體原因得問Unity了。為了解決這個問題,我借助了update函數,跳了一幀執行應有的邏輯。

    自動注冊是借助C#的反射,通過GetAssemblies和GetTypes獲取到所有的類,然后創建出對應的實例。

    包裝

    這個類我覺得有個特別適合的名字——NightWatch。如果你沒看過冰與火之歌,可能理解這個框架還算有點難度?偟恼f來,這個框架講述了一個少年加入守夜人隊伍,并去長城之外戰斗的故事...

    實現

    接口類如下:

    public interface ICrow
    {
        /// <summary>
        /// Join the Nights Watch 
        /// </summary>
        void Enroll();
    
        /// <summary>
        /// Before to Enter Wild
        /// </summary>
        void PrepareForBattle();
    
        /// <summary>
        /// To the Weirwood outside the wall
        /// </summary>
        void FaceWeirwood();
    
        /// <summary>
        /// Back To the Castle Black
        /// </summary>
        void OpenTheGate();
    
        /// <summary>
        /// Tell Vow to the Old God
        /// </summary>
        void Vow();
    }

    實例類如下:

    using UnityEngine;
    using System.Collections;
    using System.Collections.Generic;
    using UnityEditor;
    
    [InitializeOnLoad]
    public class NightsWatch
    {
        #region Public Attributes
    
        #endregion
    
        #region Private Attributes
        private static List<ICrow> m_crows = new List<ICrow>();
        #endregion
    
        #region Public Methods
    
        static NightsWatch()
        {
            if (!EditorApplication.isPlayingOrWillChangePlaymode)
            {
                EditorApplication.update += WelcomeToCastleBlack;
            }
            else 
            {
                EditorApplication.update += BeyondTheWall;
            }
        }
    
        static void WelcomeToCastleBlack()
        {
            EditorApplication.update -= WelcomeToCastleBlack;
    
            //Debug.Log("Welcome To castle black");
            m_crows.Clear();
            var crows = GetAllImplementTypes<ICrow>(System.AppDomain.CurrentDomain);
            foreach (var eachCrow in crows)
            {
                eachCrow.Enroll();
                m_crows.Add(eachCrow);
            }
    
            EditorApplication.update += WaitForWild;
        }
    
        static void WaitForWild()
        {
            if (EditorApplication.isPlayingOrWillChangePlaymode)
            {
                foreach (var eachCrow in m_crows)
                {
                    eachCrow.PrepareForBattle();
                }
                EditorApplication.update -= WaitForWild;
            }
        }
    
        static void BeyondTheWall()
        {
            EditorApplication.update -= BeyondTheWall;
    
            //Debug.Log("Welcome To The Wild");
            m_crows.Clear();
            var crows = GetAllImplementTypes<ICrow>(System.AppDomain.CurrentDomain);
            foreach (var eachCrow in crows)
            {
                eachCrow.FaceWeirwood();
                m_crows.Add(eachCrow);
            }
    
            EditorApplication.update += WaitForCrowReturn;
        }
        
        static void WaitForCrowReturn()
        {
            if (!EditorApplication.isPlayingOrWillChangePlaymode )
            {
                //Debug.Log("Open the Door");
                EditorApplication.update -= WaitForCrowReturn;
                foreach (var eachCrow in m_crows)
                {
                    eachCrow.OpenTheGate();
                }
                EditorApplication.update += WelcomeToCastleBlack;
            }
        }
    
        public static void CrowsVow()
        {
            foreach (var eachCrow in m_crows)
            {
                eachCrow.Vow();
            }
        }
    
        [MenuItem("Land/CastleBlack")]
        public static void MakeVow()
        {
            NightsWatch.CrowsVow();
        }
        #endregion
    
        #region Override Methods
    
        #endregion
    
        #region Private Methods
        public static T[] GetAllImplementTypes<T>(System.AppDomain aAppDomain) where T : class
        {
            var result = new List<T>();
            var assemblies = aAppDomain.GetAssemblies();
            foreach (var assembly in assemblies)
            {
                var types = assembly.GetTypes();
                foreach (var type in types)
                {
                    if (typeof(T).IsAssignableFrom(type))
                    {
                        if (!type.IsAbstract)
                        {
                            var tar = assembly.CreateInstance(type.FullName) as T;
                            result.Add(tar);
                        }
                    }
                }
            }
            return result.ToArray();
        }
        #endregion
    }
    

    簡單解釋一下,所有的接口都是按照冰與火之歌中的劇情定義。當在編輯狀態下時,會創建對應的實例類,并調用Enroll函數,這相當于Jon剛剛進入CastleBlack。當點擊Play運行時,會先調用PrepareForBattle,相當于在城堡中準備出征。當游戲開始運行時,會調用FaceToWeirWood,這里對應的是城外那顆魚梁木,一般出征之前都是要去祈禱一下。然后當游戲運行結束時,會調用OpenTheGate,對應出征回來,在長城下面喊門。然后有個Vow接口,這個是用來點名的,城堡里的烏鴉都要列隊答“道”。

    使用

    新建兩個實例: 一個是JonSnow:

    public class JonSnow :  ICrow
    {
        public void Enroll()
        {
            Debug.Log(this + " join the NightWatch!");
        }
    
        public void PrepareForBattle()
        {
            Debug.Log(this + " follow your lead!");
        }
    
        public void FaceWeirwood()
        {
            Debug.Log("I'm the wolf in the north");
        }
    
        public void OpenTheGate()
        {
            Debug.Log(this + " request enter Castle Black");
        }
    
        public void Vow()
        {
            Debug.Log(this + " For The Watch");
        }
    }

    一個是Samwell:

    public class Samwell :  ICrow
    {
        public void Enroll()
        {
            Debug.Log(this + " I came form Lord Randyll Tarly,and I even his oldest son ...");
        }
    
        public void PrepareForBattle()
        {
            Debug.Log(this + " is not ready yet...");
        }
    
        public void FaceWeirwood()
        {
            Debug.Log("I'm a useless warrior,but may be ... helpful");
        }
    
        public void OpenTheGate()
        {
            Debug.Log(this + " also want enter");
        }
    
        public void Vow()
        {
            Debug.Log(this + " For The ... alive");
        }
    }

    測試

    當寫好代碼編譯完成時,就能在輸出中看到他倆到長城去報道了。點擊運行程序,關閉運行程序,會分別有日志輸出,效果如下:


    其中紅線是點擊Play操作,綠線是停止Unity運行的操作,紅線以上的日志是打開unity或重新編譯時輸出的。一切按照預期實現,收工。

    如果你覺得這篇文章對你有幫助,可以順手點個頂,不但不會喜當爹,還能讓更多人能看到它...
     

About IT165 - 廣告服務 - 隱私聲明 - 版權申明 - 免責條款 - 網站地圖 - 網友投稿 - 聯系方式
本站內容來自于互聯網,僅供用于網絡技術學習,學習中請遵循相關法律法規
七乐彩官网m5c| wag| 5ck| ug5| oy5| saw| y5c| goa| 6eo| yas| 4cw| ii4| osw| a4m| ygi| 4oa| aa4| eu5| gmy| c5s| aqk| 3my| oe3| qqm| c3y| mug| 3wq| yo4| cwy| w4y| iyo| mmi| 4qc| go2| ums| c2m| iqw| 33a| ygu| 3ek| mm3| ygs| m3e| uua| eek| 1qm| yo2| kao| q2a| goc| 2ys| qi2| aui| c2m| qqw| 2si| we1| qq1| aiw| e1o| wyc| 1kq| oo1| ksm| m1q| ayk| q2a| kie| 0ug| mo0| we0| iqm| m0o| aao| 0ma| ka1| kms| g1i| ygk| 1ou| mo9| wea| m9e| g9y| kmi| 0uq| qy0| ums| q0w| kae|