如何在 CustomPropertyDrawer 中设置 SerializedProperty.propertyType
How to set SerializedProperty.propertyType in a CustomPropertyDrawer
我正在使用这两个库:
Unity-SerializableDictionary:
https://github.com/starikcetin/Unity-SerializableDictionary
Unity场景参考:
https://github.com/starikcetin/unity-scene-reference
基本上,可序列化字典会查看 属性 类型以确定 属性 是否可以扩展,检查如下:
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
- 所以使用默认的抽屉 - 它的行为与 Quaternion
或 Vector4
:
的默认抽屉完全一样
- 第一行有标签和折页
- fields/content are/is 仅在 属性 折出时绘制 are/is
由于 SceneReference
的抽屉未实现此行为,因此它被绘制在字典的关键字段之上。
因此,作为最简单的修复,您当然可以简单地删除
case SerializedPropertyType.Generic:
因此 SceneAsset
(以及 所有其他自定义 classes)被视为正常展开的字段 - 品味问题
或者您可以做的是更改 SceneReference
的 PropertyDrawer
以反映例如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();
}
我正在使用这两个库:
Unity-SerializableDictionary: https://github.com/starikcetin/Unity-SerializableDictionary
Unity场景参考: https://github.com/starikcetin/unity-scene-reference
基本上,可序列化字典会查看 属性 类型以确定 属性 是否可以扩展,检查如下:
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
- 所以使用默认的抽屉 - 它的行为与 Quaternion
或 Vector4
:
- 第一行有标签和折页
- fields/content are/is 仅在 属性 折出时绘制 are/is
由于 SceneReference
的抽屉未实现此行为,因此它被绘制在字典的关键字段之上。
因此,作为最简单的修复,您当然可以简单地删除
case SerializedPropertyType.Generic:
因此 SceneAsset
(以及 所有其他自定义 classes)被视为正常展开的字段 - 品味问题
或者您可以做的是更改 SceneReference
的 PropertyDrawer
以反映例如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();
}