Subscribing UI Elements to Object Values - Worth It?

I’m setting up a UI panel that displays a ton of information, most of which is mapped directly to values in a set of objects. While I started doing it the “default” way, something like this:

public Text playerName;
public Text playerTitle;
public Text playerScore;
public Text playerHealth;

public void SetValues() {
    Player player = CharacterManager.Instance.Player;
    playerName.text = player.name;
    playerTitle.text = player.title;
    playerScore.text = player.score.ToString();
    playerHealth.text = player.health.ToString();
}

I thought to myself “There has to be a better way.” As it stands, every time a value changes, I need to call SetValues() again on this panel.

There’s the more obvious way of using Update:

private Player player;

public Text playerName;
public Text playerTitle;
public Text playerScore;
public Text playerHealth;

public void Awake() {
    player = CharacterManager.Instance.Player;
}

public void Update() {
    playerName.text = player.name;
    playerTitle.text = player.title;
    playerScore.text = player.score.ToString();
    playerHealth.text = player.health.ToString();
}

But this seems excessive to be doing every frame.

Soooooo…

I had the idea to use C# events to subscribe UI elements to object properties and have them update themselves only as needed. Ideally this would be done through some sort of utility class so that I could just call it generically like:

public Text playerName;
public Text playerTitle;
public Text playerScore;
public Text playerHealth;

public void Awake() {
    Player player = CharacterManager.Instance.Player;
    UISubscribe.Subscribe(playerName, player.name);
    UISubscribe.Subscribe(playerTitle, player.title);
    UISubscribe.Subscribe(playerScore, player.score);
    UISubscribe.Subscribe(playerHealth, player.health);
}

And the Subscribe method would take care of converting to string, binding to the events, and setting the text values.

I’ve never worked much with C#'s event system, so I’m not positive what I’m getting myself into. Any advice or pointers are appreciated. In general, though, is this a good idea? Or am I better off throwing it all into Update and calling it a day?

For a small set of values, it’s not the end of the world to call the setup function, I’d say. If you really wanted to , you could create events for each one individually; I’d say that this could be useful if you have a number of places in code that need to be update when a certain value changes.
Update, as you thought, too, would be a bad place, in my opinion.

Did you want some help setting up events or were you just looking for more some opinionated feedback?

The thing deterring me from doing that (which isn’t represented well by my example) is that my values will not only be changing a lot, but can be changed from many different sources.

A better example is the player’s food supply, which is just an int value. It degrades steadily over time depending on the player’s party size. But it can also be reduced via a number of game events that happen. Or it could be increased by the player having a successful hunt. Rather than each of these other events having to tell the UI to update to show the new value, my thinking was have the UI be responsible for redrawing itself.

By this, do you mean that each panel (MonoBehavior) should have the events defined within themselves, and not in some sort of utility class or manager that handles all UI events? That makes sense, I suppose. I think I have a tendency to overplan and design grand, generic, scaleable systems instead of practical ones. :stuck_out_tongue:

Mostly opinionated feedback, but I certainly wouldn’t turn you down if you wanted to provide some code! I’ve been peeking at these two Unity Answer threads off and on today (1) (2), so if those are more or less how you’d do it, I won’t take up your time.

Thanks for your input!

One pitfall to watch out for is a circular update when using events to bind an UI element to a value.

As long as the UI element only gets the value and displays it, you’re OK. However, as soon as you allow for a two way binding where the UI element (such as a slider/scrollbar) can also set the value, which triggers an event, which updates the UI…

One way to deal with that is to only fire the event if the value actually changed, but that can get tricky when dealing with floats. Another way is to temporarily remove the event, update the value, then re-add the event, but that’s a good way to create bugs. Another way is to just use a “bool ignoreEvents” flag to decide whether to fire the event or not if the UI changes the value.

However, all that is moot if all you care about is a one way binding where the UI doesn’t change values. In which case, it’s actually an excellent use of events.

1 Like

To speak in more generic terms… I think what you’re looking for is the MVC pattern, which keeps the UI separate from the logic or data.

Under this pattern, it makes sense that anything can change the amount of food you have, and the UI only cares if the food amount changes and gets updated accordingly. By having the food property fire an “I changed!” event, you keep UI implementation details out of your logic and data.

A more extreme way to think about this is this: Are you able to completely change or replace the UI without affecting the underlying game logic or data? If so, you have a good MVC system going. This (in theory) allows you to have easier cross platform support, etc.

Thanks for the input, BlackPete! I think you’re right with the MVC thing. I’m a web developer by trade and working on a big Angular app right now, so that’s how I want my Unity stuff to work, too, I guess. :stuck_out_tongue:

This is a great point, because while what I’m currently doing is just puking a bunch of info out onto the screen, eventually I’m going to have inventory panels and ones with buttons that allow you to change data. I think the approach I would take would still be to have the display of that data (a Text) be bound to the value, while the buttons and such ran methods that affected that data

The two links that you said you were looking at seem decent. I just took a quick look.
Having properties that initiate events could be a good solution for you.
The event is subscribed to from any UI element that needs to be updated if said value changes, and gets notified when it does.
This way, only the parts that need updating get notified, and you don’t have to update everything at once.
Also, for example, if you had more than 1 place in your program that wants to know about a changed value, they can all subscribed to this event and be more self-contained in that manner. :slight_smile:

1 Like

One tip: Define your events like so:

public event Action<string> OnUserNameChanged = delegate { };

That way, you don’t need to check if it’s null before calling it. It’s always valid, so just call it.

1 Like

A few random thoughts, in no special order.

Subscribing to an event must always come with an unsubscribe. Otherwise you can get weird GC stuff.

Trigger events via properties. That way they will fire no matter what changes the value.

Building an event for every single property in your game that affects the UI will make your UI tightly coupled to your game. This will increase development time, everytime you change something you’ll have to follow the full event chain through the UI.

If you aren’t strapped for performance, I would leave the UI refreshing every frame in Update. If you need further optimisation I would go to a single UIChanged event. It’s only if things get really tight that I would consider individual events. Developer time is often more precious then cpu time.

1 Like