我正在编写一种扩展方法来简化SerializedProperty
的使用这个想法是该方法的用户应该能够设置SerializedProperty
的值而不必担心类型。如下例所示,应根据serializedProperty
和myValue
的类型来处理正确的类型。
常规使用SerializedProperty
(Unity方式)的示例:
serializedProperty.intValue = myIntValue;
serializedProperty.floatValue = myFloatValue;
serializedProperty.boolValue = myBoolValue;
...
我的SerializedProperty
方法扩展的预期语法
serializedProperty.SetValue(myValue);
此SerializedProperty
方法扩展的当前实现
public static void SetValue<TValue>(this SerializedProperty property, TValue value)
{
if (property.hasMultipleDifferentValues)
throw new ArgumentOutOfRangeException();
Type parentType = property.serializedObject.targetObject.GetType();
FieldInfo fieldInfo = parentType.GetField(property.propertyPath);
fieldInfo.SetValue(property.serializedObject.targetObject, value);
}
问题:
此实现不调用OnValidate
Unity回调。经常使用(mySerializedProperty.intValue = myInt
)。
我的问题:有什么方法可以强制Unity在OnValidate
上调用SerializedProperty
方法?
我曾考虑过自己通过反射来调用OnValidate
,但是由于这已经在Unity中实现,所以我想知道是否有办法将SerializedProperty
标记为已更改或类似内容。
为测试此行为,我编写了以下测试(NUnit):
private IntMonoBehaviourMock _mockInstance;
[SetUp]
public void CallBeforeEachTest()
{
GameObject gameObject = new GameObject();
this._mockInstance = gameObject.AddComponent<IntMonoBehaviourMock>();
}
[Test]
// This test fails for the current implementation
public void SetValue_ToDifferentValue_OnValidateCalledOnce()
{
this.SetValueOfSUT(0x1CE1CE);
int numCallsBefore = this._mockInstance.NumTimesValidateCalled;
this.SetValueOfSUT(0xBABE);
int numCallsAfter = this._mockInstance.NumTimesValidateCalled;
int actualNumCalls = numCallsAfter - numCallsBefore;
Assert.AreEqual(1, actualNumCalls); // Fails
}
private void SetValueOfSUT(int value)
{
string fieldName = "publicSerializedField"
SerializedObject serializedObject = new SerializedObject(this._mockInstance);
SerializedProperty sut = this._serializedObject.FindProperty(fieldName);
// This is the call to function being tested!
// Swapping this for sut.intValue = value, makes the test pass.
// (But the purpose is to write a function that handles any type correctly)
sut.SetValue(value);
this._serializedObject.ApplyModifiedProperties();
}
我在测试中使用的 IntMonoBehaviourMock 的实现是:
public class IntMonoBehaviourMock: MonoBehaviour
{
[SerializeField]
public int publicSerializedField = default;
public int NumTimesValidateCalled { get; private set; }
protected void OnValidate()
{
this.NumTimesValidateCalled++;
}
}
测试结果为:
Expected: 1
But was: 0
答案 0 :(得分:1)
您当然也可以在没有反思的情况下致电OnValidate
public void OnValidate() ...
,然后在编辑器调用中
theProperty.SetValue(5);
((IntMonoBehaviourMock)target).OnValidate();
或者我有时要做的是使Editor成为相应类型的子类,因此您也可以访问private
和protected
方法。我通常会使用
public partial class IntMonoBehaviourMock
{
...
#if UnityEditor
private partial class IntMonoBehaviourMockEditor { }
#endif
}
,然后在单独的文件中
#if UnityEditor
public partial class IntMonoBehaviourMock
{
[CustomEditor(typeof(IntMonoBehaviourMock)]
private partial class IntMonoBehaviourMockEditor : Editor
{
...
}
}
#endif
可能的类型非常有限
AnimationCurve, BoundsInt, bool, Bounds, Color, double, float, int, long,
Quaternion, RectInt, Rect, string, Vector2, Vector2Int, Vector3, Vector3Int, Vector4
和一个特殊的
UnityEngine.Object
(几乎)所有Unity类的父类。
我可以考虑使用类似的方法
var type = typeof(TValue);
if(type == typeof(int))
{
serializedProperty.intValue = value;
}
else if(type == typeof(string)
{
serializedProperty.stringValue = value;
}
...
else if(type.IsAssignableFrom(typeof(UnityEngine.Object)))
{
serializedProperty.objectReferenceValue = value;
}
else
{
Debug.LogError("Unassignable type: " + type.FullName);
}
实际上,您甚至不需要通用的,也可以简单地使用
var type = value.GetType();