Last time I made an entry I said that my next task would be to have enemies drop items, and to create enemy spawners. When I opened my project, I started looking through my code and wound up refactoring quite a bit of it.
By the end I had 3 or 4 new classes, making all of my classes quite a bit leaner and more maintainable. However, one of the solutions I came up with to better handle UI updates still doesn't feel quite right.
My UI basically has 4 main elements: Health, Experience, Weapons and Money. The first three utilize a slider to visualize % of health remaining, % XP to next level and % of weapon capacity currently loaded, respectively. The fourth simply shows the user's current amount of cash (Placeholder icons):
I had two main ideas for a solution. The first was to create a class for each group of UI elements (one for health, one for XP, etc) and to simply have them update the sliders and text each and very frame using a reference to either my Stats (of which there is only one of in the game, as an instance) or WeaponController (of which there is one for each different weapon) objects. Stats holds player variables like health and XP, while WeaponController (which maybe should be split into Weapon and WeaponController classes) contains weapon variables like capacity, roundsLoaded and roundsOwned.
This, to me, seemed like the optimal design choice. It had the least amount of complexity and seemed like it could cause the fewest number of problems. Essentially, the UI elements would stand alone, and even if they were removed from the game, nothing would break. Also, it would be the easiest to write this way.
My second option, the one I chose, was a little more complex, but, I think, more efficient. However, as I'm writing this, I'm beginning to think I made the wrong choice. The reason I even came up with this option was because I was thinking about efficiency. Having the UI elements just update 'magically' on every frame is easy and clean, but maybe a little wasteful. I decided that it would be more efficient to have the UI elements only update when needed, i.e. when stats change.
At the time, it seemed to me that good design is just fine, but it should be tossed out if a more efficient solution exists. Now, I realize that the complexity of simply updating UI elements isn't going to be very CPU intensive, but I figured every little bit of CPU savings helps, so this is the solution I went with. I still have a class for each UI group, as in my first solution, but these classes do not have references to my instance(s) of Stats or WeaponController. Instead, Stats has a reference to HealthUI, CashUI and XPUI, and WeaponController has a reference to WeaponUI.
Any time a player takes damage, gains cash or gains experience, the related code winds up leading to methods in Stats, which complete all the appropriate actions, including making calls to the UpdateUI() method in the relevent UI Manager object (HealthUI, CashUI, etc), passing itself as an argument. So, for example, Stats' TakeDamage() method looks something like this:public void TakeDamage(){ //do some stuff ... healthUI.UpdateUI(this);}
The same goes for the calls to the other UI Manager objects. They all take a reference to Stats as an argument (except for WeaponUI).
WeaponController functions similarly, passing itself as an argument in its calls to WeaponUI's UpdateUI() method.
Thinking about this now, it seems like I've chosen an overly complicated solution with little to gain from it (only slight efficiency benefits), and it seems like the cons far outweight the pros (for example, removing the UI elements will now break the game). Also, it stinks of circular dependency.
I think I've answered my own question, but what do you think? Which option would you have chosen, and do you have any better solutions I didn't think of?
I was going to post some questions about the design of my game's enemy spawning mechanic as well (yet to be implemented), but this post has gone on long enough, so I guess I'll make a second entry for that this evening.
I remember facing a similiar problem when I tried to update UI elements. I always tried a "only update when needed" approach instead of updating every frame, because otherwise it would be "bad" code.
What a pain in the ass. It's a nightmare to code right and it doesn't make any significant gain in performance. Once I realized that drawing stuff on screen is what takes 90% of a frame time, I stopped worrying about things like that.
Just update every frame, it will be better for your mental health. If you still want to be a little more efficient, in your "update" method check if the current value is different than the new value, and if so, do the update stuff, otherwise nothing happens.