如何在 CustomPropertyDrawer 中设置 SerializedProperty.propertyType

How to set SerializedProperty.propertyType in a CustomPropertyDrawer

我正在使用这两个库:

基本上,可序列化字典会查看 属性 类型以确定 属性 是否可以扩展,检查如下:

static bool CanPropertyBeExpanded(SerializedProperty property)
{
    switch(property.propertyType)
    {
    case SerializedPropertyType.Generic:
    case SerializedPropertyType.Vector4:
    case SerializedPropertyType.Quaternion:
        return true;
    default:
        return false;
    }
}

但是,场景参考似乎已注册为可展开的 属性,但实际上并非如此。这是因为 - 显然 - Unity 将其注册为 Generic.

类型

我可以简单地通过将 SerializedProperty.propertyType 设置为更有意义的类型来解决这个问题,但它是只读的。

那么,如何设置自定义 属性 抽屉的 SerializedProperty.propertyType

想象一下这样的 class

public class Test
{
   public string stringProperty;
}

现在,尝试设置 属性Type 基本上就像试图告诉编译器更改 class 上的 属性 类型,这是不可能的,因为它已经写好了下降为 'string'.

你能做的是

static bool CanPropertyBeExpanded(SerializedProperty property)
{
   float height = EditorGUI.GetPropertyHeight(property);
   // Property expandable if its height is twice the single line height.
   return height >= EditorGUIUtility.singleLineHeight * 2;
}

它没有记录,但类型 Generic 会自动分配给任何自定义 class(例如 SceneReference)。你不能改变propertyType因为它是只读 ...你不能告诉编译器处理你的自定义 class 作为其他东西...

即使如果你也可以……什么是"more meaningful type"? SerializedPropertyType 的可用类型有限,其中 none 对于自定义 class.

更有意义

这里的主要"issue"是:

SerializableDictionary 抽屉简单地假设通常如果你有一个自定义的 (Generic) class 而没有一个自定义的 PropertyDrawer - 所以使用默认的抽屉 - 它的行为与 QuaternionVector4:

的默认抽屉完全一样

  • 第一行有标签和折页
  • fields/content are/is 仅在 属性 折出时绘制 are/is

由于 SceneReference 的抽屉未实现此行为,因此它被绘制在字典的关键字段之上。


因此,作为最简单的修复,您当然可以简单地删除

case SerializedPropertyType.Generic:

因此 SceneAsset(以及 所有其他自定义 classes)被视为正常展开的字段 - 品味问题


或者您可以做的是更改 SceneReferencePropertyDrawer 以反映例如Quaternion:

  • 添加带有更改 property.isExpaned 值的标签的 EditorGUI.Foldout
  • 将任何内容移到下方一行(可选)
  • 在 属性 高度和 if(!property.isExpanded)
  • 的条件中添加一行

可能看起来像喜欢:

// Made these two const btw
private const float PAD_SIZE = 2f;
private const float FOOTER_HEIGHT = 10f;

public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
    // Move this up
    EditorGUI.BeginProperty(position, GUIContent.none, property);
    {
        // Here we add the foldout using a single line height, the label and change
        // the value of property.isExpanded
        property.isExpanded = EditorGUI.Foldout(new Rect(position.x, position.y, position.width, lineHeight), property.isExpanded, label);

        // Now you want to draw the content only if you unfold this property
        if (property.isExpanded)
        {
            // Optional: Indent the content
            //EditorGUI.indentLevel++;
            //{

            // reduce the height by one line and move the content one line below
            position.height -= lineHeight;
            position.y += lineHeight;

            var sceneAssetProperty = GetSceneAssetProperty(property);

            // Draw the Box Background
            position.height -= FOOTER_HEIGHT;
            GUI.Box(EditorGUI.IndentedRect(position), GUIContent.none, EditorStyles.helpBox);
            position = boxPadding.Remove(position);
            position.height = lineHeight;

            // Draw the main Object field
            label.tooltip = "The actual Scene Asset reference.\nOn serialize this is also stored as the asset's path.";


            var sceneControlID = GUIUtility.GetControlID(FocusType.Passive);
            EditorGUI.BeginChangeCheck();
            {
                // removed the label here since we already have it in the foldout before
                sceneAssetProperty.objectReferenceValue = EditorGUI.ObjectField(position, sceneAssetProperty.objectReferenceValue, typeof(SceneAsset), false);
            }
            var buildScene = BuildUtils.GetBuildScene(sceneAssetProperty.objectReferenceValue);
            if (EditorGUI.EndChangeCheck())
            {
                // If no valid scene asset was selected, reset the stored path accordingly
                if (buildScene.scene == null) GetScenePathProperty(property).stringValue = string.Empty;
            }

            position.y += paddedLine;

            if (!buildScene.assetGUID.Empty())
            {
                // Draw the Build Settings Info of the selected Scene
                DrawSceneInfoGUI(position, buildScene, sceneControlID + 1);
            }

            // Optional: If enabled before reset the indentlevel
            //}
            //EditorGUI.indentLevel--;
        }
    }
    EditorGUI.EndProperty();
}

public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
    var sceneAssetProperty = GetSceneAssetProperty(property);
    // Add an additional line and check if property.isExpanded
    var lines = property.isExpanded ? sceneAssetProperty.objectReferenceValue != null ? 3 : 2 : 1;
    // If this oneliner is confusing you - it does the same as
    //var line = 3; // Fully expanded and with info
    //if(sceneAssetProperty.objectReferenceValue == null) line = 2;
    //if(!property.isExpanded) line = 1;

    return boxPadding.vertical + lineHeight * lines + PAD_SIZE * (lines - 1) + FOOTER_HEIGHT;
}

现在看起来像

[Serializable]
public class TestDict : SerializableDictionary<string, SceneReference> { }

public class Example : MonoBehaviour
{
    public SceneReference NormalReference;

    public TestDict DictExample = new TestDict();
}