Download - UniteEurope 2015
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
EntitasMatch 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
GroupPerformance 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
Componentsmutable vs immutable
EntityStore components inDictionary vs Array
ComparisonDictionary
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 isopen 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