kitta
kitta

Reputation: 2069

Unity C#, How script know when public variable(not property) changed

For example of UnityEngine.UI.Text

It's contains text variable for set the value of text as string.

Now, In my own class I use property(get;set) instead directly access the variable, This let me able to update views or fire some events when new value have set. The problem is when using property(get set) it'll not show this field in inspector and even in integration test tools.

But Unity's text component decided not to use get set. Which in my opinion I feel not comfortable for deal with value changed.

Question is how original UnityEngine.UI.Text in text variable can dealing with this?

I think It not just OnValidate() because it can only work on editor. But text variable also work on runtime scripting.

This function is called when the script is loaded or a value is changed in the inspector (Called in the editor only). https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnValidate.html

Upvotes: 4

Views: 20512

Answers (4)

kitta
kitta

Reputation: 2069

Another solution I used:

public T car;
private T _car;

public Car { get => _car; set => _car = value; }
public void OnValidate() => Car = car;

Upvotes: 2

Alexander van der Zalm
Alexander van der Zalm

Reputation: 163

If you are not using a monobehavior (so you can do OnValidate()) or want to do polling than you could opt for going for an propertyattribute string to method reflection solution along the lines of this gist. Also below a simple implementation and example:

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 => myPropVar;
        set { myPropVar = value; ImChanged();  }
    }


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

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

Upvotes: 5

Falanwe
Falanwe

Reputation: 4744

You just can't. That's why most components in Unity poll the value they are relying on every single frame.

If you really want to detect changes, you can do something like this:

public sometype Field;

private sometype _previousValueOfField;

public void Awake()
{
    _previousValueOfField = Field;
}

public void Update()
{
    if(Field != _previousValueOfField)
    {
        _previousValueOfField = Field;
        // Execute some logic regarding the change of the field there        
    }
}

(By the way that's another reason why public fields are evil and I hate Unity for relying on them.)

Upvotes: 0

Bart
Bart

Reputation: 20028

There is no real way other than to check if the text has changed with respect to a cached "previous" value. In the absence of extension properties you'll simply have to do so manually.

Upvotes: 0

Related Questions