How do you bind a Progress bar in UI Toolkit?

I am creating a custom inspector using UI Toolkit, and I’d like to have a progress bar that displays the current health of the actor.

I have already drawn the progress bar (and set the upper / lower values) via C#. This works, however it’s not automatically redrawing. I have to select a different GO in the scene and go back to see it update. I assume the problem is that I need to bind to a serialized property but I am not sure how to do that with the Progress bar since it needs to bind two - the upper and the lower.

Here’s the C# code that I have, incase it is helpful:

        public override VisualElement CreateInspectorGUI()
        {
            VisualElement myInsepctor = new VisualElement();

            VisualElement defaultInspector = new VisualElement();
            InspectorElement.FillDefaultInspector(defaultInspector, serializedObject, this);
            myInsepctor.Add(defaultInspector);

            ProgressBar healthBar = new ProgressBar();
            healthBar.title = _player.Health == null ? healthBar.title = "Health" : $"{_player.Health.CurrentHealth} / {_player.Health.TotalHealth}";
            healthBar.lowValue = _player.Health == null ? 50 : _player.Health.CurrentHealth;
            healthBar.highValue = _player.Health == null ? 100 : _player.Health.TotalHealth;

            myInsepctor.Add(healthBar);
        }

This is where you’d likely be using TrackPropertyValue.

There’s examples of how to use it in the manual: Unity - Manual: Receive callbacks when a bound property changes

This might work. I’m stuck on trying to figure out how to bind to a nested property though. My Player class (which is the inspector I’m modifying) looks loosely like this:

    public partial class Player : MonoBehaviour
    {
        ///irrelevant stuff above

        [SerializeField]
        public Health Health;

and then health looks like:

    public class Health
    {
        ///irrelevant stuff above

        public int TotalHealth;
        public int CurrentHealth; 
   }

I’ve written some C# for the Inspector, which looks like this for the binding (I’ve tried a few things here - but this is the latest):

SerializedObject so = new SerializedObject(_player);
SerializedProperty sp = so.FindProperty("Health.CurrentHealth");

_healthBar.Bind(so);
 _healthBar.BindProperty(sp);
_healthBar.TrackPropertyValue(sp, (e) => { Debug.Log(e.intValue); });

I get an error telling me that sp is null. How do I set this to be a nested property? (I have searched a lot online, but nothing I’ve found has worked.)

Double check if your Health instance is being serialised? If it’s not being serialised/showing in the inspector, there’ll be no serialised property. If it is, you can right click it and there should be an option to copy it’s property path.

Ah, you are a genius my friend. That was the issue! Thank you. :slight_smile:

1 Like