c# - 如何强制 Unity 的 SerializedProperty 调用 OnValidate 回调?
问题描述
我正在编写一个扩展方法来简化SerializedProperty的使用
这个想法是该方法的用户应该能够设置 a 的值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
解决方案
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();
推荐阅读
- python - tf.train.init_from_checkpoint 不初始化使用 tf.Variable 创建的变量
- c# - iTextSharp 5 - 如何转换 HTML 元素
- video-streaming - 记录RTC | 我们可以在不停止记录的情况下保存 blob 数据吗
- java - 二维数组中的真值表
- function - 重构箭头函数以兼容 IE
- android-studio - Android Studio 3.3.1 中关于 Github 的 Update Project 和 Pull 命令有什么不同?
- webserver - 使用 Esp8266 传递强制门户
- android - Retrofit2:adapter-rxjava 证书问题
- powershell - 根据内容有选择地更新文件中的行
- elasticsearch - 弹性:我想写一个查询(在特定月份的任何时候遇到的每小时峰值量)