unity3d unity -如何在序列化字段更改时更新对象?

kyxcudwk  于 2023-04-12  发布在  其他
关注(0)|答案(4)|浏览(267)

我相信这个问题已经在那里了,但我找不到它,对不起。
我正在尝试将对象的Serialized字段与其其他组件同步。
假设我有一个字段“大小”,它应该会影响对象变换比例:

[SerializeField]
int _size;

我正在寻找一个事件处理程序或允许我做的事情:

void onSerializedPropertyChange() {
    transform.localScale = new Vector3(_size,_size,_size);
}

这样的方法存在吗?
最后的想法是使用多个属性,并在预览结果时调整对象属性。

gcxthw6b

gcxthw6b1#

是否基于序列化字段更新对象?
添加OnChangedCall元素时,可以更新具有SerializeField的变量。

成员变量:

[SerializeField]
[OnChangedCall("onSerializedPropertyChange")]
private int _size;

你现在应该可以把函数作为一个字符串添加到括号中,并且应该在更改时调用它。
调用的方法必须是公共的,否则将产生错误!

方法:

public void onSerializedPropertyChange() {
    transform.localScale = new Vector3(_size,_size,_size);
}

OnChangedCall是一个自定义PropertyAttribute,需要从它继承。

OnChangedCall类:

using System.Linq;
using UnityEngine;
using UnityEditor;
using System.Reflection;

public class OnChangedCallAttribute : PropertyAttribute
{
    public string methodName;
    public OnChangedCallAttribute(string methodNameNoArguments)
    {
        methodName = methodNameNoArguments;
    }
}

#if UNITY_EDITOR

[CustomPropertyDrawer(typeof(OnChangedCallAttribute))]
public class OnChangedCallAttributePropertyDrawer : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        EditorGUI.BeginChangeCheck();
        EditorGUI.PropertyField(position, property, label);
        if(EditorGUI.EndChangeCheck())
        {
            OnChangedCallAttribute at = attribute as OnChangedCallAttribute;
            MethodInfo method = property.serializedObject.targetObject.GetType().GetMethods().Where(m => m.Name == at.methodName).First();

            if (method != null && method.GetParameters().Count() == 0)// Only instantiate methods with 0 parameters
                method.Invoke(property.serializedObject.targetObject, null);
        }
    }
}

#endif
aor9mmx1

aor9mmx12#

这是我从IndieGameDev的答案中得到的OnChangedCallAttributePropertyDrawer版本。
主要的变化是添加了以下行

property.serializedObject.ApplyModifiedProperties();

这将确保在调用函数之前更新SerializedField,修复了当序列化字段更改时场景无法正确更新的错误。

更改后调用属性属性抽屉:

#if UNITY_EDITOR

[CustomPropertyDrawer(typeof(OnChangedCallAttribute))]
public class OnChangedCallAttributePropertyDrawer : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        EditorGUI.BeginChangeCheck();
        EditorGUI.PropertyField(position, property, label);
        if (!EditorGUI.EndChangeCheck()) return;

        var targetObject = property.serializedObject.targetObject;
        
        var callAttribute = attribute as OnChangedCallAttribute;
        var methodName = callAttribute?.MethodName;

        var classType = targetObject.GetType();
        var methodInfo = classType.GetMethods().FirstOrDefault(info => info.Name == methodName);

        // Update the serialized field
        property.serializedObject.ApplyModifiedProperties();
        
        // If we found a public function with the given name that takes no parameters, invoke it
        if (methodInfo != null && !methodInfo.GetParameters().Any())
        {
            methodInfo.Invoke(targetObject, null);
        }
        else
        {
            // TODO: Create proper exception
            Debug.LogError($"OnChangedCall error : No public function taking no " +
                           $"argument named {methodName} in class {classType.Name}");
        }
    }
}
#endif

编辑:

我只是想分享一个小技巧给那些可能不知道的人,我不喜欢用字符串字面量引用函数,它通常容易出错,而且你很可能不会从IDE中得到帮助。
你可以使用nameof()来代替,这将允许你使用IDE自动完成,Rider也会识别正在使用的函数。

[SerializeField]
[OnChangedCall(nameof(UpdateHeuristic))]
private AStar.Heuristic heuristic;

public void UpdateHeuristic()
{
    _pathfinding?.SetHeuristic(heuristic);
}

完整代码:

using System.Linq;
using UnityEngine;
using UnityEditor;
using System.Reflection;

public class OnChangedCallAttribute : PropertyAttribute
{
    public string methodName;
    public OnChangedCallAttribute(string methodNameNoArguments)
    {
        methodName = methodNameNoArguments;
    }
}

#if UNITY_EDITOR

[CustomPropertyDrawer(typeof(OnChangedCallAttribute))]
public class OnChangedCallAttributePropertyDrawer : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        EditorGUI.BeginChangeCheck();
        EditorGUI.PropertyField(position, property, label);
        if (!EditorGUI.EndChangeCheck()) return;

        var targetObject = property.serializedObject.targetObject;

        OnChangedCallAttribute callAttribute = attribute as OnChangedCallAttribute;
        var methodName = callAttribute?.methodName;

        var classType = targetObject.GetType();
        var methodInfo = classType.GetMethods().FirstOrDefault(info => info.Name == methodName);

        // Update the serialized field
        property.serializedObject.ApplyModifiedProperties();

        // If we found a public function with the given name that takes no parameters, invoke it
        if (methodInfo != null && !methodInfo.GetParameters().Any())
        {
            methodInfo.Invoke(targetObject, null);
        }
        else
        {
            // TODO: Create proper exception
            Debug.LogError($"OnChangedCall error : No public function taking no " +
                           $"argument named {methodName} in class {classType.Name}");
        }
    }
}
#endif
njthzxwz

njthzxwz3#

public class UIAnimatorGroup:MonoBehaviour
    {
        public UIAnimatorTransform _animatorTransform;

    }

[System.Serializable]
public class UIAnimatorTransform
{
    [HideInInspector] public Matrix4x4 LocalMatrix4X4; // 局部
    [HideInInspector] public Matrix4x4 GobleMatrix4X4;
    [HideInInspector] public UIAnimatorTransform parent;

    [SerializeField] 
    [OnChangedCall("onSerializedPropertyChange","UIAnimatorTransform")]
    private Vector3 position;

    [SerializeField] 
    [OnChangedCall("onSerializedPropertyChange","UIAnimatorTransform")]
    private Vector3 rotation;

    [SerializeField] 
    [OnChangedCall("onSerializedPropertyChange","UIAnimatorTransform")]
    private Vector3 scale;

    public void onSerializedPropertyChange()
    {
        SetLocalMatrixByTransform();
        CalculateGobleTransform();
    }
}
huus2vyu

huus2vyu4#

[ExecuteInEditMode]怎么样?
然后你可以使用普通的Update()方法。

相关问题