In the last two posts I talked about data binding and an asset called Data Bind for Unity I created to use it with Unity. There are more important things in a game than a clean separation between logic and presentation though. But when we started our last work-for-hire project, I realized that our foundation for the game logic was already in a very complete state, so I could concentrate on other parts of the architecture.
At Slash Games Nick and I created a framework (called “Slash Framework” of course 😉 ) which we could use for a wide range of projects. The framework is based on an approach called Component-based Entity Systems or CBES.
There are quite a few great sources to learn about the basics of CBES. So I won’t bore you with the details and give you the links instead:
- Game Models – A different approach by Nick Prühs
- Game Programming – Component-Based Entity Systems by Nick Prühs
- Entities by David Chen
- Understanding Component-Entity-Systems by Boreal Games
- Evolve Your Hierarchy by Mick West
- Entity Systems are the future of MMOG development by T-Machine
- What is an entity system framework for game development? by Richard Lord
If you are completely new to this architecture, make sure to read a lot of articles about it to get the whole picture. You don’t have to take one approach to implement the architecture completely, but rather take the things you feel comfortable with first and build up slowly.
Our framework grew for the last 2-3 years and we made quite a few changes to it during that time. Nobody writes a perfect architecture from scratch 🙂
In this post I like to focus on how we set up our projects with component-based entity systems to keep in control of the growing complexity during development.
Structure a project with CBES
We participate in game jams regularly and in one last year I wanted to keep it simple and don’t use our framework which was quite big already. Very quickly it was clear that this was a mistake. Even small projects tend to become very messy very quickly. You always need a bit of structure, especially if you work in a team.
Component-based entity systems push you towards a clean structure with several advantages:
- You know where to put your code, so you can concentrate how a feature is implemented instead of thinking about the architecture
- Everybody else on your team (and yourself after a couple of days) knows where to find what. That makes bug fixing easier as well.
- During the development of a game, more and more features are added. CBES can be structured in a feature-oriented way. So most of the time you start a new feature task, you can start with some new classes especially for that feature. This prevents the introduction of bugs into existing features when adding a new one.
- Working in a team is far more safe from merging conflicts. Most of the time each team member works on one feature and as those features are located in their own classes, interferences are very minor.
Adding a new feature
When you start with a new feature of your game, you’ll have different parts to add:
The data of a feature contains configuration data, e.g. initial and maximum health points, and runtime data, e.g. current health points.
The logic is what changes the runtime data and makes the feature work as it should.
The events are used to communicate with other features and with the presentation of your game.
These parts map really well to CBES: Data goes into components, logic is done in a system and events are added by an event enum and event data classes.
So our current approach of a clean folder/namespace structure looks like this:
- Feature A
- Feature B
- Feature C
- Feature A
There are still some things which I don’t like, e.g. where to put components that are used for multiple features. But I hope that a clean solution will come to my mind soon.
When you start a new one you begin to think about which data is involved and add one or more components to hold it. You can often start with one component if you are not sure, as refactoring a component isn’t much work.
After that you typically create a new system. This system works on the component(s) you just created. Either it changes the data of those components regularly in the Update method. Or it does so on events.
Those events may come from other features, but it’s pretty likely that you’ll need some specific events for the feature as well. There is a specific type of event which we separate from the normal ones: Actions are events which are send to the system from outside to indicate that a specific actions is desired (e.g. FireAction). So we typically have two event enums: FeatureAction and FeatureEvent.
Keeping features separated
If you are done with a feature, you are done with it completely most of the time. If nothing changes from a game design perspective (and you didn’t write any bugs, of course), you don’t have to touch it again. This is really a nice way to work as you can be sure that no new bugs are introduced into that code.
Starting a new feature is almost as starting a new project. You start from scratch with some new classes that have well-defined responsibilities. This lets you concentrate completely on the implementation of the new feature.
The event driven way CBES work is the key to this workflow. It also allows for easy refactoring. So if one of your systems of components grows too big, just split them in a way it makes sense.
Fun fact: The features are so separated that we often use the Unity physics for collision detection for small projects. We could do it in an own PhysicsSystem but the other features really don’t care about it. All they require is the events when a collision occurred.
CBES is a nice architecture because it lets you split your game into a bunch of features and keep those really separated. This makes it easy to keep the growing complexity of your project under control. Features can be added, removed, reworked, (unit) tested,… all on their own without the risk of influencing other features.
As games are living creatures which tend to change quite a lot in the course of the development, CBES feel like the best solution for me to stay flexible and structured at the same time. You can check out a early version of our framework, but if you are really interested in the current version, just send me a mail. We plan to release it this year and could use some fresh pairs of eyes to see if it works for other developers as well as it does for us.
There is always room for improvement. So, do you have any good techniques that you already used successfully with CBES? Let me know in the comments!