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");
    }
}



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