当前位置:   article > 正文

自定义特性,PropertyAttribute 与 PropertyDrawer_propertydrawer与property

propertydrawer与property

PropertyAttribute

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

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

PropertyDrawer

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

属性绘制器有两种用途:

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

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

自定义 Serializable 类的 GUI

  1. using System;
  2. using UnityEngine;
  3. enum IngredientUnit { Spoon, Cup, Bowl, Piece }
  4. // 自定义 Serializable 类
  5. [Serializable]
  6. public class Ingredient
  7. {
  8. public string name;
  9. public int amount = 1;
  10. public IngredientUnit unit;
  11. }
  1. using UnityEngine;
  2. public class Recipe : MonoBehaviour
  3. {
  4. public Ingredient ingredient;
  5. public Ingredient[] ingredients;
  6. }

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

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

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

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

  1. using UnityEditor;
  2. using UnityEngine;
  3. // IngredientDrawer
  4. [CustomPropertyDrawer(typeof(Ingredient))]
  5. public class IngredientDrawer : PropertyDrawer
  6. {
  7. // 在给定的矩形内绘制属性
  8. public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
  9. {
  10. // 在父属性上使用 BeginProperty/EndProperty 意味着
  11. // 预制件重写逻辑作用于整个属性。
  12. EditorGUI.BeginProperty(position, label, property);
  13. //绘制标签
  14. position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
  15. // 不要让子字段缩进
  16. var indent = EditorGUI.indentLevel;
  17. EditorGUI.indentLevel = 0;
  18. // 计算矩形
  19. var amountRect = new Rect(position.x, position.y, 30, position.height);
  20. var unitRect = new Rect(position.x + 35, position.y, 50, position.height);
  21. var nameRect = new Rect(position.x + 90, position.y, position.width - 90, position.height);
  22. // 绘制字段 - 将 GUIContent.none 传入每个字段,从而可以不使用标签来绘制字段
  23. EditorGUI.PropertyField(amountRect, property.FindPropertyRelative("amount"), GUIContent.none);
  24. EditorGUI.PropertyField(unitRect, property.FindPropertyRelative("unit"), GUIContent.none);
  25. EditorGUI.PropertyField(nameRect, property.FindPropertyRelative("name"), GUIContent.none);
  26. // 将缩进恢复原样
  27. EditorGUI.indentLevel = indent;
  28. EditorGUI.EndProperty();
  29. }
  30. }

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

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

  1. using UnityEngine;
  2. using System.Collections;
  3. public class ExampleClass : MonoBehaviour
  4. {
  5. // 在 Inspector 中将此浮点数显示为 0 到 10 之间的滑动条
  6. [Range(0.0F, 10.0F)]
  7. public float myFloat = 0.0F;
  8. }

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

  1. using UnityEngine;
  2. public class RangeAttribute : PropertyAttribute
  3. {
  4. public float min;
  5. public float max;
  6. public RangeAttribute(float min, float max)
  7. {
  8. this.min = min;
  9. this.max = max;
  10. }
  11. }

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

  1. using UnityEngine;
  2. using UnityEditor;
  3. [CustomPropertyDrawer(typeof(RangeAttribute))]
  4. public class RangeDrawer : PropertyDrawer
  5. {
  6. // 在给定的矩形内绘制属性
  7. public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
  8. {
  9. // 首先获取该特性,因为它包含滑动条的范围
  10. RangeAttribute range = attribute as RangeAttribute;
  11. // 现在根据属性是浮点值还是整数来确定将属性绘制为 Slider 还是 IntSlider。
  12. if (property.propertyType == SerializedPropertyType.Float)
  13. EditorGUI.Slider(position, property, range.min, range.max, label);
  14. else if (property.propertyType == SerializedPropertyType.Integer)
  15. EditorGUI.IntSlider(position, property, Convert.ToInt32(range.min), Convert.ToInt32(range.max), label);
  16. else
  17. EditorGUI.LabelField(position, label.text, "Use Range with float or int.");
  18. }
  19. }

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

示例三,自定义特性

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

  1. using UnityEngine;
  2. public class ItemDescriptionAttribute : PropertyAttribute
  3. {
  4. }
  1. using UnityEngine;
  2. using UnityEditor;
  3. [CustomPropertyDrawer(typeof(ItemDescriptionAttribute))]
  4. public class ItemDescriptionDrawer : PropertyDrawer
  5. {
  6. public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
  7. {
  8. return base.GetPropertyHeight(property, label) * 2; // 原来高度的两倍,显示两行
  9. }
  10. public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
  11. {
  12. EditorGUI.BeginProperty(position, label, property);
  13. if(property.propertyType == SerializedPropertyType.Integer)
  14. {
  15. EditorGUI.BeginChangeCheck();
  16. int id = EditorGUI.IntField(new Rect(position.x, position.y, position.width, position.height / 2), label, property.intValue);
  17. EditorGUI.LabelField(new Rect(position.x, position.y + position.height / 2, position.width, position.height / 2), "ItemDescription", GetItemDescription(id));
  18. if (EditorGUI.EndChangeCheck())
  19. {
  20. property.intValue = id;
  21. }
  22. ItemConfig cfg = ConfigMgr.Instance.GetItemConfigById(id);
  23. SetSprite(cfg);
  24. }
  25. EditorGUI.EndProperty();
  26. }
  27. private string GetItemDescription(int id)
  28. {
  29. // 根据id读取对应配置描述
  30. ItemConfig config = ConfigMgr.Instance.GetItemConfigById(id);
  31. if(config != null)
  32. {
  33. return config.name;
  34. }
  35. return "物品描述";
  36. }
  37. private void SetSprite(cfg)
  38. {
  39. // 根据配置,更换对应的sprite图片
  40. // 获取unity当前正选择的gameobject,并获取它的精灵组件
  41. SpriteRenderer spriteRenderer = Selection.activeGameObject.GetComponent<SpriteRenderer>();
  42. //Sprite sprite = AssetDatabase.LoadAssetAtPath<Sprite>("Assets/Art/Textures/kongLong.jpeg");
  43. Sprite sprite = AssetDatabase.LoadAssetAtPath<Sprite>(cfg.sprite);
  44. spriteRenderer.sprite = sprite;
  45. // Debug.Log("Test-" + Selection.activeGameObject.name);
  46. }
  47. }

参考资料:

PropertyDrawer - Unity 脚本 API

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/585618
推荐阅读
  

闽ICP备14008679号