What you need to know before understanding this document
- Abstract Classes
- Overriding methods
Sometimes some people in this group ask about what’s the problem with OOP and why a lot of non-pajeet programmers try to avoid it. Let’s define the word “OOP” first: OOP has actually many components: like simple Objects, inherited methods. advanced callbacks, and other strange obscure stuff like inner classes. OOP is a mixture of good and bad stuff, then: “What’s the real problem with OOP?” Objects are a really good thing, they are very useful for advanced software, but what good programmers hate is hierarchical OOP, called class inheritance, a lot of bad developers exploit this functionality too much creating spaghetti code with unreadable functions.
Let’s make a meme example:
After this let’s talk about a serious problem about hierarchical OOP: The Diamond Problem
The Diamond Problem
Imagine you are making a game, a The Lord of The Ring game You make a classes’ structure like this one:
+Entity | +----+StaticEntity | | | +----+Tree (it has a method like .getSunlightAndGrow()) | +----+DynamicEntity (it has a method like .move()) | +----+Monster (it has a method like .bleedWhenAttacked()) | +----+Orc
There is nothing wrong with these classes, right? But, imagine if in the future you want to add something like a Treant:
It should be a child class of Monster and Tree, now it’s a language dependant problem:
Possible Solution #1
In languages like Java a class can have only one parent class, so the Treant will be the child of only Tree or Monster, and here you have a problem: if you make the Treant a child of Monster then you need to copy the .getSunlightAndGrow() method, if you make the Treant a child of Tree, then you need to copy a method like .move(). Now you are probably thinking: “What’s the problem with copying methods?” If in the future you need to change how one of these work, you need to change the code for every class where you pasted it, and maintenance will become a mess. So this solution is discarded.
Possible Solution #2
In languages like C++ and C# a class can inherit from a number of classes you want, what’s the problem with this? The problem is that there will be useless methods like .bleedWhenAttacked() from the Monster And then you will have your code scattered with multiple empty methods and you need to explain in the //comments that the below method is actually useless and does nothing. That’s a really bad design and this solution is discarded too.
Possible Solution #3
Some people suggested about the idea of using interfaces instead of parent classes, this seems a possible solution but in the end if your code is medium or big you will have a little interfaces for almost everything, like a Leggableinterface if you have a Monster with legs. Think about that, this solution is discarded but it’s very similar to the last correct solution (and to the meme image too).
The correct solution
What’s the correct solution you may ask? It’s very simple, using OOP without a hierarchical classes system! Then how to make a game like The Lord of Rings one?
Solution: Entity Component System
All the modern engines uses it, the Diamond Problem we discussed ealier is the main reason. What’s ECS? It’s very simple, the acronym says it all, let’s remake the Treant problem resolved using ECS:
ECS is made of Entities and Components and Systems:
Entity #1 Components: Name = Tree GetSunLightAndGrowComponent
Entity #2 Components: Name = Orc BleedWhenAttackedComponent MoveComponent
Entity #3 Components: Name = Treant GetSunLightAndGrowComponent MoveComponent
TL;DR about the Systems: they are the piece of the code that reads Components and do stuff with it, like rendering a Component texture or calling Component’s functions
In short you have Entities that hold components and systems that do stuff with entities and their components.
Some very good advantages of ECS, simple reasons why modern engines use it:
- If you want to create a monster that is an abomination like Orc+Tree you simply need to add the correct components without creating a mess of classes’ parenting
- Components can be added and removed at runtime, so if you want that a Tree suddenly transforms into a Treant you can simply add the MoveComponent.
- Entities can be components of other entities, useful example: think about a soldier Entity holding a sword Entity (having its own components like DamageComponent)
- Easy parallelizable: a drawing system will read TextureComponents and a physics system will calculate MassComponents
Note: The first game using ECS was Thief (1998) but it was kept private The 2002 game Dungeon Siege was the first one with a documented ECS
I strongly suggest you to read this if you want to go into further details
“What is an Entity in simple terms?” A list of Components
“What is a Component in simple terms?” A behaviour, or data, like a Texture,Name,Function, etc
“What is a System in simple terms?” A piece of code that reads the Entities’ Components and do useful stuff with it
“The drawing System, will just look at what drawable Components are there” Yes “If an Entity is deleted, it would just delete all Components attached to it” Yes
“An Entity could become a Component” True
“A Component could become an Entity” True, if it was before an Entity
“A Component could be cloned into another Entity” True
“How Components are stored inside an Entity?” HashSet, so you will have O(1) for reading and writing You don’t actually care about sorting Components
“Is an Entity without a TextureComponent useful?” Imagine something like a MonsterSpawner Entity
“Is an Entity without Components useful?” No
“Do Entities have some default Components?” This is implementation dependant, but some developers like to add some default Components like: Name and Position Simply because they are useful almost everytime (A MonsterSpawner could be made of 3 Components: Name,Position,DataAboutWhatMonstersToSpawn)
“Could an Entity be a Component of itself?” Do you really want to crash the engine?
“Could a Component be shared (referenced) between two Entities?” Yes, but be careful of concurrent write if there are multiple threads Generally it is avoided or not permitted at all