Subscribable Vars and UI Updating

In the DM98 code I can see the UI being updated a lot in Tick methods, for example:

public override void Tick()
{
	var player = Player.Local;
	if ( player == null ) return;

	Health.Text = $"{player.Health:n0}";
	Health.SetClass( "danger", player.Health < 40.0f );
}

Have you considered having such variables wrapped in a kind of subscribable object, this can benefit addons too because you could subscribe to changes in any networked var or other such properties.

For example:

public Subscribable<int> Health { get; private set; }

// Elsewhere for updating the UI...
player.Health.onValueChanged += (oldValue, newValue) => {
	HealthUI.Text = $"{newValue:n0}";
};

// Access directly anywhere by:
player.Health.Value;

Just a thought.

4 Likes

Taking into account that changing properties involves calling their setters, I’m pretty sure you could implement it yourself by adding an attribute that wraps property setter to trigger a callback (which you can subscribe to in any way you like) when the property is changed, possibly checking if we’re on client (possibly set by network) or server.

2 Likes

Yes indeed, I just thought it might be cool to have this functionality built in and standardised to avoid different attempts to achieve the same thing. I guess the crux of my suggestion relates to avoiding continuously polling changes to variables to update the UI. I thought this might be a good way to go about it :smile:

2 Likes

There’s a few things to consider with this stuff.

First of all, is it going to trigger every time the value is changed. Is it going to make it so when you set Health, it calls the callback. Does that add invisible performance load to setting a variable that the user isn’t aware of.

Does that have implications with where it’s called from, when it’s called. If I set health in a thread, will that break things?

If you want the HUD to show information for the currently spectated player, would that still work? Do we have to unsubscribe from the var on one player and subscribe to the var on another?

In actual reality the tick based code just creates a string. It sets Text to that string, if it’s the same, nothing happens. I coded that on purpose so that the label wouldn’t get rebuilt for nothing.

You could make it more performant by creating an IntPanel or something and setting/comparing the number for changes every tick. You could also make it so the logic code only runs at 30hz or something too.

I’d probably argue that for this situation, it’s probably simpler to do it this way than try to handle all the other possible issues.

6 Likes

A great response justifying your method, thanks for taking the time Garry. I am just personally averse to code that runs every frame / tick and avoid it where possible, because I’ve spent so long working with Unity I’m hard-wired to think about performance first and that includes things like string allocation, which in Unity can really affect performance. This is mostly to do with the fact that garbage collection in Unity is a real problem if you aren’t careful.

1 Like

Many programmers worry about performance when they shouldn’t and don’t worry when they should. The only way to find out when and what you really need to optimize is to do some profiling, where you’ll see in a form of a nice chart what kind of stuff actually makes a difference.

Unmeaningful optimizations often lead to complex and unreliable code which is also hard to follow, often it’s the best to just do it the most intuitive way and optimize only if it really causes framerate drops.

5 Likes

Yep, it took me a while to get out of the unity state of mind too. The garbage collector isn’t something to be feared in s&box.

5 Likes