Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Question [Solved]How to make class member or property that can animate and can detect change value?

Discussion in 'Scripting' started by Rukai_K, Aug 16, 2023.

  1. Rukai_K

    Rukai_K

    Joined:
    Jul 14, 2019
    Posts:
    7
    UnityEngine.UI.Slider component has a Property called "Value". Even though it can be changed from the Animation Clip like a member variable, it can Invoke OnValueChanged from the setter like a property.
    I would like to define such a property in my component class as well, but how can I declare it to do this?

    UnityEngine.UI.Slider.value is declared as
    Code (CSharp):
    1.         public virtual float value
    2.         {
    3.             get
    4.             {
    5.                 return wholeNumbers ? Mathf.Round(m_Value) : m_Value;
    6.             }
    7.             set
    8.             {
    9.                 Set(value);
    10.             }
    11.         }
    but even if I declare it the same way, it does not show up in Inspector and I cannot change the value from the Animation Window.
    Also, the OnValidate() method is an Editor Only method and cannot be used in this case.

    Please let me know if you know how to do this or if you have an existing post.

    Version: Unity 2021.3.19f1
     
  2. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    6,010
    It's probably working off the serialised backing value, not the property itself, as Unity can't serialise properties.

    So make sure you have a properly serialised regular field, and you should be good to go.
     
  3. Rukai_K

    Rukai_K

    Joined:
    Jul 14, 2019
    Posts:
    7
    When I declair serialised regular field, how to detect value change and invoke something without setter?

    Thank you for your response.
     
  4. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    6,010
    Just serialise a private field, and expose it via a public property.
     
  5. Rukai_K

    Rukai_K

    Joined:
    Jul 14, 2019
    Posts:
    7
    Hmm, I don't think this answers my question, since a public property cannot be animated from an Animation Clip like Slider.value, so just making private field a public property does not solve my question.

    Or am I missing something? Would you mind write some sample script that solve my question?
     
  6. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    6,010
    Uh, yes it does. How about you actually try it out first?

    Honestly if you just read and understand the code of the slider source code you'll answer your own question. It has a private backing value,
    m_Value
    , which is the serialized value and the value the Animator Clip is using.

    And then
    value
    is the exposed property that handles all the extra stuff when the value is set via code.

    In basic terms:
    Code (CSharp):
    1. public class MyUIComponent : Monobehaviour
    2. {
    3.     [SerializeField]
    4.     private float _someFloat = 0.5f;
    5.    
    6.     public event Action<float> OnValueChanged;
    7.    
    8.     public float SomeFloat
    9.     {
    10.         get => _someFloat;
    11.         set
    12.         {
    13.             OnValueChanged?.Invoke(value);
    14.             _someFloat = value;
    15.         }
    16.     }
    17. }
    So please actually try what I suggested before you respond.
     
  7. Rukai_K

    Rukai_K

    Joined:
    Jul 14, 2019
    Posts:
    7
    Thank you for your code!
    This code can not add event from inspector so I edited like below.
    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3.  
    4. public class MyUIComponent : MonoBehaviour
    5. {
    6.     [SerializeField]
    7.     private float _someFloat = 0.5f;
    8.  
    9.     public event Action<float> OnValueChanged;
    10.  
    11.     public float SomeFloat
    12.     {
    13.         get => _someFloat;
    14.         set
    15.         {
    16.             OnValueChanged?.Invoke(value);
    17.             _someFloat = value;
    18.         }
    19.     }
    20.  
    21.     private void Start()
    22.     {
    23.         OnValueChanged += TestHandler;
    24.     }
    25.  
    26.     private void TestHandler(float value)
    27.     {
    28.         Debug.Log($"Value changed to {value}");
    29.     }
    30. }
    Then I made an looped animation clip like attached img and run it.
    But console is no log that have to be invoked.
    SomeFloat value that discribed on inspector is actually changed by animation.
    Did you have any ideas what's missing for now?
     

    Attached Files:

  8. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    6,010
    Well you're animating the private field, and not the property. So no, it won't and can't work how you expect it to, you realise that?

    You would have to check for changes in Update, I imagine.

    I highly imagine it's the same case for Unity's components too.
     
  9. Rukai_K

    Rukai_K

    Joined:
    Jul 14, 2019
    Posts:
    7
    Gee, that's exactly why I started this thread, because I didn't want to do that "check EVERY frame with Update()".:(
    (Because unlike event-driven, it is a per-frame load.)
    But yes, your opinion seems to be correct. That is, there is no type declaration or attribute that has the functionality I mentioned in the title, and it may simply be implemented in the "check every frame with Update()" way.

    Thanks for your response, it is very much appreciated.
     
  10. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    6,010
    Well, in the same source code for Slider you were looking at, you'll find it also uses Update to refresh the visuals when the value changes.
     
  11. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,571
    The UI Slider uses an undocumented MonoBehaviour callback in the UI system called OnDidApplyAnimationProperties. In there they manually call the changed event if the normalized value has changed.
     
    Rukai_K and spiney199 like this.
  12. Rukai_K

    Rukai_K

    Joined:
    Jul 14, 2019
    Posts:
    7
    That's it! I confirmed that callback exists and overrided from UIBehaviour.
    My problem was completely solved by inheriting UIBehaviour and overriding OnDidApplyAnimationProperties() instead of MonoBehaviour.
    This totally convinced me. Thank you so much! I really appreciated to provide this information.