Unity 從Apex學到的一種提高模塊擴展性的方法

Apex有一種很好的擴展機制,當你不附加自定義腳本的時候,按照默認的Apex邏輯走,比如Steering算法。但是當你新建一個MonoBehavior腳本,並且實現了IMoveUnit方法之後。把這個腳本貼到原本的Unit的GameObecjt上的時候,就可以自動識別並執行擴展的Steering算法。


研究了一下之後,發現這個過程是這樣的。

定義接口,在基本行爲中實現這個接口,然後在OnEnable或者Start裏面檢測是否有實現了這個接口的擴展MoveBehavior的Compoent,如果有,執行擴展行爲,如果沒有,執行基本行爲。


如圖:基本行爲

擴展行爲


實現方式,先定義一個查找Interface組件的擴展方法:ToInterfaxe<T>

public static class GameObjectUtil
{
    public static void DestroyAllChild(this Component c) 
    {
        Transform root = c.transform;
            
        if (root.childCount > 0)
        {
            //從後往前刪除,效率高。
            for (int i = root.childCount - 1; i >= 0; i--)
            {
                if (Application.isPlaying)
                {
                    GameObject.Destroy(root.GetChild(i).gameObject);
                }
                else
                {
                    GameObject.DestroyImmediate(root.GetChild(i).gameObject);
                }
            }
        }
    }

    /// <summary>
    /// 將當前Component轉換成指定接口,如果當前GameOjbect上有繼承了接口的組件,則返回真,否則,返回null。
    /// 可以實現這樣的效果:
    /// 如果查找到某個腳本(因爲他實現了某個接口),則執行某個操作,否則,執行默認行爲。並且這種實現是針對接口的,非常靈活
    /// 比較耗,不要在Update中調用,比較適合在初始化的時候調用一次。
    /// 參考來源:Apex->SharedUnityExtension
    /// 
    /// 用法 this.ToInterface<IMessageHandler> 如果有某個腳本繼承了這個接口,那麼就會返回,否則,返回NULL。
    /// 這樣就可以做到,只有當附加了繼承handler的腳本,纔會發送對應的消息協議,否則,不發送,或者走默認邏輯。
    /// 這樣做也可以非常方便讓用戶自己擴展某些邏輯,比如Apex自己寫了一個基礎的Steering,實現了IMoveUnit接口
    /// 默認邏輯的時候,返回的 IMoveUnit接口爲空,這時候使用默認的Steering Move邏輯
    /// 當你想要實現自己的Steering方法時,直接填寫實現了IMoveUnit的腳本,附加到GameObeject上即可。灰常靈活。新技能Get
    public static T ToInterface<T>(this Component c, bool searchParent = false, bool required = false) where T : class
    {
        if (c.Equals(null))
        {
            return null;
        }

        return ToInterface<T>(c.gameObject, searchParent, required);
    }

    public static T ToInterface<T>(this GameObject go, bool searchParent = false, bool required = false) where T : class
    {
        if (go.Equals(null))
        {
            return null;
        }

        var c = go.GetComponent(typeof(T)) as T;

        if (c == null && searchParent && go.transform.parent != null)
        {
            return ToInterface<T>(go.transform.parent.gameObject, false, required);
        }

        if (c == null && required)
        {
            throw new MissingComponentException(string.Format("Game object {0} does not have a component of type {1}.", go.name, typeof(T).Name));
        }

        return c;
    }


}

基本方法:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using GameUtil;

public interface iBehave 
{
    void Execute();
}

public class DefaultBehave:iBehave 
{

    public void Execute()
    {
        Debug.Log("Default Behavior");
    }

}

public class BasicBehav : MonoBehaviour 
{
    iBehave execut_behave;
    void OnEnable() 
    {
        execut_behave = this.ToInterface<iBehave>();
        if (execut_behave == null)
        {
            execut_behave = new DefaultBehave();
        }
    }

    void Update() 
    {
        execut_behave.Execute();
    }

}


擴展腳本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ExtentBehav : MonoBehaviour,iBehave 
{
    public void Execute()
    {
        Debug.Log("Extent Behav");
    }
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章