Entity system
architecture
with Unity
Maxim Zaks
@icex33 | github.com/mzaks
Simon Schmid
@s_schmid | github.com/sschmid
Unity pain points
• Testability
• Code sharing
• Co-dependent logic
• Querying
• Deleting code
Testability
Code sharing
Co-dependent logic
Co-dependent logic
Co-dependent logic
Querying
Deleting code
Entitas
Match One Demo
PositionComponent
using Entitas;
public class PositionComponent : IComponent
{
public int x;
public int y;
}
GameBoardElementComponent
using Entitas;
public class GameBoardElementComponent : IComponent {}
Entity
• Add
• Replace
• Remove
Create Blocker Entity
public static Entity CreateBlocker(this Pool pool, int x, int y)
{
return pool.CreateEntity()
.IsGameBoardElement(true)
.AddPosition(x, y)
.AddResource(Res.Blocker);
}
Pool
• Create Entity
• Destroy Entity
• Get all Entities
• Get Group
Group
Performance optimization for querying
Matcher is a filter description
Get Group
_pool.GetGroup(
Matcher.AllOf(
Matcher.GameBoardElement,
Matcher.Position
)
);
+------------------+
| Pool |
|------------------|
| e e | +-----------+
| e e---|----> | Entity |
| e e | |-----------|
| e e e | | Component |
| e e | | | +-----------+
| e e | | Component-|----> | Component |
| e e e | | | |-----------|
| e e e | | Component | | Data |
+------------------+ +-----------+ +-----------+
|
|
| +-------------+
| | e | Groups
| | e e |
+---> | +------------+
| e | | |
| e | e | e |
+--------|----+ e |
| e |
| e e |
+------------+
Behaviour
System
• Start / Execute
• No State
MoveSystem
public void Execute() {
var movables = _pool.GetGroup(
Matcher.AllOf(
Matcher.Move,
Matcher.Position
));
foreach (var e in movables.GetEntities()) {
var move = e.move;
var pos = e.position;
e.ReplacePosition(pos.x, pos.y + move.speed, pos.z);
}
}
Chain of Responsibility
+------------+ +------------+ +------------+ +------------+
| | | | | | | |
| System | +---> | System | +---> | System | +---> | System |
| | | | | | | |
+------------+ +------------+ +------------+ +------------+
return new Systems()
.Add(pool.CreateSystem <GameBoardSystem> ())
.Add(pool.CreateSystem <CreateGameBoardCacheSystem> ())
.Add(pool.CreateSystem <FallSystem> ())
.Add(pool.CreateSystem <FillSystem> ())
.Add(pool.CreateSystem <ProcessInputSystem> ())
.Add(pool.CreateSystem <RemoveViewSystem> ())
.Add(pool.CreateSystem <AddViewSystem> ())
.Add(pool.CreateSystem <RenderPositionSystem> ())
.Add(pool.CreateSystem <DestroySystem> ())
.Add(pool.CreateSystem <ScoreSystem> ());
Reacting to changes in a Group
• On Entity added
• On Entity removed
ScoreLabelController
void Start() {
_pool.GetGroup(Matcher.Score).OnEntityAdded +=
(group, entity) => updateScore(entity.score.value);
updateScore(_pool.score.value);
}
void updateScore(int score) {
_label.text = "Score " + score;
}
Reactive System
• Executed only when entities in group have changed
Render Position System
public class RenderPositionSystem : IReactiveSystem {
public IMatcher GetTriggeringMatcher() {
return Matcher.AllOf(Matcher.View, Matcher.Position);
}
public GroupEventType GetEventType() {
return GroupEventType.OnEntityAdded;
}
public void Execute(Entity[] entities) {
foreach (var e in entities) {
var pos = e.position;
e.view.gameObject.transform.position = new Vector3(pos.x, pos.y);
}
}
}
Optimizations
Components
mutable vs immutable
Entity
Store components in
Dictionary vs Array
Comparison
Dictionary
e.AddComponent(new PositionComponent());
var component = e.GetComponent<PositionComponent>();
Array
e.AddComponent(new PositionComponent(), 5);
var component = (PositionComponent)e.GetComponent(5);
Code Generator
Before Code Generation
PositionComponent component;
if (e.HasComponent(ComponentIds.Position)) {
e.WillRemoveComponent(ComponentIds.Position);
component = (PositionComponent)e.GetComponent(ComponentIds.Position);
} else {
component = new PositionComponent();
}
component.x = 10;
component.y = 10;
e.ReplaceComponent(ComponentIds.Position, component);
After Code Generation
e.ReplacePosition(10, 10);
Code Generator Demo
How does it work?
Visual Debugging
Demo
Entitas is
open source
github.com/sschmid/Entitas-
CSharp
Recap
Unity pain points
• Testability
• Code sharing
• Co-dependent logic
• Querying
• Deleting code
Advantages
• Straightforward to achieve Determinism and therefore
Replay
• Simulation Speed (2x, 4x)
• Headless Simulation, just remove systems which rely on
GameObjects (render systems)
• Save Game (Serialization / Deserialization) send data to
backend on change
• For us it replaced all OO Design Patterns
Q&Atinyurl.com/entitas

Entity System Architecture with Unity - Unite Europe 2015