• entries
27
62
• views
26065

1802 views

[font=arial]Tying Up Loose Ends[/font]

[font=arial]I was able to keep the child Collider, and rather than attach a custom Head script to it and look for that reference in my Projectile's OnCollisionEnter2D function, I started digging more into Unity's Collision2D class. It turns out that this class is good for a lot more than just things like[/font]

void OnCollisionEnter2D(Collision2D col){ if(col.tag == "Enemy"){ col.getComponent().TakeDamage(damage); }}

Handling UI Updates; Lessons Learned

[/font]
[font=arial]In one of my earlier posts, I mentioned that I may have made a bad design choice when handling my UI updates. Originally, I had references to my Stats script (which holds properties for ammo, health, XP, Cash, etc.) inside of each of my UI classes. Then, these UI classes would just update their corresponding elements with whatever values they were interested in on every frame. It seemed to me that doing this every frame was bad practice and had the potential to be more processing intensive than necessary (not much, mind you, but still). So I decided to take an approach where the UI would only be updated when a stat value actually changed. This meant removing the references to Stats from my UI classes, and instead giving references to all of my UI classes to my Stats class, basically just inverting the relationship. Then, rather than call the UI update methods in the Update function, I would call them inside the methods that actually caused stats to change (TakeDamage, Fire, AddCash, etc.) This wound up being so disgusting that I decided to add another layer and bring in a UIManager class. This just wound up making everything more disgusting, and in the long run, my UIManager just wound up updating stats every frame anyway. What a disgusting waste of time. The end result is, from the outside, identical. Internally, however, it's probably worse in all ways imaginable.[/font]

[font=arial]In hindsight, I should've just left it.[/font]

[font=arial]I shudder at the thought of what the UML or something for my project might look like. Okay, you twisted my arm, here you go (those of you who are easily offended or prone to seizures, look away now):[/font]

[font=arial][/font]

As you can see, I pretty much threw design out the window at some point on this project. This is completely unreadable and really makes me want to barf. There's circular dependency pretty much everywhere. It's just gross.

For those of you who are interested, here is the diagram for just the UI and Upgrade related stuff, which is still pretty nasty:

[font=arial]Of course, as it always goes, now that I have finished the game, I figured out the perfect way to only update when required, and to do so cleanly and maintainably: Events and Delegates.[/font]

[font=arial]Rather than store references to everything all over the place, polluting my code and making for poor structure, I could have many of my classes completely agnostic to one another. [/font]

[font=arial]I could use events a delegates all over the place in my project (or any for that matter). They seem like a really great way to keep things clean and avoid storing a jillion references, especially when they're only needed once. However, the implementation I would need for my UI updates would be something simple like this:[/font]

[font=arial]Stats.cs:[/font]//Create the delegate with the method signature which each UI class will implementpublic delegate void StatEventHandler(float currentStatValue, maxStatValue);//Create events which each UI class will subscribe to, and make them static to avoid the need of storing references to Statspublic static event StatEventHandler OnAmmoChange;public static event StatEventHandler OnHealthChange;public static event StatEventHandler OnXPChange;//Etc., etcprivate void Fire(){... ammoCount--; OnAmmoChange(ammoCount, maxAmmo);...}public void IncreaseAmmo(int amount){... ammoCount += amount; OnAmmoChange(ammoCount, maxAmmo);...}public void GainXP(float amount){... currentXP += amount; OnXPChange(currentXP, xpToNextLevel);...}public void TakeDamage(float amount){... health -= amount; OnHealthChange(health, maxHealth);...}public void Heal(float amount){... health = Mathf.Clamp(health + amount, 0, healthMax); OnHealthChange(health, maxHealth);...}//Etc., etc.
[font=arial]Then in each UI class, I just subscribe to the event so that my UI class's UpdateUI function is called when these events occur.[/font]

[font=arial]HealthUI.cs (or any UI class, slightly modified):[/font]Stats.OnHealthChange += UpdateUI;private void UpdateUI(float currentStatValue, maxStatValue){ healthText.text = statValue + " / " + maxStatValue; healthSlider.value = currentStatValue / maxStatValue;}
[font=arial]Now stats doesn't have a reference to UI classes, or vice-versa, which makes things a lot cleaner. Also, UI elements are only updated when a change actually occurs. I think this is the perfect solution for this case. [/font]

[font=arial]In fact, events and delegates seem like one of the most useful tools I've found thus far. I could envision a game where you can pretty much avoid storing references altogether (unless absolutely necessary), and rely on a custom event system to drive the game. I could even see this going a step further and having an EventManager class (potentially with subclasses) which acts as an intermediary. It would subscribe to events fired by other classes, but the only events that other classes would subscribe to are ones fired by EventManagers in response to the events that IT has subscribed to. [/font]

[font=arial]Basically, EventManager would listen to all GameObjects which were firing events, and GameObjects would ONLY listen to EventManager. It seems that this would provide a nice centralized solution. Granted, this would introduce a different kind of circular dependency, but one that I think would be acceptable. I might try something like this on my next projects. Has anyone tried something like this, or know of some design pattern that functions like this? Please share your thoughts.[/font]

[font=arial]That's really all I have time to write at the moment, but stay tuned for a proper post-mortem and some news on what I'll be working on next.[/font]

[font=arial]And please, if you have the time, check out the game and leave a rating![/font]

Congrats on finishing, that's one of the hardest parts.

The Eventmanager type pattern is pretty common.  Main thing to watch out for is if you have a ton of stuff hooked up to an event, and you process your events as they come, you can get a sudden drop in FPS.  One way to work around that is to have the events go in a queue, and then have the event manager go through some number of events per update.

Congratulations!!

Well done.  Fingers crossed for you!

cool beans, now I gotta play!

Sometimes UML charts can only display small sections of things to prevent poor illustration.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account