自定义特性,PropertyAttribute 与 PropertyDrawer

PropertyAttribute

自定义特性的基类,可以使用它为脚本创建自定义属性。

PropertyAttribute 与 PropertyDrawer一起使用,可以控制具有该特性的脚本在Inspector中的显示方式。

PropertyDrawer

借助属性绘制器,可以通过使用脚本上的特性或通过控制特定 Serializable 类的外观来自定义 Inspector 窗口中某些控件的外观。

属性绘制器有两种用途:

  • 自定义 Serializable 类的每个实例的 GUI。如果您有自定义的 Serializable 类,可以使用 PropertyDrawer 来控制它在 Inspector 中的外观。
  • 自定义具有自定义 PropertyAttribute 的类成员的 GUI。

属性绘制器类应放在编辑器脚本中,该脚本位于称为 Editor 的文件夹内

自定义 Serializable 类的 GUI

using System;
using UnityEngine;

enum IngredientUnit { Spoon, Cup, Bowl, Piece }

// 自定义 Serializable 类
[Serializable]
public class Ingredient
{
    public string name;
    public int amount = 1;
    public IngredientUnit unit;
}
using UnityEngine;

public class Recipe : MonoBehaviour
{
    public Ingredient ingredient;
    public Ingredient[] ingredients;
}

可以使用自定义属性绘制器来更改 Inspector 中 Ingredient 类的每个外观。比较不带有和带有自定义属性绘制器的 Inspector 中 Ingredient 属性的外观:

不带有(左)和带有(右)自定义属性绘制器的 Inspector 中的类。

不带有(左)和带有(右)自定义属性绘制器的 Inspector 中的类。

可以使用 CustomPropertyDrawer 属性将属性绘制器附加到 Serializable 类,然后传入属性绘制器所针对的 Serializable 类的类型。

using UnityEditor;
using UnityEngine;

// IngredientDrawer
[CustomPropertyDrawer(typeof(Ingredient))]
public class IngredientDrawer : PropertyDrawer
{
    // 在给定的矩形内绘制属性
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        // 在父属性上使用 BeginProperty/EndProperty 意味着
        // 预制件重写逻辑作用于整个属性。
        EditorGUI.BeginProperty(position, label, property);

        //绘制标签
        position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);

        // 不要让子字段缩进
        var indent = EditorGUI.indentLevel;
        EditorGUI.indentLevel = 0;

        // 计算矩形
        var amountRect = new Rect(position.x, position.y, 30, position.height);
        var unitRect = new Rect(position.x + 35, position.y, 50, position.height);
        var nameRect = new Rect(position.x + 90, position.y, position.width - 90, position.height);

        // 绘制字段 - 将 GUIContent.none 传入每个字段,从而可以不使用标签来绘制字段
        EditorGUI.PropertyField(amountRect, property.FindPropertyRelative("amount"), GUIContent.none);
        EditorGUI.PropertyField(unitRect, property.FindPropertyRelative("unit"), GUIContent.none);
        EditorGUI.PropertyField(nameRect, property.FindPropertyRelative("name"), GUIContent.none);

        // 将缩进恢复原样
        EditorGUI.indentLevel = indent;

        EditorGUI.EndProperty();
    }
}

使用属性特性来自定义脚本成员的 GUI

PropertyDrawer 的另一用途是改变脚本中具有自定义 PropertyAttribute 的成员的外观。 假如您要将脚本中的浮点数或整数限制在特定范围内,并在 Inspector 中将其显示为滑动条。 那么,您可以使用内置的 PropertyAttribute(名为 RangeAttribute)来执行此操作:

using UnityEngine;
using System.Collections;

public class ExampleClass : MonoBehaviour
{
    // 在 Inspector 中将此浮点数显示为 0 到 10 之间的滑动条
    [Range(0.0F, 10.0F)]
    public float myFloat = 0.0F;
}

您还可以创建自己的 PropertyAttribute。我们将以 RangeAttribute 的代码为例。 该特性必须扩展 PropertyAttribute 类。如果需要,属性可以使用参数并将它们存储为公共成员变量。

using UnityEngine;

public class RangeAttribute : PropertyAttribute
{
    public float min;
    public float max;

    public RangeAttribute(float min, float max)
    {
        this.min = min;
        this.max = max;
    }
}

拥有该特性后,需要创建一个 PropertyDrawer 来绘制具有该特性的属性。 绘制器必须扩展 PropertyDrawer 类,且必须具有 CustomPropertyDrawer 特性来说明绘制器所对应的特性。

using UnityEngine;
using UnityEditor;

[CustomPropertyDrawer(typeof(RangeAttribute))]
public class RangeDrawer : PropertyDrawer
{
    // 在给定的矩形内绘制属性
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        // 首先获取该特性,因为它包含滑动条的范围
        RangeAttribute range = attribute as RangeAttribute;

        // 现在根据属性是浮点值还是整数来确定将属性绘制为 Slider 还是 IntSlider。
        if (property.propertyType == SerializedPropertyType.Float)
            EditorGUI.Slider(position, property, range.min, range.max, label);
        else if (property.propertyType == SerializedPropertyType.Integer)
            EditorGUI.IntSlider(position, property, Convert.ToInt32(range.min), Convert.ToInt32(range.max), label);
        else
            EditorGUI.LabelField(position, label.text, "Use Range with float or int.");
    }
}

请注意,出于性能原因,EditorGUILayout 函数不能用于属性绘制器。

示例三,自定义特性

在Inspector面板修改物品id,会自动生成对应的物品描述;根据配置,更换对应的sprite图片。

using UnityEngine;

public class ItemDescriptionAttribute : PropertyAttribute
{
    
}
using UnityEngine;
using UnityEditor;

[CustomPropertyDrawer(typeof(ItemDescriptionAttribute))]
public class ItemDescriptionDrawer : PropertyDrawer
{
    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        return base.GetPropertyHeight(property, label) * 2; // 原来高度的两倍,显示两行
    }

    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        EditorGUI.BeginProperty(position, label, property);

        if(property.propertyType == SerializedPropertyType.Integer)
        {
            EditorGUI.BeginChangeCheck();

            int id = EditorGUI.IntField(new Rect(position.x, position.y, position.width, position.height / 2), label, property.intValue);

            EditorGUI.LabelField(new Rect(position.x, position.y + position.height / 2, position.width, position.height / 2), "ItemDescription", GetItemDescription(id));

            if (EditorGUI.EndChangeCheck())
            {
                property.intValue = id;
            }

            ItemConfig cfg = ConfigMgr.Instance.GetItemConfigById(id);
            SetSprite(cfg);
        }

        EditorGUI.EndProperty();
    }

    private string GetItemDescription(int id)
    {
        // 根据id读取对应配置描述
        ItemConfig config = ConfigMgr.Instance.GetItemConfigById(id);
        if(config != null)
        {
            return config.name;
        }
        return "物品描述";
    }

    private void SetSprite(cfg)
    {
        // 根据配置,更换对应的sprite图片
        // 获取unity当前正选择的gameobject,并获取它的精灵组件
        SpriteRenderer spriteRenderer = Selection.activeGameObject.GetComponent<SpriteRenderer>();
        //Sprite sprite = AssetDatabase.LoadAssetAtPath<Sprite>("Assets/Art/Textures/kongLong.jpeg");
        Sprite sprite = AssetDatabase.LoadAssetAtPath<Sprite>(cfg.sprite);
        spriteRenderer.sprite = sprite;
        // Debug.Log("Test-" + Selection.activeGameObject.name);
    }
}

参考资料:

PropertyDrawer - Unity 脚本 API

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值