Events Icon

Component-based entity systems: Event-driven systems to implement the logic

This post will give you another bunch of information how to implement a component-based entity system. We talked already about the way to setup a project in a clean way and the way data is stored. Now we’ll have a look where the logic of the game has its home.

On this note I’ll also explain the event-driven way of our framework. As a system is very self-contained, events are the way to communicate with other systems and the world outside a system in general.

Adding a event-driven architecture

An event consists of a unique identifier, the event type, and an arbitrary bunch of data, the event data. For the event type we use a simple enum value. Although you could use one big enum for your event types, every feature should have its own event enum instead. This way each feature stays separated from the others. There are some general events, like Initialized. So if each feature has its own enum you won’t run into naming conflicts as well.

The event data is passed through the framework as an object variable. So it might be a simple integer value as well as whole custom class. The custom class doesn’t have to fulfill any specific requirements.

The receiver of an event has to cast the event data to the correct type. This is why we added the information about the type of the event data in the comment of each event type.

EventManager: Sending and receiving events

In the last posts I talked about two important managers already: The component manager to add, remove, initialize the components of the entities and the blueprint manager to hold the templates to create entities from. For the event handling we need another manager, fittingly called EventManager.

It contains four main methods for event handling:

  • QueueEvent  Queues a new event which is send to the listeners later
  • RegisterListener  Registeres a callback for a specific event type
  • RemoveListener  Removes a callback from a specific event type
  • ProcessEvents  Processes all queued events

 

You can think of the event manager as a generic event delegate which lets you listen for specific types of events. There is also a RegisterListener / RemoveListener  method to listen for all events. This is mainly used for debugging issues, such as logging all processed events.

Actions vs. events

In our last projects we split up the events into two groups: The standard events which are send from the systems when something happened which might by of interest for another system or the presentation of the game. And the actions which are requests from outside to start an action in the system. The actions are most of the time initiated by the user via his input.

Although the division is a pure logical one (the framework itself doesn’t care if an event is an action or not), it makes it easier to see which information go in and out of a system. The naming of an event should be chosen according to its group. Events indicate that something happened, so we use the past form (HealthChanged, WeaponFired, SomethingHappend,…). Actions are requests/orders for a system, which is reflected by the imperative (FireWeapon, UseHealthPack, DoSomething,…).

One feature, one system

Now we have all parts together to create the logic for a specific game: We can create entities which hold data in their components. And we can send events around to trigger actions and inform other features and the presentation side of the game what happened.

A system uses this ingredients to implement the logic of a specific feature. You can think of a system as a small framework on its own. You’ll have fixed parts with well-defined functions that you have to implement depending on the feature:

  • Init  Initialization
  • UpdateSystem  Per frame update

In the Init  method you have the chance to use the EventManager reference to register for events that are of interest for the feature you are implementing. This includes events from other features and actions that trigger some functionality, e.g. due to user input.

The UpdateSystem  method gives you the possibility to do some frame-by-frame stuff. You should try to implement your features as event-driven as possible. But there are some features that are better off with a regular update, like movement, collision or some timer system.

Those two methods are all you need as a foundation to implement the logic of your feature. When you start a new one, you’ll have to think about the data that each entity needs for this feature. Than you will most likely add some actions (e.g. FireWeapon), use an existing event from another feature (e.g. DamageTaken) or use the regular update to trigger the functionality of the feature.

Adding a feature to the game

Implementing the system is very independent from the rest of the game. So independent that I got to the point a bunch of times where I implemented a feature, started the game and didn’t see any changes. The code compiled correctly and I was sure that there had to be at least some effect I should see.

What I just missed was to add the system to the game! It’s a really interesting thing that you have features in your game that you can just take out and the game will run fine, just without this one feature. This makes not only the entities component-based, but the whole game is component-based. The components are the systems in this case.

To add the system to the game a single line of game.AddSystem<TSystem>()  or game.AddSystem(new TSystem())  is enough where TSystem  is the type of the system. Its Init  method will get called when the game starts and its UpdateSystem  method each frame.

To make things even easier (and not forget to add it that often anymore), we added an attribute called GameSystem  to our framework. If you flag your system class with it, the game will automatically add the system on game start via reflection.

Conclusion

Systems are the components of the game and implement its features. Due to an event-driven architecture, each system is very self-contained and independent from other systems. From within the system you have access to the EventManager  to register for events and queue events. The EntityManager  reference lets you create new entities or get the data of existing ones.

The system is the point of contact for your project. Here is where you add the game-specific code besides the game-specific components to hold the data which is used by the systems. It may take some time to become familiar with the way to split up the features. But due to the self-contained way of the systems, it’s easy to refactor a system, if you want or have to.

This concludes the main concepts of our component-based entity system framework already. One of the next posts will be a wrap-up to show how they come together in a game. If you feel already informed enough and you are interested in having a deeper look at our framework, get in touch and I can give you access to our github repository.

If you have questions or feedback, just submit a comment, I’m happy to answer all your questions and hear your opinion!

  • Pingback: CBES: Creating a component-based game - Coding with Style in Unity3D()

  • akbar

    hi.
    unfortunately i cant understand event-driven programing and when and why i should use this.
    but i know this is very good for managing game and make it more readable.
    how can i learn it?

    • coeing

      Hi Akbar,

      Event-driven programming is not only useful for managing games but instead is used in many, many different kind of games (and other software as well). The big advantage is the decoupling between different features, so they don’t influence each other and create difficult-to-find bugs. Projects get bigger and bigger, so at some point it’s not possible that a single person knows the whole project (even if he is the only dev). So it is a great thing when the features of a project are really separated and one can have a look at a feature in isolation.

      At the beginning of this article are some links which cover the basics of component-based and event driven programming: http://unity-coding.slashgames.org/component-based-entity-systems-project-setup Hope it helps! 🙂