如何在 CustomPropertyDrawer 中设置 SerializedProperty.propertyType
Posted
技术标签:
【中文标题】如何在 CustomPropertyDrawer 中设置 SerializedProperty.propertyType【英文标题】:How to set SerializedProperty.propertyType in a CustomPropertyDrawer 【发布时间】:2020-01-16 18:27:59 【问题描述】:我正在使用这两个库:
Unity-SerializableDictionary: https://github.com/starikcetin/Unity-SerializableDictionary
Unity 场景参考: https://github.com/starikcetin/unity-scene-reference
基本上,可序列化字典查看 propertyType 以确定该属性是否可以扩展,并进行以下检查:
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
?
【问题讨论】:
【参考方案1】:没有记录,但类型 Generic
会自动分配给任何自定义类(例如 SceneReference
)。您不能更改propertyType
,因为它是只读 ...并且您不能告诉编译器将您的自定义类作为其他东西来处理...
即使如果你可以......什么是“更有意义的类型”? SerializedPropertyType
的可用类型是有限的,没有一个对自定义类更有意义。
这里的主要“问题”是:
SerializableDictionary
抽屉通常假设如果您有一个没有自定义 PropertyDrawer
的自定义 (Generic
) 类 - 所以使用默认抽屉 - 它的行为与 Quaternion
或 @987654336 的默认抽屉完全相同@:
由于SceneReference
的抽屉没有实现此行为,它被绘制在字典的关键字段之上。
所以作为最简单的修复当然你可以简单地删除
case SerializedPropertyType.Generic:
所以SceneAsset
(和所有其他自定义类)被视为正常展开的字段 - 口味问题
或者,您可以做的是更改 PropertyDrawer
的 SceneReference
以反映例如的行为。 Quaternion
:
EditorGUI.Foldout
,其标签会更改property.isExpaned
值
将任何内容移到下一行(并且可以选择)
在属性高度和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();
【讨论】:
嘿,感谢您的努力。您想将此作为 PR 发送到 GitHub 上吗?我希望你的名字能作为贡献者出现在上面。 @S.TarıkÇetin 不客气!将其添加到 git。我在 SerializedDictionary 中也有一个错误顺便说一句:当我有冲突的键名(由于两个都是空字符串)并在上下文菜单中点击Reset
时,我得到了很多异常并且它是不可逆转的(除了注释掉损坏的字典并重新编译)
谢谢!【参考方案2】:
想象一下这样的课程
public class Test
public string stringProperty;
现在,尝试设置 propertyType 基本上就像试图告诉编译器更改类的属性类型,这是不可能的,因为它已经写成“字符串”。
你能做的是
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;
【讨论】:
因为这个SceneReference
属性基本上有两行,这不会改变检查器中的行为。以上是关于如何在 CustomPropertyDrawer 中设置 SerializedProperty.propertyType的主要内容,如果未能解决你的问题,请参考以下文章