Best way to handle UI elements / Scripts/Components/Elements?

I’ve recently started thinking about ways to handle having lots of UI elements, or scripts/components that other gameobjects would need to reference.

For my example, I’m developing a multiplayer game that involves spawning a player object at runtime, so the first thing I tried is a direct reference to my UI objects. So when a player gameobject is spawned, I set my UI healthbar to depend on THAT player’s health. And that worked, but felt sort of cluttered, and I just wasn’t happy with it.

Then I decided to put all my UI elements into a UIManager class. Then when a player/gameobject spawns, all they have to do is have a reference to that UIManager’s gameobject. And that worked better, but still wasn’t quite what I wanted. As It still required having some sort of reference to the UIManager gameobject, in any of the player’s scripts that needed it. So even this was still cluttered.

So next I took my UIManager class, and turned it into a Static class. So now when my player spawns, all I have to reference is “UIManager.instance.STUFF” and this seems to work quite well, but I’m a bit worried if this isn’t a good way in doing so, as I’ve read that Static’s can be “troublesome” especially since this is a networked game.

This doesn’t just go for UI elements and objects, this could really be for any gameObject that a player/object has to reference at runtime, that isn’t on their own prefab.

I’m interested to hear how good/bad my setup’s are. And I’d love to hear how some of you folks handle this?

2 Likes

Well, I always do like the last method you mentioned, the only difference is that I call it UIController :p, I don’t know how that would be in an online game though…
I attach this monobehavior to my main canvas, and then on this class I have just a bunch of references to other components and gameobjects, if I just need to activate/deactivate I reference the gameobject, if I need to change some text, I make a reference to the text and so on, then to change it I do it like you said:

UIController.instance.SetScore(score);
//...
public void SetScore(int score)
{
 scoreText.text = score.ToString();
}

I think this is the best way, I will keep an eye to this thread since I also want to know what the other guys has to tell. :smile:

2 Likes

The static manager approach can work well enough as long as there’s only ever going to be one UI, one player character, and a clear delineation about which character that is at any given time.

I personally use events for this kind of thing, as they help keep things flexible. I’ll often make some of my events static, but they always include a reference to their sender. So if I decide to, say, add a second health bar to my UI (say I’m implementing a boss fight and I want to show their health too) then I can make a second health bar and give it a reference to the boss character. Both health bars listen for an OnHealthChanged(Character, int, int) event. They compare the Character reference in the event to an internal reference to their target Character and trigger an update if they match.

There’s other methods as well. That’s just my current preference (and has been for quite a while now).

It’s important to note that many methods can get the same functional result. Often the pros and cons of different approaches are more to do with maintainability than functionality. That’s what I like about the event approach - adding a new health bar for a boss character in that case involves two simple steps: 1. create a new health bar widget, then 2. set its target to the other character. Basically, you wouldn’t even need a programmer to make that change.

2 Likes

See this is one thing that has never really hit off with me as far as C# and OO goes. People are making special classes for health, damage, player, player controller, ui controller, game controller, etc etc. Classes for everything really, and I see how this helps keep everything modular and maintainable.

Now don’t get me wrong I am educated in both Object oriented and non object oriented, but I am in 3rd year of university and they mostly only slammed down C in our brains. The problem with this is my code is mostly “C”, my project is a 6000+ lines that look mostly like C and it works perfectly on Mobile devices so it’s not really an issue. It really has almost no object oriented approach to it, and I still don’t see why it would need it. When it comes to asking about all these classes, really my game is a GameController, and PlayerController and thats it for 6000 lines. PlayerController holds the health, stats, healthbar, stamina bar, misc info about monster, etcc. While GameController literally handles everything, and mostly filled with 2D game object arrays to have everything handled properly.

So really I don’t see any reason why not just do player_.health = 100; and its own player script will handle the update of the health bar and damage etc when it needs to, I don’t see the reason to split this into 4+ classes. I just feel like having a strong C background I want to keep it mostly array based and have strict control over it. I really am not grasping the reason behind going so crazy on classes and weird depth on OO for no reason. Will a job be impressed by your amazing OO concepts or about getting the job done, with no bugs, on time, and performs with good fps?_
I have taken multiple OO classes, and yes my game works flawless on the phone if we are talking about performance.
Anyone care to explain why I should be doing stuff more OO? As far as I know setters and getters are pointless, and everything is still easily hackable no matter what. Creating more methods / calls to make everything OO just adds more overhead, but more modular code. Also I understand my code is a strong “black box” and “god script”, but in the end result I don’t see why that is a big deal.
PS. This is not an attack towards OP, I just wanted to learn something new.

I guess it just comes down to personal preference then. I’m not going to bash your way of doing things. But personally, having one huge class for your player, and one huge class for the GameController, is just crazy.

I suppose it also comes down to how “complicated” your game is, if it’s something like a simple “Flappy Bird” clone for android, you can get away with that easier then you could with something like a multiplayer PC game, which is far more complicated.

I’m interested to hear from someone who has more experience with networked projects, and what they’ve done to solve this issue.

Well right now its a core for a turn based strategy game, so it obviously will have some serious depth to it. I plan to make it as a simple matchmaking game online later so it is a big deal and yes I would also love to hear more from someone more experienced.

But I don’t see why this is “crazy” if its a big complicated game it will be a big complicated game no matter what even if you used OO or non-OO approaches. You would just have it moved into more scripts and I got it in one or two scripts total. Beauty of this is I know exactly where something is, and I have almost no getComponents in my entire code. Sure I am gonna be looking at 10,000+ lines in the future but if I was at a job I could be looking at a million lines of code a day with a game company on one script.

I don’t know much about multiplayer / networking, but as that goes I would only need the server to create a single GameController when game launches and the controller will do the rest…I see that as a huge benefit, and I can easily track whats going on in it.

For the applications you are writing it probably isn’t a big deal. When you work on larger applications or when you work in a team it will rapidly become a different matter.

As I said:

You are achieving your desired functional result, and you haven’t run into problems with scale yet, so you’re happy. If you keep at software development, though, you will almost certainly run into issues with scale and/or flexibility.

A classic example is working for a client, designing to implement exactly what they asked for in the fastest reasonable time, and then being asked to change it. In my early days that resulted in lots of terrible code because I didn’t want to “over complicate” things, so I took approaches like the ones described here because they were quick. That did indeed result in fast initial turnarounds. But then come the changes…

Lets continue with the health bar example. I might set one up for the player with a few variables straight in my UI class. Quick, easy, done probably in under an hour. Show the client. “That’s awesome. Now I was thinking that at the end of level 3 there’ll be a boss. Can you just apply it to them, too?” Yeah, ok, that’s not too bad, I make a second set of variables, one for the player and one for bosses. Cool.

The client loves it. “This is awesome, man. Can we add it to all of the enemies?” And then they all need different sizes and colours depending on the importance of the enemy and the type of damage they take. And then we want to add a different type of particle effect to each.

Yes, we can indeed keep kludging it all on top of our base UI class. That can get the required functional results. But it isn’t the most efficient way. Consider:

  • How much more complex is that class getting?
  • Is the complexity leading to changes taking longer to perform?
  • Is the complexity leading to increased bug count?
  • Do the above effect design of the application?
  • Is a programmer required to make changes?
  • What happens if you’ve got two programmers who want to work on the UI at once?
  • If the rest of your code is similar, what happens if you want multiple programmers to work on it at all?

OO provides a solution to this. It’s called a “Class”. :wink: Unity provides extensions to this solution, namely MonoBehaviours (classes which can be attached to GameObjects and receive scene callbacks) and Prefabs (a pre-configured GameObject we can drop into a scene).

If we use those solutions then instead of playing a time consuming game of stack-a-hack with a monolithic kludge, we instead make a new variation of a Prefab and set a reference to point at it instead of the original.

Also - and this is critical in cross-discipline teams (ie: game development) - you don’t have to be a programmer to reconfigure something. (This is true in many component environments other than Unity, too.) You sure as heck do have to be a programmer to modify the kludge class.

I’m most experienced with the combination of Object Oriented + Component Oriented because I’ve worked in Unity so much and that’s what’s used here, but there are also other paradigms that also offer solutions.

2 Likes

Thank you very much for the post! It was great insight, and I can see how having multiple programmers OO would be the best approach for sure. I would still love some insight from a person that started with C and worked in that for a few years and after started some game dev.

1 Like

Do you know any tutorial that show ways of using events to update UI elements? How would you implement that, can you explain more?

The UnityEvent documentation covers it pretty well.

Ah, I see, it’s very simple, thanks @angrypenguin !

1 Like