PropertyDrawer

Unity自定义属性绘制器
本文介绍如何在Unity中使用自定义属性绘制器来改善Inspector界面的显示效果,特别是针对自定义类Ingredient的属性展示进行了详细说明。文章通过具体代码示例展示了如何创建并应用自定义的IngredientDrawer来优化Inspector面板上的数据呈现。


Ingredient.cs

<pre class="csharp" name="code">using UnityEngine;
using System.Collections;
using System.Collections.Generic;
[System.Serializable]
public enum IngredientUnit { Spoon, Cup, Bowl, Piece }

// Custom serializable class
[System.Serializable]
public class Ingredient 
{
    public string name;
    public int amount = 1;
    public IngredientUnit unit;
    
    //public int list;
    public List<int> list = new List<int>();
}

 

IngredientDrawer.cs

// C# example.
using UnityEngine;
using UnityEditor;
using System.Collections;

[CustomPropertyDrawer(typeof(Ingredient))]
public class IngredientDrawer : PropertyDrawer
{

    // Draw the property inside the given rect
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        // Using BeginProperty / EndProperty on the parent property means that
        // prefab override logic works on the entire property.
        EditorGUI.BeginProperty(position, label, property);

        // Draw label
        position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);

        // Don't make child fields be indented
        int indent = EditorGUI.indentLevel;
        EditorGUI.indentLevel = 0;

        // Calculate rects
        Rect amountRect = new Rect(position.x, position.y, 30, position.height);
        Rect unitRect = new Rect(position.x + 35, position.y, 50, position.height);
        Rect nameRect = new Rect(position.x + 90, position.y, position.width - 90, position.height);

        // Draw fields - passs GUIContent.none to each so they are drawn without labels
        EditorGUI.PropertyField(amountRect, property.FindPropertyRelative("amount"), GUIContent.none);
        EditorGUI.PropertyField(unitRect, property.FindPropertyRelative("unit"), GUIContent.none);
        EditorGUI.PropertyField(nameRect, property.FindPropertyRelative("name"), GUIContent.none);


        Debug.Assert(property.FindPropertyRelative("list") != null, "list");

        //Debug.Assert(property.FindPropertyRelative("table") != null, "table");


        // Set indent back to what it was
        EditorGUI.indentLevel = indent;

        EditorGUI.EndProperty();
    }
}

Recipe.cs

using UnityEngine;
using System.Collections;
public class Recipe : MonoBehaviour
{
    public Ingredient potionResult;
    public Ingredient[] potionIngredients;
      
}


注意:

Note that for performance reasons, EditorGUILayout functions are not usable with Property Drawers.


扩展:

IngredientDrawer.cs

using UnityEditor;
using System.Collections;

[CustomPropertyDrawer(typeof(Ingredient))]
public class IngredientDrawer : PropertyDrawer
{
    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        int propertCount = property.CountInProperty();
        return propertCount * 40;
    }

    // Draw the property inside the given rect
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        Debug.Log(position);
        
        label = EditorGUI.BeginProperty(position, label, property);

        Rect contentPosition = EditorGUI.PrefixLabel(position, label);
        contentPosition.height = 20;
        EditorGUI.PropertyField(contentPosition, property.FindPropertyRelative("amount"), GUIContent.none);
        contentPosition.y += 40f;
        EditorGUI.PropertyField(contentPosition, property.FindPropertyRelative("unit"), GUIContent.none);

        EditorGUI.EndProperty();

    }
}


参考网址:

http://catlikecoding.com/unity/tutorials/editor/custom-data/




<think>我们正在讨论Unity中显示自定义类数组字段的方法。根据用户提供的引用和问题,用户希望能在Unity编辑器的Inspector窗口中显示自定义类数组中的字段。引用[1]提到,自定义的结构体或类默认不会显示出来,除非加上[Serializable]序列化标记。用户已经为结构体People添加了[Serializable]属性,这样它就可以在Inspector中显示。引用[3]展示了如何通过自定义属性(Attribute)来改变Inspector中字段的显示名称。用户创建了一个CustomLabelAttribute特性,用于指定字段在Inspector中显示的自定义名称。引用[4]提到了使用CustomPropertyDrawer来自定义显示属性。当需要更复杂的显示方式时,可以创建自定义的PropertyDrawer。用户的问题是:如何在Unity编译器中查看或显示自定义类数组中的字段?解决方案:1.确保自定义类或结构体标记为[Serializable]。2.如果需要在数组(或List)中显示,则将该数组或List声明为public(或[SerializeField]private)。3.如果需要自定义显示字段的名称,可以使用类似引用[3]中的CustomLabelAttribute,然后为这个Attribute编写相应的PropertyDrawer(参考引用[4])。4.如果需要更复杂的布局,可以编写自定义的PropertyDrawer。具体步骤:步骤1:序列化自定义类例如,有一个自定义类:```csharp[Serializable]publicclassMyCustomClass{publicintnumber;publicstringtext;}```步骤2:在MonoBehaviour中使用这个类的数组```csharppublicclassMyBehaviour:MonoBehaviour{publicMyCustomClass[]myArray;}```这样,在Inspector中就会显示一个数组,每个元素都可以展开并显示其中的字段。但是,如果用户希望修改字段在Inspector中的显示名称,或者对字段的显示进行更复杂的控制,就需要使用自定义Attribute和PropertyDrawer。例如,使用CustomLabelAttribute(如引用[3]):首先,定义CustomLabelAttribute:```csharpusingUnityEngine;publicclassCustomLabelAttribute:PropertyAttribute{publicstringname;publicCustomLabelAttribute(stringname){this.name=name;}}```然后,为这个Attribute编写PropertyDrawer(需要放在Editor文件夹下):```csharpusingUnityEditor;usingUnityEngine;[CustomPropertyDrawer(typeof(CustomLabelAttribute))]publicclassCustomLabelDrawer:PropertyDrawer{publicoverridevoidOnGUI(Rectposition,SerializedPropertyproperty,GUIContentlabel){//获取我们自定义的attributeCustomLabelAttributecustomLabel=attributeasCustomLabelAttribute;//使用自定义的名称作为标签EditorGUI.PropertyField(position,property,newGUIContent(customLabel.name),true);}}```在自定义类中使用这个Attribute:```csharp[Serializable]publicclassMyCustomClass{[CustomLabel("数字")]publicintnumber;[CustomLabel("文本")]publicstringtext;}```这样,在Inspector中,字段number将显示为“数字”,字段text将显示为“文本”。另外,对于数组中的每个元素,Unity默认会显示为“Element0”、“Element1”等,如果想自定义数组元素的显示,可以使用自定义PropertyDrawer绘制整个数组,但这样比较复杂。通常,我们通过自定义数组元素的类(即MyCustomClass)的显示方式,来达到目的。如果用户希望自定义数组元素的显示方式(例如,将每个元素的字段水平排列),可以为MyCustomClass编写一个自定义的PropertyDrawer。例如,为MyCustomClass编写PropertyDrawer(同样放在Editor文件夹):```csharp[CustomPropertyDrawer(typeof(MyCustomClass))]publicclassMyCustomClassDrawer:PropertyDrawer{publicoverridevoidOnGUI(Rectposition,SerializedPropertyproperty,GUIContentlabel){//使用EditorGUI.PropertyField绘制默认的,或者自定义布局//例如,将两个字段水平排列Rectrect1=newRect(position.x,position.y,position.width/2,position.height);Rectrect2=newRect(position.x+position.width/2,position.y,position.width/2,position.height);//分别绘制两个属性EditorGUI.PropertyField(rect1,property.FindPropertyRelative("number"));EditorGUI.PropertyField(rect2,property.FindPropertyRelative("text"));}}```这样,在数组中每个元素就会以自定义的方式绘制。注意:如果数组元素是自定义类,并且我们为该类编写了PropertyDrawer,那么数组中的每个元素都会按照这个PropertyDrawer绘制。总结:1.序列化自定义类和数组。2.使用Attribute和PropertyDrawer自定义字段的标签和显示方式。3.对于数组中的自定义类,可以通过为该类编写PropertyDrawer来自定义每个元素的显示。用户可以根据需要选择合适的方法。相关问题:1.Unity中如何序列化自定义类?2.如何为UnityInspector中的字段自定义显示名称?3.如何为自定义类编写PropertyDrawer?4.如何自定义数组在Inspector中的显示?5.如何让私有字段在Inspector中显示?</think>### 在Unity编辑器中显示自定义类数组字段的方法 要在Unity编辑器的Inspector窗口中显示自定义类数组字段,需遵循以下步骤: #### 1. **序列化自定义类和数组** 确保自定义类标记 `[Serializable]`,数组字段需声明为 `public` 或使用 `[SerializeField]`: ```csharp [System.Serializable] // 关键序列化标记 public class People { public string Name; public int Age; public string Sex; } public class Example : MonoBehaviour { public People[] peopleArray; // 公有数组自动显示 // 或 [SerializeField] private People[] privateArray; // 私有数组需SerializeField } ``` #### 2. **自定义字段显示名称(可选)** 使用 `CustomLabelAttribute` 修改Inspector中的字段名[^3]: ```csharp // 定义特性 public class CustomLabelAttribute : PropertyAttribute { public string name; public CustomLabelAttribute(string name) => this.name = name; } // 应用特性 [System.Serializable] public class People { [CustomLabel("姓名")] public string Name; [CustomLabel("年龄")] public int Age; [CustomLabel("性别")] public string Sex; } ``` #### 3. **创建PropertyDrawer(高级定制)** 如需深度定制显示样式,需在 **Editor文件夹** 下创建 `PropertyDrawer`[^4]: ```csharp using UnityEditor; using UnityEngine; // 为CustomLabel特性创建绘制器 [CustomPropertyDrawer(typeof(CustomLabelAttribute))] public class CustomLabelDrawer : PropertyDrawer { public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { var attr = (CustomLabelAttribute)attribute; EditorGUI.PropertyField(position, property, new GUIContent(attr.name), true); } } // 为People类创建整体绘制器(可选) [CustomPropertyDrawer(typeof(People))] public class PeopleDrawer : PropertyDrawer { public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { EditorGUI.PropertyField(position, property, label, true); } } ``` #### 4. **使用效果** 完成上述步骤后,自定义数组在Inspector中将显示为: - 可折叠的数组列表 - 每个元素可展开显示详细字段 - 字段名称按定制显示(如使用CustomLabel) ![Inspector显示效果](https://docs.unity3d.com/uploads/Main/Inspector-ArrayExample.png) > ⚠️ 注意事项: > 1. 所有自定义类必须标记 `[Serializable]` > 2. PropertyDrawer脚本必须放在 **Editor文件夹** 内 > 3. 修改脚本后需重新编译才能生效 > 4. 数组元素修改需在Play模式外进行(序列化限制) --- ### 常见问题解决方案 | 问题现象 | 解决方法 | |---------|----------| | 数组不显示 | 检查类是否标记 `[Serializable]`,字段是否为public或`[SerializeField]` | | 字段名未改变 | 确保PropertyDrawer在Editor文件夹,且特性名称匹配 | | 嵌套类不显示 | 在内嵌类上也添加 `[Serializable]` 标记 | | 修改不生效 | 关闭脚本后重新编译,或重启Unity | 通过以上方法,您可以在Unity编辑器中完整显示和控制自定义类数组的所有字段。如需进一步定制UI布局,可扩展 `PropertyDrawer` 的 `OnGUI` 方法实现更复杂的绘制逻辑[^2][^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值