Unity 从Apex学到的一种提高模块扩展性的方法

来源:互联网 发布:原生js实现todo list 编辑:程序博客网 时间:2024/05/29 19:20

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