简体   繁体   English

如何强制 Unity 的 SerializedProperty 调用 OnValidate 回调?

[英]How to force Unity's SerializedProperty to call OnValidate callback?

I am writing a extension method to simplify the use of SerializedProperty我正在编写一个扩展方法来简化SerializedProperty的使用

The idea is that the user of the method should be able to set the value of a SerializedProperty without having to worry about the type.这个想法是,该方法的用户应该能够设置SerializedProperty的值,而不必担心类型。 As illustrated in the example below, the correct type should be handled according the the type of serializedProperty and myValue .如下例所示,正确的类型应该根据serializedPropertymyValue的类型来处理。

Example of the regular use of SerializedProperty , the Unity way:常规使用SerializedProperty的示例,Unity 方式:

serializedProperty.intValue = myIntValue;
serializedProperty.floatValue = myFloatValue;
serializedProperty.boolValue = myBoolValue;
...

Intended syntax for my SerializedProperty method extension我的SerializedProperty方法扩展的预期语法

serializedProperty.SetValue(myValue);

My current implementation of this SerializedProperty method extension我当前对此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);
}

The Problem:问题:

This implementation does not call the OnValidate Unity callback.此实现不调用OnValidate Unity 回调。 The regular usage ( mySerializedProperty.intValue = myInt ) does.常规用法( mySerializedProperty.intValue = myInt )确实如此。

MY QUESTION : Is there any way to force Unity to call the OnValidate method on the SerializedProperty ?我的问题:有什么方法可以强制 Unity 在SerializedProperty上调用OnValidate方法?

I have considered calling OnValidate by myself via reflection but since this is already implemented in Unity I was wondering if there was a way to mark the SerializedProperty as changed or something similar.我曾考虑通过反射自己调用OnValidate ,但由于这已经在 Unity 中实现,我想知道是否有办法将SerializedProperty标记为已更改或类似的东西。


For testing this behavior, I have written the following test (NUnit):为了测试这种行为,我编写了以下测试(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();
}

The implementation of IntMonoBehaviourMock that I am using in the test is:我在测试中使用的IntMonoBehaviourMock的实现是:

public class IntMonoBehaviourMock: MonoBehaviour
{
    [SerializeField]
    public int publicSerializedField = default;

    public int NumTimesValidateCalled { get; private set; }

    protected void OnValidate()
    {
        this.NumTimesValidateCalled++;
    }
}

The result of the test is:测试结果是:

Expected: 1
  But was:  0

You could ofcourse call OnValidate also without Reflection by making it你当然可以调用OnValidate也没有反射

public void OnValidate() ...

and then in the Editor call然后在编辑器调用中

theProperty.SetValue(5);
((IntMonoBehaviourMock)target).OnValidate();

or what I sometimes do is make the Editor a subclass of the according type so you have access to private and protected methods as well.或者我有时会做的是让 Editor 成为相应类型的子类,这样你也可以访问privateprotected的方法。 I usually male it still in different files using我通常男性仍然使用不同的文件

public partial class IntMonoBehaviourMock
{
    ...

#if UnityEditor
    private partial class IntMonoBehaviourMockEditor { }
#endif
}

and then in a separate file然后在一个单独的文件中

#if UnityEditor
public partial class IntMonoBehaviourMock
{
    [CustomEditor(typeof(IntMonoBehaviourMock)]
    private partial class IntMonoBehaviourMockEditor : Editor
    {
        ...
    }
}
#endif

The possible types are quite limited可能的类型非常有限

AnimationCurve, BoundsInt, bool, Bounds, Color, double, float, int, long, 
Quaternion, RectInt, Rect, string, Vector2, Vector2Int, Vector3, Vector3Int, Vector4

and a special one和一个特别的

UnityEngine.Object

which is the parent class for (almost) all Unity classes.这是(几乎)所有 Unity 类的父 class。

I can think of alternatively using something like我可以考虑使用类似的东西

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);
}

actually you would then not even require the generic but could also simply use实际上你甚至不需要泛型,但也可以简单地使用

var type = value.GetType();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Unity:将空白元素添加到 SerializedProperty 的数组中 - Unity: Adding a blank element to an array of SerializedProperty's 如何判断 SerializedProperty 是否是 Unity 中数组的一部分? - How to tell if a SerializedProperty is part of an array in Unity? Unity Editor Scripting:如何以编程方式更新 CustomEditor SerializedProperty? - Unity Editor Scripting: How to update a CustomEditor SerializedProperty programmatically? 如何追踪调用“OnValidate()”的来源 - How do I trace the source of the call “OnValidate()” 如何为LINQ to SQL的OnValidate方法提供默认实现? - How to provide a default implementation for LINQ to SQL's OnValidate method? SerializedProperty在Unity3D PropertyDrawers中始终为null - SerializedProperty always null with Unity3D PropertyDrawers 如何在CustomPropertyDrawer中设置SerializedProperty.propertyType - How to set SerializedProperty.propertyType in a CustomPropertyDrawer 为什么我不能在 Unity3D 中覆盖“OnValidate()”? - Why can't I override “OnValidate()” in Unity3D? Unity - 使用自定义检查器时是否可以访问OnValidate()? - Unity - Is it possible to access OnValidate() when using a custom inspector? Unity中的回调监听器 - 如何从Android中的UnityPlayerActivity调用脚本文件方法 - Callback Listener in Unity - How to call script file method from UnityPlayerActivity in Android
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM