After the last posts about our component-based entity system framework called Slash I was regularly ask why one should use a component-based framework although Unity itself is already component-based.
While it is nice that Unity itself isn’t build in an object oriented way and might be even good enough for small projects or prototypes, you should really separate your game logic completely from the Unity engine in bigger projects.
If you are working on a multiplayer game which will have a dedicated server, the decision is quite easy to make. You really don’t want to run Unity on your server considering the performance overhead and additional setup, so your logic has to be pure C#.
Not as obvious as the server argument, but even Unity itself suggests to separate your logic from Unity-specific code in case you want to unit test it. If you look at their example you will see that the SpaceshipController doesn’t contain any Unity-specific code anymore and that’s the easiest way to unit test its functionality.
In a bigger project you want to be able to unit test every part of the game logic. You might not write your unit test from the start although test-driven development has its advantages. But you need at least the possibility to unit test any part of your logic in case of bugs that occur.
I saved the main reason to use your own component-based architecture above Unity’s until last. Unity exists now since 2005, that’s 10 years which is quite a long time in software engineering. One problem that engines have is that they can’t adapt their main architecture that quickly. So the pattern Unity Technologies decided for ten years ago still exists in the current versions of the engine.
As I said it is component-based already, you aggregate your game objects from any number of mono behaviours (components). This was kind of state of the art when they started. But there were new ideas that were introduced in component-based frameworks which proved to create a cleaner and more flexible code base.
The main new concept is the one of the entity systems. While in Unity both data and logic are placed inside of the components/mono behaviours, a better approach is to divide those two. In current CBES frameworks, the components just hold data, so they are plain data classes which can be serialized pretty easy for that reason.
All the logic of the game is placed inside the systems. Those systems are clearly separated from each other and just know themselves and the components they are working on. They communicate with the outside world only via events. Those events can either be incoming events that tell the system to do something (e.g. due to player input) or outgoing events that the system uses to let others know that something happened.
Due to those clear responsibilities, your game will become very modular. Each new feature has its own set of components, systems and events that it will introduce. It can react on already existing events like the game start or a bullet that hit something. Already existing features don’t care about the new feature at all. The only adjustments you might have to do are additional events from existing systems, so the new feature can listen to them. If a feature isn’t required anymore, the only thing you have to do is not adding the system to the game.
Those are the main reasons for us to keep our game logic separated from Unity-specific stuff. It takes some experience to know where to draw the line and comes with a bit of overhead as you can’t handle any presentation adjustments directly after some data changed.
But in the long run as the project grows it’s definitively worth it. You will have a cleaner architecture where it is easier to know what happens where. Want to add new data or change existing? Check out the components. Feature XYZ behaves strange? Have a look into its system, check the logic there and unit test it. A new special effect should be added when a rocket hit its target? You won’t have to touch your logic at all, but only your presentation side, so there is no chance to introduce a logical bug.
Do you have any additional or counter reasons? Let me know by posting a comment!