简体   繁体   中英

OnValueChanged in Scriptable Object Unity

Can I use some kinds of OnValueChanged() or OnValueChanged Attribute on a variable/property in a class extented ScriptableObject in C# Unity to invoke another method whenever the value of that variable/property in its asset file is changed in inspector? Or is there anyway to serve that purpose?

You can use Custom Editors to archive this. Here is a minimal example that can monitor changes of both fields and properties

public class CustomType : ScriptableObject {
    public int myField;

    private int myProperty;
    public int MyProperty {
        get { return myProperty; }
        set {
            if (value != myProperty) {
                // do stuff here
                myProperty = value;
            }
        }
    }

    public void OnMyFieldChanged(int from, int to) {
        // do stuff here
    }
}

#if UNITY_EDITOR

    [CustomEditor(typeof(CustomType))]
    class CustomTypeEditor : Editor {
        public override void OnInspectorGUI() {
            CustomType customType = (CustomType)target;

            int field = EditorGUILayout.IntField("Field", customType.myField);
            if (field != customType.myField) {
                customType.OnMyFieldChanged(customType.myField, field);
                customType.myField = field;
            }

            customType.MyProperty = EditorGUILayout.IntField("Property", customType.MyProperty);
        }
    }

#endif

This will only work if you open this opject in the inspector. If you want this object as part of another object you have to use Property Drawers instead. These are very similar to custom editors. This is a very basic example to show that it is possible to solve your problem with a custom editor, but you probably want to look more into the topic if you plan on using them.

A simple propertydrawer solution can be found in this gist , if your class is not inheriting from monobehavior for OnValidate() or want to avoid making a custom editor for each class. Also below a simple implementation and example:

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

Example:

using UnityEngine;

public class OnChangedCallTester : MonoBehaviour
{
    public bool UpdateProp = true;

    [SerializeField]
    [OnChangedCall("ImChanged")]
    private int myPropVar;

    public int MyProperty
    {
        get { return myPropVar; }
        set { myPropVar = value; ImChanged();  }
    }


    public void ImChanged()
    {
        Debug.Log("I have changed to" + myPropVar);
    }

    private void Update()
    {
        if(UpdateProp)
            MyProperty++;
    }
}

The free NaughtyAttributes Asset has the attribute OnValueChanged which you can use to trigger when a value changes via Inspector. Here is an example with a drop down list visible in the Inspector:

[Dropdown("LevelNames")]
[OnValueChanged("OnLevelChanged")]
public string currentLevelName;

private List<string> LevelNames {
    get {
        return new List<string>() { "Level 1", "Level 2", "Level 3" };
    }
}

private void OnLevelChanged() {
    Debug.LogFormat("OnLevelChanged: {0}", currentLevelName);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM