505 lines
10 KiB
Markdown
505 lines
10 KiB
Markdown
+++
|
|
title = "Entity Component System - It's not a system"
|
|
outputs = ["Reveal"]
|
|
+++
|
|
|
|
# Entity Component System
|
|
|
|
---
|
|
|
|
## What is (an) ECS?
|
|
- {{< frag c="unnecessary" >}}
|
|
- {{< frag c="useful" >}}
|
|
- {{< frag c="not a system" >}}
|
|
|
|
{{% note %}}
|
|
|
|
Talk about structure of presentation here:
|
|
- define terms like "entity", "component", "system"
|
|
- go over other approaches to "motivate" ECS
|
|
- main 3 game engines will be mentioned (Unity, Unreal, Godot)
|
|
- talking about the level of programming engines, not games
|
|
|
|
{{% /note %}}
|
|
|
|
---
|
|
|
|
## What are entities?
|
|
[^1]
|
|
|
|
[^1]: https://br.ign.com/league-of-legends-wild-rift/89123/feature/wild-rift-tudo-sobre-o-lol-mobile ; Image: Riot Games
|
|
|
|
---
|
|
|
|
## What are entities?
|
|
[^2]
|
|
|
|
[^2]: https://pressakey.com/gameinfos,4981,screenshots,,,Age-of-Empires-Definitive-Edition,.html
|
|
|
|
---
|
|
|
|
## What are entities?
|
|
- Objects in a game (or simulation)
|
|
- Can have different behaviours
|
|
- {{< frag c="Basically everything" >}}
|
|
|
|
---
|
|
|
|
# INHERITANCE
|
|
|
|
---
|
|
|
|
{{% section %}}
|
|
|
|
{{< slide transition="none" >}}
|
|
|
|
## Inheritance
|
|
```mermaid
|
|
graph TD;
|
|
Entity-->MoveableEntity;
|
|
Entity-->PhysicsEntity;
|
|
```
|
|
|
|
{{% note %}}
|
|
|
|
Entity: Basic information like transform and mesh in our example
|
|
MoveableEntity: Entity with functions to move with and support animations
|
|
PhysicsEntity: Walls, Falling Boulders and stuff; Colliders and Gravity
|
|
|
|
{{% /note %}}
|
|
|
|
---
|
|
|
|
{{< slide transition="none" >}}
|
|
|
|
## Inheritance
|
|
```mermaid
|
|
graph TD;
|
|
Entity-->MoveableEntity;
|
|
Entity-->PhysicsEntity;
|
|
MoveableEntity-->Character;
|
|
PhysicsEntity-->BoxColliderEntity;
|
|
```
|
|
|
|
{{% note %}}
|
|
|
|
Entity: Basic information like transform and mesh in our example
|
|
MoveableEntity: Entity with functions to move with and support animations
|
|
PhysicsEntity: Walls, Falling Boulders and stuff; Colliders and Gravity
|
|
|
|
{{% /note %}}
|
|
|
|
---
|
|
|
|
{{< slide transition="none" >}}
|
|
|
|
## Inheritance
|
|
```mermaid
|
|
graph TD;
|
|
Entity-->MoveableEntity;
|
|
Entity-->PhysicsEntity;
|
|
MoveableEntity-->Character;
|
|
Character-->Player;
|
|
Character-->NPC;
|
|
PhysicsEntity-->BoxColliderEntity;
|
|
```
|
|
|
|
{{% note %}}
|
|
|
|
Entity: Basic information like transform and mesh in our example
|
|
MoveableEntity: Entity with functions to move with and support animations
|
|
PhysicsEntity: Walls, Falling Boulders and stuff; Colliders and Gravity
|
|
|
|
{{% /note %}}
|
|
|
|
{{% /section %}}
|
|
|
|
---
|
|
|
|
## Godot
|
|

|
|
[^3]
|
|
|
|
[^3]: Screenshot: Godot Engine
|
|
|
|
---
|
|
|
|

|
|
|
|
---
|
|
|
|
## Godot Code
|
|
```cpp
|
|
class Node2D : public CanvasItem {
|
|
...
|
|
}
|
|
.
|
|
.
|
|
.
|
|
class PhysicsBody2D : public CollisionObject2D {
|
|
...
|
|
}
|
|
class CharacterBody2D : public PhysicsBody2D {
|
|
...
|
|
}
|
|
```
|
|
[^4]
|
|
|
|
[^4]: https://github.com/godotengine/godot
|
|
|
|
---
|
|
|
|
{{% section %}}
|
|
{{< slide transition="none" >}}
|
|
|
|
## Inheritance
|
|
```mermaid
|
|
graph TD;
|
|
Entity-->MoveableEntity;
|
|
Entity-->PhysicsEntity;
|
|
MoveableEntity-->Character;
|
|
Character-->Player;
|
|
Character-->NPC;
|
|
PhysicsEntity-->BoxColliderEntity;
|
|
MoveableEntity-->Vehicle;
|
|
```
|
|
|
|
---
|
|
|
|
{{< slide transition="none" >}}
|
|
|
|
## Multiple Inheritance
|
|
```mermaid
|
|
graph TD;
|
|
Entity-->MoveableEntity;
|
|
Entity-->PhysicsEntity;
|
|
MoveableEntity-->Character;
|
|
Character-->Player;
|
|
Character-->NPC;
|
|
PhysicsEntity-->BoxColliderEntity;
|
|
MoveableEntity-->Vehicle;
|
|
BoxColliderEntity-->Vehicle;
|
|
```
|
|
|
|
{{% /section %}}
|
|
|
|
---
|
|
|
|
<section data-noprocess>
|
|
<h1>Godot</h1>
|
|
<div style="display: flex; flex-direction: row; justify-content: center; align-items: center;">
|
|
<img style="margin-right: 10%;" src="Godot_player_scene_nodes.webp">
|
|
<ul>
|
|
<li class="fragment" >uses hierarchy instead</li>
|
|
<li class="fragment" >child nodes as "components"</li>
|
|
</ul>
|
|
</div>
|
|
</section>
|
|
|
|
---
|
|
|
|
## What are components?
|
|
- {{< frag c="Split entities from behaviour" >}}
|
|
- {{< frag c="Data associated with a certain \"behaviour\"" >}}
|
|
|
|
---
|
|
|
|
# COMPOSITION
|
|
|
|
---
|
|
|
|
# Composition
|
|
```mermaid
|
|
graph LR;
|
|
EntityA-->BoxColliderComponent;
|
|
EntityA-->StaticMeshComponent;
|
|
EntityB-->SphereColliderComponent;
|
|
EntityB-->DynamicMeshComponent;
|
|
EntityB-->PlayerControllerComponent;
|
|
linkStyle default stroke:lime,stroke-width:4px;
|
|
```
|
|
|
|
---
|
|
|
|
## Composition
|
|
- {{< frag c="Entities have minimal data and behaviour" >}}
|
|
- {{< frag c="Entities hold their components or references to them" >}}
|
|
|
|
{{% fragment %}}
|
|
```cpp
|
|
class Component;
|
|
|
|
class Entity {
|
|
//...
|
|
private:
|
|
std::unordered_map<std::type_info, Component*> m_components;
|
|
}
|
|
```
|
|
{{% /fragment %}}
|
|
|
|
{{% note %}}
|
|
|
|
Wanted to have a look at Unreal Source code, but...
|
|
|
|
{{% /note %}}
|
|
|
|
---
|
|
|
|

|
|
|
|
---
|
|
|
|
## Unreal
|
|
|
|
Code in Unreal's AActor class[^5]:
|
|
|
|
```cpp
|
|
/**
|
|
* All ActorComponents owned by this Actor. Stored as a Set as actors may have a large number of components
|
|
* @see GetComponents()
|
|
*/
|
|
TSet<TObjectPtr<UActorComponent>> OwnedComponents;
|
|
|
|
/**
|
|
* Get all components derived from class 'ComponentType' and fill in the OutComponents array with the result.
|
|
* It's recommended to use TArrays with a TInlineAllocator to potentially avoid memory allocation costs.
|
|
* TInlineComponentArray is defined to make this easier, for example:
|
|
* {
|
|
* TInlineComponentArray<UPrimitiveComponent*> PrimComponents(Actor);
|
|
* }
|
|
*
|
|
* @param bIncludeFromChildActors If true then recurse in to ChildActor components and find components of the appropriate type in those Actors as well
|
|
*/
|
|
template<class ComponentType, class AllocatorType>
|
|
void GetComponents(TArray<ComponentType, AllocatorType>& OutComponents, bool bIncludeFromChildActors = false) const
|
|
{
|
|
typedef TPointedToType<ComponentType> T;
|
|
|
|
OutComponents.Reset();
|
|
ForEachComponent_Internal<T>(T::StaticClass(), bIncludeFromChildActors, [&](T* InComp)
|
|
{
|
|
OutComponents.Add(InComp);
|
|
});
|
|
}
|
|
```
|
|
|
|
[^5]: Engine/Source/Runtime/Engine/Classes/GameFramework/Actor.h
|
|
|
|
{{% note %}}
|
|
|
|
next, Unity, but...
|
|
|
|
*b. Distributing Examples
|
|
You may Distribute Examples (including as modified by you) in Source Code or object code to any third party.*
|
|
|
|
{{% /note %}}
|
|
|
|
---
|
|
|
|

|
|
|
|
---
|
|
|
|
## Unity
|
|
[^6]
|
|
|
|
[^6]:https://docs.unity3d.com/6000.0/Documentation/Manual/UsingTheInspector.html
|
|
|
|
{{% note %}}
|
|
|
|
- can't confirm what Unity uses (no source found)
|
|
- interface suggests composition
|
|
- Unity DOTS?
|
|
|
|
{{% /note %}}
|
|
|
|
---
|
|
|
|

|
|
|
|
---
|
|
|
|
## What are systems?
|
|
- {{< frag c="███████████████████" >}}
|
|
- {{< frag c="█████████████████" >}}
|
|
- {{< frag c="████████████████████" >}}
|
|
|
|
{{% note %}}
|
|
We'll talk about it later.
|
|
{{% /note %}}
|
|
|
|
---
|
|
|
|
# Entity Component System
|
|
|
|
---
|
|
|
|

|
|
|
|
{{% note %}}
|
|
|
|
ECS is Data-Oriented rather than Object-Oriented
|
|
|
|
{{% /note %}}
|
|
|
|
---
|
|
|
|
## Entity Component System
|
|
- {{< frag c="split components even more from entities" >}}
|
|
- {{< frag c="entities are minimal data" >}}
|
|
- {{< frag c="components store data associated with certain behaviours" >}}
|
|
- {{< frag c="components aren't managed by their entities" >}}
|
|
|
|
{{% note %}}
|
|
|
|
-
|
|
- entities can just be ids
|
|
- component manager
|
|
|
|
|
|
{{% /note %}}
|
|
|
|
---
|
|
|
|

|
|
|
|
---
|
|
|
|
## What are systems?
|
|
- {{< frag c="main way of interacting with components" >}}
|
|
- {{< frag c="loop over every component of a type" >}}
|
|
- {{< frag c="e.g. Rendering System, Physics System..." >}}
|
|
|
|
{{% note %}}
|
|
|
|
- due to systems, want to optimise for iterating over components of a type
|
|
|
|
{{% /note %}}
|
|
|
|
---
|
|
|
|
## Examples:
|
|
|
|
- [Unity DOTS](https://unity.com/dots)
|
|
- [Bevy](https://bevyengine.org/)
|
|
- [EnTT](https://github.com/skypjack/entt)
|
|
|
|
{{% fragment %}}
|
|
|
|
> One thing known to most is that *EnTT* is also used in Minecraft.
|
|
> Given that the game is available literally everywhere, I can confidently say that the library has been sufficiently tested on every platform that can come to mind.
|
|
|
|
{{% /fragment %}}
|
|
|
|
{{% note %}}
|
|
|
|
Bevy ECS used for [Tiny Glade](https://store.steampowered.com/app/2198150/Tiny_Glade/)
|
|
|
|
{{% /note %}}
|
|
|
|
---
|
|
|
|
## Cache
|
|
|
|
CPU |L1 |L2 |L3
|
|
-------------|-------|-------|-----
|
|
Pentium N4200|96 KiB |128 KiB|2 MiB
|
|
i7 14700HX |1800 KB|28 MB |33 MB
|
|
|
|
{{% note %}}
|
|
|
|
top: silas' laptop
|
|
bottom: mine
|
|
|
|
Mine are probably also KiB/MiB, because Windows is doing Windows things
|
|
|
|
Cache Locality less important with newer cpus, but will probably still lead to benefits.
|
|
Storing data contigous should reduce cache misses, since accesses are more predictable.
|
|
Collider Component may have 3\*float64 position + 4\*float64 rotation + 3\*float64 size + 10\*float64 miscellanious = 160 Byte
|
|
300 of those = 48 KB
|
|
500 of those = 80 KB
|
|
|
|
{{% /note %}}
|
|
|
|
---
|
|
|
|
## How to implement it in C++
|
|
### Entities:
|
|
```cpp
|
|
//one possibility
|
|
typedef uint_32t UID;
|
|
|
|
typedef UID Entity;
|
|
```
|
|
|
|
---
|
|
|
|
## How to implement it in C++
|
|
### Components:
|
|
A [std::flat_map](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p0429r9.pdf) is a fitting datatype.
|
|
|
|
{{% fragment %}}
|
|
|
|
GCC libstdc++ | Clang libc++ | MSVC STL
|
|
---------------|---------------------|---------------
|
|
not implemented|partially implemented|not implemented
|
|
|
|
{{% /fragment %}}
|
|
|
|
[^7]
|
|
[^7]: https://en.cppreference.com/w/cpp/compiler\_support/23
|
|
|
|
---
|
|
|
|
## How to implement it in C++
|
|
### Components:
|
|
|
|
```cpp
|
|
class ComponentManager{
|
|
template<typename comp_type> requires std::is_base_of<Component, comp_type>
|
|
foreachComponent(std::function func){
|
|
//...
|
|
}
|
|
|
|
template<typename comp_type> requires std::is_base_of<Component, comp_type>
|
|
addComponentToEntity(Entity entity){
|
|
//...
|
|
}
|
|
|
|
//some type of ComponentMap store
|
|
}
|
|
```
|
|
|
|
{{% note %}}
|
|
|
|
possible ComponentMap store:
|
|
```cpp
|
|
std::unordered_map<std::typeid, std::size_t> m_typeOffsetMap;
|
|
void* m_componentMaps;
|
|
```
|
|
|
|
{{% /note %}}
|
|
|
|
---
|
|
|
|
## How to implement it in C++
|
|
### ComponentMap:
|
|
|
|
```cpp
|
|
template <typename component_type>
|
|
class ComponentMap
|
|
std::vector<component_type> m_values;
|
|
std::unordered_map<Entity, size_type> m_mapping;
|
|
```
|
|
|
|
---
|
|
|
|

|
|
|
|
---
|
|
|
|
## Links
|
|
- Images from [deathgenerator.com](https://deathgenerator.com)
|
|
- Helpful [thesis by Toni Härkönen](https://trepo.tuni.fi/bitstream/handle/123456789/27593/H%C3%A4rk%C3%B6nen.pdf)
|
|
- [Data Structures for Entity Systems: Contiguous memory](https://t-machine.org/index.php/2014/03/08/data-structures-for-entity-systems-contiguous-memory/)
|
|
|