天天看點

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

width="150" height="210" frame scrolling="no" src="http://widget.weibo.com/relationship/bulkfollow.php?language=zh_cn&uids=2080045857&wide=1&color=FFFFFF,FFFFFF,0082CB,666666&showtitle=0&showinfo=1&sense=0&verified=1&count=1&refer=http%3A%2F%2Fwww.himigame.com%2Fandroid-game%2F1521.html&dpc=1" style="font-size: 14px; font-weight: bold; border-width: 0px; margin: 0px; padding: 0px; font-family: arial, helvetica, clean, sans-serif; line-height: 16px;">

文章作者:松陽

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

原文連結:http://blog.csdn.net/fansongy/article/details/53318791

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

做編輯器插件時,我總是想要拿到監聽編輯器的狀态變化。比如在打開編輯器開始運作自己的服務。這時就需要使用者打開編輯器的事件。再比如我希望在遊戲退出運作模式之前,把一些編輯的東西緩存出來,然後對這些資料做自動化處理,那麼我就需要退出運作模式的事件。諸如此類吧。

另一方面,我希望用觀察者模式,并且能自動化注冊。因為我注意到,導入資源時的

AssetImporter

回調就是這樣做的。使用者隻需要實作一個接口,就可以收到回調。極大的簡化了擴充流程。編輯器代碼又不必考慮效率問題,借助C#的反射,可以很容易的實作這種功能。

概述

整套架構的啟動核心是屬性

InitializeOnLoad

。當Unity3d運作或啟動時,會重新加載有腳本。當使用這個宏時,編輯器會自動将被标注的類執行個體化到記憶體中。是以我們可以利用這個特性,在它的構造函數中拉起我們整個服務。 這裡有個小技巧。在啟動Unity編輯器的情況下,如果在構造函數中建立對象,會被其他清除函數幹掉。我認為是腳本初始化順序,或是場景切換引起的,具體原因得問Unity了。為了解決這個問題,我借助了

update

函數,跳了一幀執行應有的邏輯。

自動注冊是借助C#的反射,通過

GetAssemblies

GetTypes

擷取到所有的類,然後建立出對應的執行個體。

包裝

這個類我覺得有個特别适合的名字——

NightWatch

。如果你沒看過冰與火之歌,可能了解這個架構還算有點難度。總的說來,這個架構講述了一個少年加入守夜人隊伍,并去長城之外戰鬥的故事...

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

實作

接口類如下:

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:

Unity3d開發(十八) 監聽編輯器狀态改變,制定自定義回調
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: 

Unity3d開發(十八) 監聽編輯器狀态改變,制定自定義回調
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");
    }
}
           

測試

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

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

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

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

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

繼續閱讀