查看: 792|回复: 0
打印 上一主题 下一主题

[其他] Unity中实用的C#扩展方法

[复制链接]

2317

主题

54

听众

2万

积分

资深设计师

Rank: 7Rank: 7Rank: 7

纳金币
20645
精华
62

最佳新人 活跃会员 热心会员 灌水之王 突出贡献

跳转到指定楼层
楼主
发表于 2015-9-30 00:50:43 |只看该作者 |倒序浏览

      Unity 内置组件基本没有可以继承的。某些比较常用但现有 API 没有定义的功能,我们可以通过C# 的扩展类方法来实现(注意一点,扩展方法对于值类型传递的不是引用,所以无法修改原对象、并且传递体积较大的值类型可能造成性能问题)。下面是一些比较实用的扩展方法,这些扩展方法的实现很多使用了 C# 的委托,关于委托可以参考这里:C# 中的 delegate, Lambda 和 event。

扩展 Transform
每一个 GameObject 都含有一个 Transform 组件(在 UGUI 里新引入的 RectTransform 继承 Transform),GameObject 的层级也是由 Transform 负责的。

/// <summary>
/// (深度优先)遍历 Transform 层级, 对每一个访问的节点执行一个自定义的操作
/// </summary>
/// <param name="root">遍历开始的根部 Transform 对象</param>
/// <param name="operate">遍历到每一个节点时将调用此方法; 参数1: 当前访问的对象; 参数2: 包括本层次在内的剩余深度限制</param>
/// <param name="depthLimit">遍历深度限制, 负值表示不限制, 0 表示只访问 root 本身而不访问其子级, 正值表示最多访问的子级层数</param>
public static void TraverseHierarchy(this Transform root, Action<Transform, int> operate, int depthLimit = -1)
{
    if (operate != null)
    {
        operate(root, depthLimit);
        if (depthLimit == 0) return;

        for (int i = root.childCount - 1; i >= 0; i--)
        {
            TraverseHierarchy(root.GetChild(i), operate, depthLimit - 1);
        }
    }
}


/// <summary>
/// (深度优先)遍历 Transform 层级, 对每一个访问的节点执行一个自定义的操作, 如果此操作返回一个非空引用将终止遍历过程, 并且此返回值最终作为遍历方法的返回值
/// </summary>
/// <param name="root">遍历开始的根部 Transform 对象</param>
/// <param name="operate">遍历到每一个节点时将调用此方法; 参数1: 当前访问的对象; 参数2: 包括本层次在内的剩余深度限制; 返回值: null 表示继续遍历, 非空引用将终止遍历过程, 并且此返回值最终作为遍历方法的返回值</param>
/// <param name="depthLimit">遍历深度限制, 负值表示不限制, 0 表示只访问 root 本身而不访问其子级, 正值表示最多访问的子级层数</param>
/// <returns>第一次 operate 返回的非空引用或 null</returns>
public static object TraverseHierarchy(this Transform root, Func<Transform, int, object> operate, int depthLimit = -1)
{
    if (operate != null)
    {
        object obj = operate(root, depthLimit);
        if (obj != null || depthLimit == 0) return obj;

        for (int i = root.childCount - 1; i >= 0; i--)
        {
            obj = TraverseHierarchy(root.GetChild(i), operate, depthLimit - 1);
            if (obj != null) return obj;
        }
    }
    return null;
}

这两个扩展方法都使用了递归实现,都可以遍历所有的子物体。第一个扩展方法典型使用场景是:更改所有子物体的 Layer;第二个扩展方法带有返回值,随时可以终止遍历,典型的使用场景是:搜索特定的子物体并返回它的引用。

扩展 MonoBehaviour
在 Unity 里使用协程有时可以让程序更简洁。比如使用协程可以方便的延迟执行一些代码。MonoBehaviour 内置的 Invoke 仅支持字符串参数,下面这个扩展可以使用委托。

/// <summary>
/// 延时调用指定的方法
/// </summary>
/// <param name="monoBehaviour">协程附着的脚本对象</param>
/// <param name="seconds">延迟的秒数</param>
/// <param name="method">延时结束调用的方法</param>
public static void Invoke(this MonoBehaviour monoBehaviour, float seconds, Action method)
{
    if (method != null)
    {
        monoBehaviour.StartCoroutine(DelayCall(seconds, method));
    }
}


private static IEnumerator DelayCall(float seconds, Action method)
{
    yield return new WaitForSeconds(seconds);
    method();
}

如果你需要等待 UI 动画完成后再打开输入,以后或许可以用这个了。

扩展 Camera
如果你正在做 2D 游戏,可能需要清楚的了解你的 Sprite 和屏幕的大小关系。下面这两个扩展可以方便的获得像素和世界单位之间互相转换的系数。

/// <summary>
/// 计算正交相机屏幕上,每像素的世界单位(单位:米)大小
/// </summary>
/// <param name="camera">正交相机对象</param>
/// <returns>每像素的世界单位大小</returns>
public static float UnitsPerPixel(this Camera camera)
{
    return camera.orthographicSize * 2 / Screen.height;
}


/// <summary>
/// 计算正交相机屏幕上,每世界单位(单位:米)的像素大小
/// </summary>
/// <param name="camera">正交相机对象</param>
/// <returns>每世界单位的像素大小</returns>
public static float PixelsPerUnit(this Camera camera)
{
    return Screen.height * 0.5f / camera.orthographicSize;
}

不同屏幕的像素大小差别很大,在处理触屏输入时,你可能需要把像素转换成世界大小再判断用户的操作意图。

扩展反射方法
Unity 可定制的编辑器为开发提高了效率,但是在使用上却经常遇到各种问题。比如编辑器类需要访问编辑对象的私有成员。下面这些反射相关方法有时可以用来解决这些问题。

/// <summary>
    /// 反射扩展
    /// </summary>
    public static class ReflectionExtension
    {
        public static object GetPrivateField(this object instance, string fieldName)
        {
            return instance.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic).GetValue(instance);
        }


        public static object GetPrivateProperty(this object instance, string propertyName)
        {
            return instance.GetType().GetProperty(propertyName, BindingFlags.Instance | BindingFlags.NonPublic).GetValue(instance, null);
        }


        public static void SetPrivateField(this object instance, string fieldName, object value)
        {
            instance.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic).SetValue(instance, value);
        }


        public static void SetPrivateProperty(this object instance, string propertyName, object value)
        {
            instance.GetType().GetProperty(propertyName, BindingFlags.Instance | BindingFlags.NonPublic).SetValue(instance, value, null);
        }


        public static object InvokePrivateMethod(this object instance, string methodName, params object[] param)
        {
            return instance.GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.NonPublic).Invoke(instance, param);
        }
    }
分享到: QQ好友和群QQ好友和群 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
转播转播0 分享淘帖0 收藏收藏0 支持支持0 反对反对0
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

手机版|纳金网 ( 闽ICP备2021016425号-2/3

GMT+8, 2025-8-13 19:39 , Processed in 0.064252 second(s), 29 queries .

Powered by Discuz!-创意设计 X2.5

© 2008-2019 Narkii Inc.

回顶部