Design Patterns and Unity3D – Part One (Events Aggregator)

After working in different teams, and on different projects with Unity, I noticed couple of bad practices (in my opinion of course) – it’s the lack of code design and unified code structuring. I’ve been there with PHP couple of years ago, and if we’ll consider the amount of programmers in the world, there is a lot of “if it works – don’t touch it” guys, or “we’ll fix it later”, but “later” never comes. So I’m not surprised.

There can be a lot of reasons for this phenomenon, and I have couple of theories (not Illuminati – I promise!), but this post is about something else. Instead of ranting and bragging about it, I decided to write a series of posts about how, in my opinion, the code should and should not look. And I guess I’ll start with Design Patterns.

I’m not awarded with “Best coder of the year” award, hell I’m not even nominated and I don’t say my methods are perfect. All of the posts in these series are based on my personal opinion and my personal experience. I would love to receive criticism, advices or suggestions.

So, without further adieu, let’s start with the Events Aggregator. I suggest you this article, to understand it better. But if you don’t want to, here’s a quote:

A system with lots of objects can lead to complexities when a client wants to subscribe to events. The client has to find and register for each object individually, if each object has multiple events then each event requires a separate subscription.

An Event Aggregator acts as a single source of events for many objects. It registers for all the events of the many objects allowing clients to register with just the aggregator.

Before we’ll get into details, I would like to tell you about practices I’ve seen, and why you should not use them. In this post, I’ll present a task (and it’s solution), with minimum classes, but you should know – the more components will be in your code, the more messy it’s gonna get. How? – Let me explain.

The task:

Let’s imagine, that we need to build a tile-based field. If the user clicks on a tile – it should be highlighted, and all the others to return to their initial state. Additionally, we want to display the coordinates of the selected tile on screen.

1. Static variables

This one is very popular among Unity developers. I’ve seen it in Internet examples, and I’ve seen it in my colleague’s code. In this example, static variables are used to provide a connections between objects. Let’s solve our task with this method, and then we’ll talk why you shouldn’t do it like this.

Here’s what is going on here: Every Tile check’s is the user pressed on it, and if he does, the Tile calls to the “Instance” variables of each relevant class (in our case – the Field and the Label) to run the relevant methods.

So, what’s bad about it? I mean it looks clean and it is singletones…

  1. No, it’s not clean. When your application gonna grow, and you will have a lot more objects that rely on each other, your code will become one long spaghetti. Big amount of external calls will make your code not readable. Or worse.
  2. Tile – is a component of its own. It shouldn’t know that our scene contains other elements, like the Label or it’s part of a big Field. With this approach, every time you’ll add a class, that should be notified about selected tile – you will edit your Tile code. And in the end, your code will be a huge mess.
  3. Memory leak. Yeah, you heard right! Should you “forget” to null your “Instance”, and I promise you it will happen (it always does), your object will sit in memory while you don’t need it anymore, where no Garbage Collector can stop it. And those “ghosts” will multiply according to the size of your project. And they’ll eat, and eat, and eat system resources. Are you making a mobile game! Oh, it’s so nice!

2. Object references

This method is also popular – holding a reference to the object we need in a non static variable.

Here’s what is going on here: Unlike the previous method with the static variables, here we are holding references to the objects inside the class that needs to interact with them.

So, what is wrong with this approach?

  1. And yet again we are talking about food. What’s for dinner today? – Of course spaghetti! This method is no different from the previous in terms of spaghetti code – the only thing different is a location of the variables.
  2. And again our poor Tile should interact with others. Why can’t we leave it alone and live a happy life? Why the Tile should know about the others in the scene?
  3. Memory leak. Yes. Again. But in this case the chances to get one are much bigger. What we have here is a two-way reference. The Field class holds a list of Tiles, while each Tile holds a reference to a Field. This is really bad practice. The thing is, that Garbage Collector will collect objects, only when they are destroyed and nobody holds a reference to them. When two objects referencing each other, it leads to non destroyed objects in the memory. So those objects have a banquet. What are they eating? Spaghetti Your RAM! Did I mentioned mobile platforms?

3. Events

For some reason, this method is really unpopular among Unity developers. I wonder – they don’t know about, or just hate events?

Here we are added a delegate and events of this delegate type. Now, when the user is clicking on a tile, it will be able just to scream to the void “I’VE BEEN CLICKED”. In this approach the Tile is don’t give a damn about others – it fired it’s event, without knowing who will receive it.

It’s a good start, but not good enough. Here’s why:

  1. It’s a lot of subscriptions to maintain. If you’ll pay attention, you’ll see that the class Field is subscribed to the events of all tiles. Let us imagine that we have a 50*50 field. That’s a small, nice field. 2500 tiles. 2500 elements we subscribed to. 2500 chances that something will go wrong.  It’s not a field anymore – it’s a whore! jokes aside, we have the same probability of a memory leak and if there will be some bug/error/exception – it’ll be a lot harder to fix.
  2. Event transfer. In our case, when the Field receives an event about a clicked tile, it’s firing its own event, so the Label could update accordingly. Alternatively we could make another 2500 subscription inside a Label class, and we’ll hold a reference to a Field object. Event transfer is just making the code less readable, and harder to maintain, so better this, than another “whore” in the project.

4. Events Aggregator

At last we’re here! Let’s see our code, and then I’ll explain how it works, and why it’s better than the others.

As you can see, we added an EventsManager class. This class will serve us as Aggregator. We have the same delegate, but this time the event is one, and it’s inside the Aggregator class.

Tile, Field and Label are holding a reference to the EventsManager. When the Tile is clicked, it fires a “OnTileSelected” method inside the aggregator, which fires an event. And all subscribed objects will receive this event.

Why is it better?

  1. There is no class that knows about the others – every class is “living” it’s own life. This is logically correct and increases the readability of the code, while easing its maintenance.
  2. The only object that is referenced is the EventsManager, so the risk of memory leak is minimal.
  3. While editing one of the subscribers classes, all others remains the same.
  4. There is no endless event subscriptions.
  5. You can change the way your Aggregator works without changing it’s subscribers. For example, you could make a multi-threaded event raising.
  6. This code is a lot easier to debug

That’s it for now. I hope you found this article informative. As I already said – I would love to receive any feedback, be it about the subject, or about my poor grammar.

Поделитесь с друзьями:
  • 4

Leave a Reply