Now you’re talking with components

in Design Principles

Last week we introduced the concept of components as framework for programming game objects. As opposed to last week, this week we will discuss a more technical object: how to add communication between components. I will first describe a few use cases and why we are interested, before discussing several approaches and their advantages and disadvantages.

The GitHub repository I made for this framework can still be used. I have implemented two solutions and will be referring to them when they appear in this post.

This post is part of a series of posts all discussing subjects related to component-wise game programming. To find all posts on this blog related to this subject, check this page.

What we are trying to achieve

In games components will hardly ever complete work on their own. One of the easiest example is the Collider component: If two objects collide, this will be detected by the Collider components. By the single responsibility principle (I guess you guys know how much I like this by now) the collider should not also decide what should happen with the component when such a collision occurs. Moreover, different objects might respond differently to such collision and instead of using components that inherit from colliders, we will add a way for the collider to inform the other components about something happening.

blog_msg_1

Why this is not trivial

A very common way of dealing with situations like this is using the Observer Pattern. C# in particular offers us a decent event system. We could add an OnCollision to the Collider component and subscribe to that in any of the components that need to know about this event.

There are a few problems with this approach: first of all we need direct access to the Collider event in the collision handler. We of course have the GetComponent method to find it, but this requires two things:
1. We need a reference to the GameObject to call GetComponent on;
2. The Collider has to be added to the game object before the collision handler.
Of course this is only the start of our problems: what happens if we have multiple candidates for Collider, what if the Collider is removed from the game object, possibly to be replaced by another. We would have to set up a whole bunch of bookkeeping to keep track of the colliders.

The problems become even worse if the chain of events becomes longer. The following example could be from a game where the player can kill enemies by jumping on them. The CollisionHandler could check if the collision comes from a jumping player and forward it to a DamageHandler, which might again be different for every enemy.

blog_msg_2

It becomes clear that we need a different way of implementing these kind of messages, as I will call them henceforth, to stay in line with the terminology used in Unity.

Centralising the dispatcher

The common factor in both of the solutions I will describe is that we will centralise the place from which the messages are dispatched. This is a concept I have introduced in an [earlier blog about achievements]. Instead of adding direct communication between components, we just have to make sure there is a common object that connects the components and will take responsibility of passing on messages.

Luckily we already have such an object: the GameObject. In the easiest solution we would send a message to the game object, which would then forward the message to all of its children.

blog_msg_3

This does add another responsibility to the GameObject, but I consider it a more elegant solution either way. I also prefer this solution over subscribing to an event in the GameObject class from each of the components, since this would require the game component to know the game object and involves more bookkeeping. Also internally the event system would behave pretty much identical to notifying the components from the game object directly.

Let’s take a quick look at how we would implement this: We could extend the IGameComponent interface with another method Listen which takes for example a string identifier of the event and maybe some additional parameters. This immediately brings up one of the main problems of this approach: each of the components has to filter all incoming events themselves. Especially if a component wants to respond to multiple events (think OnMouseUp, OnMouseDown as simple example) this leaves us with an undesirable mess of code. Wouldn’t it be great if we could have this done automatically?

Reflection

In the previous section I have assumed that events are identified using strings, and we will continue to assume so. If we can define what kind of messages we want to receive in the definition of the component, we can automatically filter these automatically.

Unity solves this problem using reflection. If a component has a method called OnCollision, it is pretty obvious it wants to receive messages of that type, and it is also pretty obvious what code should be executed if such a message would pass along. Since reflection allows us to peek into a class for its methods, we can easily find out if the component knows how to resolve these messages.

public void SendMessage(string msg)
{
    foreach (var c in this.components)
    {
        var type = c.GetType();
        var methodInfo = type.GetMethod(msg);
        if (methodInfo != null)
            methodInfo.Invoke(c, null);
    }
}

This snippet of code enumerates over all components looking for a method with the same name as the message. If it can find such a method, it calls it. Extending this to allow a parameter is also not very difficult.

public void SendMessage<T>(string msg, T parameter)
{
    foreach (var c in this.components)
    {
        var type = c.GetType();
        var methodInfo = type.GetMethod(msg);
        if (methodInfo != null)
            methodInfo.Invoke(c, new object[] { parameter });
    }
}

My original intention was to post this solution and be done with it, but as I did some research to sending messages in Unity, I quickly found out that this solution is far from optimal.

The big advantages of this approach is that it is incredibly easy to use. Components only have to implement a method with a given name and sending different types of messages is also incredibly easy.

The most obvious disadvantage is that it is not very type safe. Using strings as identifiers makes things easy, but also prone to errors. The messages are also not specifically linked to the methods, since the methods are called implicitly. You will end up with a set of private methods of which you are not even sure whether they are called or not by some message. What if some other programmer comes along and thinks it’s a good idea to rename the method or even completely remove them? Finally there is also no way of checking the type of the parameters, making the whole thing even more fiddly.

I was aware from the start that using reflection would never end up being completely elegant, but I became aware that the disadvantages were too big to just suck up. I still want to describe this solution however since it indicates how Unity approaches this problem, and this solution still is the most convenient to use, since it requires minimal work to set up communications between components.

A reference implementation can be found here. Remark that in this implementation I have chosen to turn the IGameComponent interface into a GameComponent abstract class and store a reference to the GameObject. This is a design choice that is unrelated to this approach, but it makes it more convenient to send messages to the game object. This approach does however has several other implications which I will probably bring up in another blog post.

Listeners

Again interfaces come to the rescue. The following approach is loosely inspired by Sebastiano MandalĂ , who in his own blog describes how the message broadcast system in Unity is wrong and proposes a solution. The biggest attention in his post is spent on dealing with the fact that game objects in Unity can be hierarchical, but his approach very much comes down to the general Listen method in each component we described earlier. The significant difference is that it only sends the messages to components that implement an IChainListener interface.

My proposed solution also heavily relies on the implementation of interfaces. The interface in particular looks as follows:

public interface IListener<T>
{
    void Listen(T message);
}

This looks very similar to the IChainListener class as proposed by Sebastiano, but instead of the Listen method, I made the entire interface generic. This means I can use types as identifiers for my event instead of strings.

Of course we need to adjust our code in the GameObject class as well:

public void SendMessage<T>(T message)
{
    foreach (var c in this.GetComponents<IListener<T>>())
        c.Listen(message);
}

A game component can now specify which events it wants to receive by implementing the right interfaces. The signature of our collision handler for example could look like this:

class CollisionHandler : IListener<CollisionEvent>

The CollisionEvent class in this case would not only be used to identify the type of event, but also to pass along any relevant information. Implementing multiple variations of the IListener interface also poses no problem.

This approach clearly deals with the issues the reflection approach brings up: it’s type safe and the private implicitly called methods are suddenly required by an interface, making sure every method is accounted for.

Is this the holy grail of component communication? Sadly not. Simple events (e.g. the MouseDown and MouseUp events again) now require a type linked to them which has no further use, since no additional parameters are necessary. This complicates adding a new type of event in case of simple events.

Of course it is also possible to use your own interfaces. We could for each type of event create our own listener interface, i.e. ICollisionListener and IMouseListener (which allows us to group multiple events). While in fact equivalent to generic IListener interfaces, we would lose the common contract these interfaces have, thus we would have to implement the enumeration through all components every time we send the message, introducing unnecessary code duplication.

An implementation of this approach can be found in this GitHub repository. I have also created the methods to use this approach in Unity in this Gist. I have not tested if these worked, so use them at your own risk. Also remark that while you can use them for your own inter-component communication, you are still dependent on using reflection for listening to the messages of the default components.

Conclusion

In this post I have introduced the problem of communicating between components. The trivial approach has some big problems, so we looked at several other solutions. By using the game object as relay for our messages we have been able to solve a lot of issues. We then looked at two ways of filtering which events are sent to a specific game component. The solution involving reflection which is used by Unity is a very convenient approach, but has several problems with robustness. These issues can be resolved by using an alternative approach based on IListener<T> interfaces, but this approach sacrifices convenience – especially for very simple messages.

I have not done any performance tests between the different approaches, but I expect that the reflection approach will probably lose out against the IListener<T> approach, but only by a small factor, unless GetComponents is optimised for retrieving components by interface quickly.

Eventually the choice on how to implement this comes down to the context and the developer’s personal preference. Small projects probably benefit more from the reflection approach, while larger projects require a more robust framework to keep everything working. There are probably even more solutions and if you have any interesting thoughts, please let me know through the comments or get in contact with me through another medium.

Coming next

Since I will be going on vacation next week, I will not have time to do further research into components, and this subject will be on hold at least until I return. I will try to write the two posts that should come out while I am away in advance so I can schedule them for automatic publication, but in particular the second post might be slightly delayed.

I am planning on touching several smaller subjects about components in a future post, but if you have anything specific you want me to discuss, please let me know.

Best wishes for the Summer!

Comments

Mathew

3 days ago

Great article!

This may sound like a silly question but when it comes to the listeners part of the article; how would I go about creating an event to listen for?

Should it just be a class, or should it inherit some special event class from the .NET framework?

Place comment

Your email address will not be published. Required fields are marked *