版本: Unity 6 (6000.0)
语言English
  • C#

SerializedProperty.boxedValue

建议修改

成功!

感谢您帮助我们改进 Unity 文档的质量。虽然我们无法接受所有提交,但我们确实阅读了用户提出的每项修改建议,并在适用的情况下进行更新。

关闭

提交失败

由于某些原因,您的修改建议无法提交。请<a>稍后再试</a>。感谢您抽出时间帮助我们改进 Unity 文档的质量。

关闭

取消

public object boxedValue;

描述

SerializedProperty 的值,以 System.Object 的形式封装。

此属性表示 SerializedProperty 的值,作为一个包装底层类型的 System.Object。

此属性使编写不需要 SerializedProperty 精确类型的代码来获取或设置其值变得更容易。例如,此属性可以使用相同的语法访问任何数值类型、字符串、内置类型(如 Vector3)和托管引用对象。此属性可以消除使用 switch-case 语句或基于 .NET 反射的缓慢替代方案来确定 SerializedProperty 类型的需要。

将值类型包装为基于堆的 System.Object 需要一个称为“装箱”的转换,这会增加性能开销。在性能至关重要的并且您提前知道类型的情况下,请使用相应的特定于类型的访问器,例如 intValuestringValuemanagedReferenceValue,而不是此属性。这消除了此属性所需的装箱性能开销。

当您的应用程序设置此属性时,Unity 会尝试将提供的 System.Object 拆箱并转换为 SerializedProperty 的属性类型。如果失败,Unity 会抛出 InvalidCastException 错误。

boxedValue 对类型为 SerializedPropertyType.Generic 的属性有一些限制。

  • 可以访问按值序列化的结构体和对象,除非它们包含固定缓冲区。
  • 属性不能是数组或列表。但支持访问作为数组或列表元素的属性。
  • 无法访问分类为通用类型的 Unity 内置结构体类型。但具有自身条目(在 SerializedPropertyType 枚举中)的内置类型(如 Vector3)可以使用。

其他资源:propertyType

using UnityEditor;
using UnityEngine;
using System.Collections.Generic;

// To try this example save it as a script called BoxedValueStructExample.cs, // then create an asset file from the Project Window context menu, then inspect it

[System.Serializable] public struct Element { public int m_IntData; public Color m_ColorData; public Rect m_Rect;

public void Change() { ++m_IntData; m_ColorData = new Color(Random.Range(0f, 1f), Random.Range(0f, 1f), Random.Range(0f, 1f), 1); m_Rect = new Rect(Random.Range(0, 100), Random.Range(0, 100), Random.Range(0, 100), Random.Range(0, 100)); } };

[CreateAssetMenu] public class BoxedValueStructExample : ScriptableObject { public Element m_NewItem = new Element(); public List<Element> m_ItemList = new List<Element>(); }

[CustomEditor(typeof(BoxedValueStructExample)), CanEditMultipleObjects] public class BoxedValueStructExampleEditor : Editor { SerializedProperty m_NewItemProp; SerializedProperty m_ListProp;

public void OnEnable() { m_NewItemProp = serializedObject.FindProperty("m_NewItem"); m_ListProp = serializedObject.FindProperty("m_ItemList"); }

public override void OnInspectorGUI() { EditorGUILayout.PropertyField(m_NewItemProp);

GUILayout.Space(30);

if (GUILayout.Button("Add New Item to List")) { // Read full Element struct Element newItem = (Element)m_NewItemProp.boxedValue;

// Append a new item to list and set it to the same values as m_NewItem m_ListProp.arraySize++; m_ListProp.GetArrayElementAtIndex(m_ListProp.arraySize - 1).boxedValue = newItem;

// Update NewItem to some new values newItem.Change(); m_NewItemProp.boxedValue = newItem;

// Because boxedValue is used, the code above does not need to deal with fields inside the struct, // and it would not need to change as fields are added and removed to Element }

GUILayout.Space(30); EditorGUILayout.PropertyField(m_ListProp);

serializedObject.ApplyModifiedProperties(); } }
using System.Text;
using UnityEditor;
using UnityEngine;

public class BoxedValueExample { [MenuItem("Example/Log Property Values")] static void MenuCallback() { var log = new StringBuilder(); var obj = Selection.activeGameObject; { log.AppendLine($"Contents of {obj.name}"); LogProperties(obj, log); }

foreach (var comp in obj.GetComponents<Component>()) { log.AppendLine(); log.AppendLine($"Component {comp.GetType().ToString()}"); LogProperties(comp, log); }

Debug.Log(log.ToString()); }

static void LogProperties(UnityEngine.Object obj, StringBuilder log) { using (var so = new SerializedObject(obj)) { var iterator = so.GetIterator(); iterator.Next(true); // Move past root property

// Printing top level propertise only do { log.Append(iterator.name); log.Append(" type: "); log.Append(iterator.propertyType); LogValue(iterator, log); log.AppendLine(); } while (iterator.Next(false)); } }

static void LogValue(SerializedProperty serializedProperty, StringBuilder log) { // Don't attempt to print these types as strings if (serializedProperty.propertyType == SerializedPropertyType.Generic || serializedProperty.propertyType == SerializedPropertyType.ObjectReference || serializedProperty.propertyType == SerializedPropertyType.ManagedReference) { return; }

log.Append($" value: {serializedProperty.boxedValue}"); }

[MenuItem("Example/Log Property Values", true)] static bool MenuValidation() { return Selection.activeGameObject != null; } }

这是一个递归版本的示例,包含更多信息和格式,但仍然依赖于 boxedValue。

using System.Collections.Generic;
using System.Text;
using UnityEditor;
using UnityEngine;

public class RecursivePropertyLogExample { [MenuItem("Example/Log Property Values (Recursive)")] static void MenuCallback() { var obj = Selection.activeGameObject; { var log = new StringBuilder(); log.AppendLine($"Contents of {obj.name}"); LogProperties(obj, log);

// Log separately to avoid reaching the individual message size limit Debug.Log(log.ToString()); }

foreach (var comp in obj.GetComponents<Component>()) { var log = new StringBuilder(); log.AppendLine($"Component {comp.GetType().ToString()} of {obj.name}"); LogProperties(comp, log); Debug.Log(log.ToString()); } }

static void LogProperties(UnityEngine.Object obj, StringBuilder log) { using (var so = new SerializedObject(obj)) { var iterator = so.GetIterator(); iterator.Next(true); // Move past root property

// Prevent endless loops if SerializeReference instances form cyclical graphs var visitedManagedReferences = new HashSet<long>();

bool visitChild; do { visitChild = false;

if (iterator.propertyType == SerializedPropertyType.Generic) { visitChild = true; } else if (iterator.propertyType == SerializedPropertyType.ManagedReference) { long refId = iterator.managedReferenceId; if (visitedManagedReferences.Add(refId)) visitChild = true; // First time seeing node, so visit it }

for (int i = 0; i < iterator.depth; i++) log.Append(" ");

if (iterator.name == "data") { // If this is an array element then it is more informative to use the name exposed by // propertyPath, e.g. "data[7]" instead of "data". var stringPos = iterator.propertyPath.LastIndexOf('.'); if (stringPos > 0) log.Append(iterator.propertyPath.Substring(stringPos + 1)); else log.Append(iterator.name); } else log.Append(iterator.name);

LogType(iterator, log); LogValue(iterator, log); log.AppendLine();

// Skip past the "Array" child inside each property of type array if (iterator.isArray) iterator.Next(true); } while (iterator.Next(visitChild)); } }

static void LogType(SerializedProperty serializedProperty, StringBuilder log) { log.Append(" type: "); if (serializedProperty.propertyType == SerializedPropertyType.Integer || serializedProperty.propertyType == SerializedPropertyType.Float) log.Append(serializedProperty.numericType); else if (serializedProperty.propertyType == SerializedPropertyType.Generic && serializedProperty.isArray) log.Append("Array"); else log.Append(serializedProperty.propertyType); }

static void LogValue(SerializedProperty serializedProperty, StringBuilder log) { // use boxedValue to get and print the value as a string // There are a few special cases to improve the quality of the output

if (serializedProperty.propertyType == SerializedPropertyType.Generic || serializedProperty.propertyType == SerializedPropertyType.AnimationCurve || serializedProperty.propertyType == SerializedPropertyType.Gradient || serializedProperty.propertyType == SerializedPropertyType.LayerMask) { return; }

if (serializedProperty.propertyType == SerializedPropertyType.ObjectReference) { if (ReferenceEquals(serializedProperty.objectReferenceValue, null)) log.Append($" value: null"); else log.Append($" instanceID: {serializedProperty.objectReferenceValue.GetInstanceID()}"); } else if (serializedProperty.propertyType == SerializedPropertyType.ManagedReference) { if (ReferenceEquals(serializedProperty.managedReferenceValue, null)) log.Append($" value: null"); else log.Append($" refId: {serializedProperty.managedReferenceId} ({serializedProperty.managedReferenceFullTypename})"); } else { log.Append($" value: {serializedProperty.boxedValue.ToString()}"); } }

[MenuItem("Example/Log Property Values (Recursive)", true)] static bool ValidateMenuItem() { return Selection.activeGameObject != null; } }