SlideShare a Scribd company logo
Building a turn-based game prototype using ECS - Unite Copenhagen 2019
Building a Turn-Based
Game Prototype …
the ECS Way
Setting the stage!
3
— Not the next Civilization game
— Code focussed
— No ECS experience is fine
— Agenda
– My background
– Concepts of this talk
– Non-turn-based prototype with ECS
– Turn it turn-based
About me
4
Turn Based Games
5
Turn Based Game Loop
6
— Continuous update
— Poll all events
— Change game world
Game world continuously changes
every frame
while (true)
{
processInput();
update(elapsed);
render();
}
System A
System Y
System Z
…
…
7
— Frame != Turn
— We have continuous systems: update every frame
— We have turn based systems: update every turn
Turn Based Game Loop
Turn Based Game Loop
8
— Continuous update
— Poll all events
— Wait for player action
— Update other actors only when
player makes a move
Game world only changes in
response to player action
while (true)
{
action = processInput();
if(action != null)
{
update(elapsed);
}
render();
}
Player Action
TB System
TB System
…
— Composition vs
Inheritance
— Atomic parts
— Filtering
— Systematic design
Entity
Component
System
Unity ECS
9
DATA
DATA
Unity ECS
10
— Quite useable today
— High degree of control over gameworld (Savegames!)
— Performance and data layout
— Path into future awesomeness of Unity
Let’s prototype away!
11
Example Project Setup
12
— Unity 2019.2.6f1
— 3D Template
— Packages:
– Entities 0.1.1 [preview]
– Hybrid Renderer 0.1.1 [preview]
— Content from different assets:
– PolyWorks FullPack, GalaxyBox1, SurrounDead - Free Sample, Bitgem Texture pack
— https://github.com/floAr/UniteCPH_TurnBasedPrototypeECS
“Flying Gardener”
13
— Grid based world
— Two kind of actors:
– Player(s)
– NPCs (Evil Snails)
— 2DOF Movement
Creating actors
14
— Actors move on grid in
game world
— Current- and target
position
[Serializable]
public struct ActorComponent : IComponentData
{
public float3 position;
public float3 target_positon;
}
public void Convert(Entity entity,
EntityManager dstManager,
GameObjectConversionSystem conversionSystem)
{
float3 grid_pos = new float3(
math.floor(transform.position.x),
0,
math.floor(transform.position.z)
);
dstManager.AddComponentData(entity,
new ActorComponent()
{
position = grid_pos,
target_positon = grid_pos
});
}
Creating actors
15
— Actors move on grid in
game world
— Current- and target
position
[Serializable]
public struct ActorComponent : IComponentData
{
public float3 position;
public float3 target_positon;
}
public void Convert(Entity entity,
EntityManager dstManager,
GameObjectConversionSystem conversionSystem)
{
float3 grid_pos = new float3(
math.floor(transform.position.x),
0,
math.floor(transform.position.z)
);
dstManager.AddComponentData(entity,
new ActorComponent()
{
position = grid_pos,
target_positon = grid_pos
});
}
Creating actors
16
— Actors move on grid in
game world
— Current- and target
position
— Convert from
MonoBehaviour to
Component
[Serializable]
public struct ActorComponent : IComponentData
{
public float3 position;
public float3 target_positon;
}
public void Convert(Entity entity,
EntityManager dstManager,
GameObjectConversionSystem conversionSystem)
{
float3 grid_pos = new float3(
math.floor(transform.position.x),
0,
math.floor(transform.position.z)
);
dstManager.AddComponentData(entity,
new ActorComponent()
{
position = grid_pos,
target_positon = grid_pos
});
}
Creating actors
17
— Actors move on grid in
game world
— Current- and target
position
— Convert from
MonoBehaviour to
Component
[Serializable]
public struct ActorComponent : IComponentData
{
public float3 position;
public float3 target_positon;
}
public void Convert(Entity entity,
EntityManager dstManager,
GameObjectConversionSystem conversionSystem)
{
float3 grid_pos = new float3(
math.floor(transform.position.x),
0,
math.floor(transform.position.z)
);
dstManager.AddComponentData(entity,
new ActorComponent()
{
position = grid_pos,
target_positon = grid_pos
});
}
— Show player prefab with scripts be converted to entity
— Highlight entity debugger
[Editor / Live Code]
18
Moving actors
19
— Interpolate between
current and target
position
struct ActorSystemJob : IJobForEach<Translation, Rotation, ActorComponent>
{
public float deltaTime;
public void Execute(ref Translation translation,
ref Rotation rotation,
ref ActorComponent actor )
{
var direction = actor.target_positon - actor.position;
if (math.length(direction) > 0.1f)
{
direction = math.normalize(direction);
rotation.Value= quaternion.LookRotation(direction, math.up());
translation.Value = translation.Value + direction * deltaTime;
actor.position = translation.Value;
}
else
{
translation.Value = actor.target_positon;
actor.position = translation.Value;
}
}
}
Moving actors
20
— Interpolate between
current and target
position
— Change Translation and
Rotation accordingly
struct ActorSystemJob : IJobForEach<Translation, Rotation, ActorComponent>
{
public float deltaTime;
public void Execute(ref Translation translation,
ref Rotation rotation,
ref ActorComponent actor )
{
var direction = actor.target_positon - actor.position;
if (math.length(direction) > 0.1f)
{
direction = math.normalize(direction);
rotation.Value= quaternion.LookRotation(direction, math.up());
translation.Value = translation.Value + direction * deltaTime;
actor.position = translation.Value;
}
else
{
translation.Value = actor.target_positon;
actor.position = translation.Value;
}
}
}
Moving actors
21
— Interpolate between
current and target
position
— Change Translation and
Rotation accordingly
— Execute on all entities
with Translation,
Rotation and
ActorComponent
struct ActorSystemJob : IJobForEach<Translation, Rotation, ActorComponent>
{
public float deltaTime;
public void Execute(ref Translation translation,
ref Rotation rotation,
ref ActorComponent actor )
{
var direction = actor.target_positon - actor.position;
if (math.length(direction) > 0.1f)
{
direction = math.normalize(direction);
rotation.Value= quaternion.LookRotation(direction, math.up());
translation.Value = translation.Value + direction * deltaTime;
actor.position = translation.Value;
}
else
{
translation.Value = actor.target_positon;
actor.position = translation.Value;
}
}
}
Moving actors
22
— Interpolate between
current and target
position
— Change Translation and
Rotation accordingly
— Execute on all entities
with Translation,
Rotation and
ActorComponent
struct ActorSystemJob : IJobForEach<Translation, Rotation, ActorComponent>
{
public float deltaTime;
public void Execute(ref Translation translation,
ref Rotation rotation,
ref ActorComponent actor )
{
var direction = actor.target_positon - actor.position;
if (math.length(direction) > 0.1f)
{
direction = math.normalize(direction);
rotation.Value= quaternion.LookRotation(direction, math.up());
translation.Value = translation.Value + direction * deltaTime;
actor.position = translation.Value;
}
else
{
translation.Value = actor.target_positon;
actor.position = translation.Value;
}
}
}
Moving actors
23
— Interpolate between
current and target
position
— Change Translation and
Rotation accordingly
— Execute on all entities
with Translation,
Rotation and
ActorComponent
— Pipe external data in
and schedule the job
public class ActorSystem : JobComponentSystem
{
[BurstCompile]
struct ActorSystemJob
{ […] }
protected override JobHandle OnUpdate(JobHandle inputDependencies)
{
var job = new ActorSystemJob();
job.deltaTime = UnityEngine.Time.deltaTime;
return job.Schedule(this, inputDependencies);
}
}
public struct MoveIntention : IComponentData
{
public int2 direction_xz;
}
protected override void OnUpdate()
{
Entities.WithAll<ActorComponent>().ForEach((Entity id) =>
{
var direction = generateMove();
var intent = new MoveIntention()
{
direction_xz = direction
};
PostUpdateCommands.AddComponent<MoveIntention>(id, intent);
});
}
Moving actors
24
— Create MoveIntention
public struct MoveIntention : IComponentData
{
public int2 direction_xz;
}
protected override void OnUpdate()
{
Entities.WithAll<ActorComponent>().ForEach((Entity id) =>
{
var direction = generateMove();
var intent = new MoveIntention()
{
direction_xz = direction
};
PostUpdateCommands.AddComponent<MoveIntention>(id, intent);
});
}
Moving actors
25
— Create MoveIntention
public struct MoveIntention : IComponentData
{
public int2 direction_xz;
}
protected override void OnUpdate()
{
Entities.WithAll<ActorComponent>().ForEach((Entity id) =>
{
var direction = generateMove();
var intent = new MoveIntention()
{
direction_xz = direction
};
PostUpdateCommands.AddComponent<MoveIntention>(id, intent);
});
}
Moving actors
26
— Create MoveIntention
— System that generate
random intentions (for
our non-player actors)
public struct MoveIntention : IComponentData
{
public int2 direction_xz;
}
protected override void OnUpdate()
{
Entities.WithAll<ActorComponent>().ForEach((Entity id) =>
{
var direction = generateMove();
var intent = new MoveIntention()
{
direction_xz = direction
};
PostUpdateCommands.AddComponent<MoveIntention>(id, intent);
});
}
Moving actors
27
— Create MoveIntention
— System that generate
random intentions (for
our non-player actors)
public struct MoveIntention : IComponentData
{
public int2 direction_xz;
}
protected override void OnUpdate()
{
Entities.WithAll<ActorComponent>().ForEach((Entity id) =>
{
var direction = generateMove();
var intent = new MoveIntention()
{
direction_xz = direction
};
PostUpdateCommands.AddComponent<MoveIntention>(id, intent);
});
}
Moving actors
28
— Create MoveIntention
— System that generate
random intentions (for
our non-player actors)
public struct MoveIntention : IComponentData
{
public int2 direction_xz;
}
protected override void OnUpdate()
{
Entities.WithAll<ActorComponent>().ForEach((Entity id) =>
{
var direction = generateMove();
var intent = new MoveIntention()
{
direction_xz = direction
};
PostUpdateCommands.AddComponent<MoveIntention>(id, intent);
});
}
Moving actors
29
— Create MoveIntention
— System that generate
random intentions (for
our non-player actors)
— Component changes
need synchronization
public struct MoveIntention : IComponentData
{
public int2 direction_xz;
}
protected override void OnUpdate()
{
Entities.WithAll<ActorComponent>().ForEach((Entity id) =>
{
var direction = generateMove();
var intent = new MoveIntention()
{
direction_xz = direction
};
PostUpdateCommands.AddComponent<MoveIntention>(id, intent);
});
}
Moving actors
30
— Create MoveIntention
— System that generate
random intentions (for
our non-player actors)
— Component changes
need synchronization
New scene -> grid + player + enemies
[Editor / Live Code]
31
— Player input / way to pass the turn
— Communicate between systems when allowed to run
— Variant 1: Manual control
— Variant 2: Component based control
Changes:
— PlayerComponent
— InputSystem: WASD -> MoveIntention
Bringing things in order. The player update
lock
32
Show InputSystem
Filter Player Entities from RandomMoveSystem
[Editor / Live Code]
33
Variant 1: Manual Update
34
— Remove turn-based systems from
ECS update loop
— Manually trigger update after
each turn
— [DisableAutoCreation] of turn
based systems
— World.GetOrCreateSystem()
— System.Update()
TB System
TB System
Player Action
…
Variant 1: Manual Update
35
— Remove turn-based systems from
ECS update loop
— Manually trigger update after
each turn
— [DisableAutoCreation] of turn
based systems
— World.GetOrCreateSystem()
— System.Update()
TB System
TB System
Player Action
…
[Disable on Load] Move and RandomMove
Manually call them from TurnBasedGameLoop
[Editor / Live Code]
36
Variant 1: Manual Update
37
One column body text
lorem ipsum dolor sit
amet, consectetur
adipiscing elit. Nunc
lacinia, nisi ac vehicula
pellentesque, justo tellus
dignissim velit, nec
rhoncus tellus lorem id
sapien.
Fine grained control
Low footprint
Manual effort for each system
Does not scale well
Variant 2: Interlocked Components
38
— Components to communicate
— Player has AwaitAction
— InputSystem consumes
AwaitAction
— If no player is waiting,
TurnBasedSystem hand out
ReadyToHandle flag
— After one frame every other
system updated so we remove
the flag again
Player Action
TB System
TB System
…
Variant 2: Interlocked Components
39
— Components to communicate
— Player has AwaitAction
— InputSystem consumes
AwaitAction
— If no player is waiting,
TurnBasedSystem hand out
ReadyToHandle flag
— After one frame every other
system updated so we remove
the flag again
Player Action
TB System
TB System
…
[Editor / Live Code]
40
Player gets await flag
Input system consumes await flag
Talk about Query
If no one is waiting:
Hand out ready to handle tokens
Refresh await action token
Include handle token in move system
Variant 2: Interlocked Components
41
One column body text
lorem ipsum dolor sit
amet, consectetur
adipiscing elit. Nunc
lacinia, nisi ac vehicula
pellentesque, justo tellus
dignissim velit, nec
rhoncus tellus lorem id
sapien.
Scales nicely
Frontloading effort
Extensible
Multiple players out of the box
Complexity
Order related problems
What could be next?
42
— Gameplay!
— Alternative turn modes: Timed, multiple actions
— Integrate awesome Unity packages:
– Deterministic, stateless Physics
– Mulitplayer
– Live Play
– …
Recap
43
— ECS building blocks
— MonoBehaviours ECS
— Turn based game loop
— Player action triggers world
change
— Variants of turn based systems
Thank you!
Florian Uhde
@florianuhde
florian.uhde@posteo.de
44
Bonus Slide! Local Multiplayer
— Give ID to PlayerComponent
— Filter InputSystem based on ID
— MoveSystem -> WithAny
Bonus Slide! Update Groups
46
— Use attributes to sort systems
into update order
— [UpdateAfter] [UpdateBefore]
Player Action
TB System
TB System
…

More Related Content

PPTX
Game Project / Working with Unity
PDF
MVC Seminar Presantation
PDF
Technical Deep Dive into the New Prefab System
PPTX
Snake game powerpoint presentation by rohit malav
PPTX
Game Development Step by Step
PDF
Pitfalls of Object Oriented Programming by SONY
PPTX
Game Architecture and Programming
PPT
SIGGRAPH 2012: NVIDIA OpenGL for 2012
Game Project / Working with Unity
MVC Seminar Presantation
Technical Deep Dive into the New Prefab System
Snake game powerpoint presentation by rohit malav
Game Development Step by Step
Pitfalls of Object Oriented Programming by SONY
Game Architecture and Programming
SIGGRAPH 2012: NVIDIA OpenGL for 2012

What's hot (20)

PDF
06. Game Architecture
PPTX
20 Game Ideas You Should Steal
PPTX
Game optimization techniques - Most Commons
PDF
The Guerrilla Guide to Game Code
PPTX
Game Concept
PPTX
Writing and using Hamcrest Matchers
PPTX
A Scalable Real-Time Many-Shadowed-Light Rendering System
PDF
Entity Component Systems
PDF
Run Qt on Linux embedded systems using Yocto
PPTX
Khronos Munich 2018 - Halcyon and Vulkan
PPTX
Video game proposal
PPTX
A Step Towards Data Orientation
PPTX
Scene Graphs & Component Based Game Engines
PPTX
Intrinsics: Low-level engine development with Burst - Unite Copenhagen 2019
PPTX
Optimizing the Graphics Pipeline with Compute, GDC 2016
PPTX
Scaling CPU Experiences: An Introduction to the Entity Component System
PDF
Action Bar in Android
PDF
Learn C Programming Language by Using GDB
PDF
Syysgraph 2018 - Modern Graphics Abstractions & Real-Time Ray Tracing
PDF
도우진&김영수, 게임잼 운영과 게이미피케이션, NDC2012
06. Game Architecture
20 Game Ideas You Should Steal
Game optimization techniques - Most Commons
The Guerrilla Guide to Game Code
Game Concept
Writing and using Hamcrest Matchers
A Scalable Real-Time Many-Shadowed-Light Rendering System
Entity Component Systems
Run Qt on Linux embedded systems using Yocto
Khronos Munich 2018 - Halcyon and Vulkan
Video game proposal
A Step Towards Data Orientation
Scene Graphs & Component Based Game Engines
Intrinsics: Low-level engine development with Burst - Unite Copenhagen 2019
Optimizing the Graphics Pipeline with Compute, GDC 2016
Scaling CPU Experiences: An Introduction to the Entity Component System
Action Bar in Android
Learn C Programming Language by Using GDB
Syysgraph 2018 - Modern Graphics Abstractions & Real-Time Ray Tracing
도우진&김영수, 게임잼 운영과 게이미피케이션, NDC2012
Ad

Similar to Building a turn-based game prototype using ECS - Unite Copenhagen 2019 (20)

PDF
Unity 101
PDF
An Introduction to the Unity GamingEngine
PPTX
Unity workshop
PDF
ECS architecture with Unity by example - Unite Europe 2016
PDF
Converting Scene Data to DOTS – Unite Copenhagen 2019
PDF
Entity system architecture with Unity @Unite Europe 2015
PDF
Entity System Architecture with Unity - Unite Europe 2015
PDF
How tomakea gameinunity3d
PDF
School For Games 2015 - Unity Engine Basics
PDF
Unity 2018からのハイパフォーマンスな機能紹介
PDF
【Unite 2017 Tokyo】C#ジョブシステムによるモバイルゲームのパフォーマンス向上テクニック
PDF
玉転がしゲームで学ぶUnity入門
PPTX
Unity programming 1
PDF
2 d gameplaytutorial
PPTX
Unity - Building your first real-time 3D project
PDF
intern.pdf
PDF
Extending the Animation Rigging package with C# – Unite Copenhagen 2019
PDF
Game Models - A Different Approach
PPTX
Unity - Building Your First Real-Time 3D Project - All Slides
PDF
Unreal Engine Basics 02 - Unreal Editor
Unity 101
An Introduction to the Unity GamingEngine
Unity workshop
ECS architecture with Unity by example - Unite Europe 2016
Converting Scene Data to DOTS – Unite Copenhagen 2019
Entity system architecture with Unity @Unite Europe 2015
Entity System Architecture with Unity - Unite Europe 2015
How tomakea gameinunity3d
School For Games 2015 - Unity Engine Basics
Unity 2018からのハイパフォーマンスな機能紹介
【Unite 2017 Tokyo】C#ジョブシステムによるモバイルゲームのパフォーマンス向上テクニック
玉転がしゲームで学ぶUnity入門
Unity programming 1
2 d gameplaytutorial
Unity - Building your first real-time 3D project
intern.pdf
Extending the Animation Rigging package with C# – Unite Copenhagen 2019
Game Models - A Different Approach
Unity - Building Your First Real-Time 3D Project - All Slides
Unreal Engine Basics 02 - Unreal Editor
Ad

More from Unity Technologies (20)

PDF
Build Immersive Worlds in Virtual Reality
PDF
Augmenting reality: Bring digital objects into the real world
PDF
Let’s get real: An introduction to AR, VR, MR, XR and more
PDF
Using synthetic data for computer vision model training
PDF
The Tipping Point: How Virtual Experiences Are Transforming Global Industries
PDF
Unity Roadmap 2020: Live games
PDF
Unity Roadmap 2020: Core Engine & Creator Tools
PDF
How ABB shapes the future of industry with Microsoft HoloLens and Unity - Uni...
PPTX
Unity XR platform has a new architecture – Unite Copenhagen 2019
PDF
Turn Revit Models into real-time 3D experiences
PDF
How Daimler uses mobile mixed realities for training and sales - Unite Copenh...
PDF
How Volvo embraced real-time 3D and shook up the auto industry- Unite Copenha...
PDF
QA your code: The new Unity Test Framework – Unite Copenhagen 2019
PDF
Engineering.com webinar: Real-time 3D and digital twins: The power of a virtu...
PDF
Supplying scalable VR training applications with Innoactive - Unite Copenhage...
PDF
XR and real-time 3D in automotive digital marketing strategies | Visionaries ...
PDF
Real-time CG animation in Unity: unpacking the Sherman project - Unite Copenh...
PDF
Creating next-gen VR and MR experiences using Varjo VR-1 and XR-1 - Unite Cop...
PDF
What's ahead for film and animation with Unity 2020 - Unite Copenhagen 2019
PDF
How to Improve Visual Rendering Quality in VR - Unite Copenhagen 2019
Build Immersive Worlds in Virtual Reality
Augmenting reality: Bring digital objects into the real world
Let’s get real: An introduction to AR, VR, MR, XR and more
Using synthetic data for computer vision model training
The Tipping Point: How Virtual Experiences Are Transforming Global Industries
Unity Roadmap 2020: Live games
Unity Roadmap 2020: Core Engine & Creator Tools
How ABB shapes the future of industry with Microsoft HoloLens and Unity - Uni...
Unity XR platform has a new architecture – Unite Copenhagen 2019
Turn Revit Models into real-time 3D experiences
How Daimler uses mobile mixed realities for training and sales - Unite Copenh...
How Volvo embraced real-time 3D and shook up the auto industry- Unite Copenha...
QA your code: The new Unity Test Framework – Unite Copenhagen 2019
Engineering.com webinar: Real-time 3D and digital twins: The power of a virtu...
Supplying scalable VR training applications with Innoactive - Unite Copenhage...
XR and real-time 3D in automotive digital marketing strategies | Visionaries ...
Real-time CG animation in Unity: unpacking the Sherman project - Unite Copenh...
Creating next-gen VR and MR experiences using Varjo VR-1 and XR-1 - Unite Cop...
What's ahead for film and animation with Unity 2020 - Unite Copenhagen 2019
How to Improve Visual Rendering Quality in VR - Unite Copenhagen 2019

Recently uploaded (20)

DOCX
The AUB Centre for AI in Media Proposal.docx
PDF
Network Security Unit 5.pdf for BCA BBA.
PPTX
Big Data Technologies - Introduction.pptx
PDF
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
PDF
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
PDF
Advanced methodologies resolving dimensionality complications for autism neur...
PPT
Teaching material agriculture food technology
PPTX
Cloud computing and distributed systems.
PPTX
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx
PDF
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
PDF
Spectral efficient network and resource selection model in 5G networks
PPTX
20250228 LYD VKU AI Blended-Learning.pptx
PPTX
Spectroscopy.pptx food analysis technology
PDF
Reach Out and Touch Someone: Haptics and Empathic Computing
PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
PDF
Profit Center Accounting in SAP S/4HANA, S4F28 Col11
PDF
Review of recent advances in non-invasive hemoglobin estimation
PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PPTX
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
The AUB Centre for AI in Media Proposal.docx
Network Security Unit 5.pdf for BCA BBA.
Big Data Technologies - Introduction.pptx
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
Advanced methodologies resolving dimensionality complications for autism neur...
Teaching material agriculture food technology
Cloud computing and distributed systems.
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
Spectral efficient network and resource selection model in 5G networks
20250228 LYD VKU AI Blended-Learning.pptx
Spectroscopy.pptx food analysis technology
Reach Out and Touch Someone: Haptics and Empathic Computing
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
Profit Center Accounting in SAP S/4HANA, S4F28 Col11
Review of recent advances in non-invasive hemoglobin estimation
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy

Building a turn-based game prototype using ECS - Unite Copenhagen 2019

  • 2. Building a Turn-Based Game Prototype … the ECS Way
  • 3. Setting the stage! 3 — Not the next Civilization game — Code focussed — No ECS experience is fine — Agenda – My background – Concepts of this talk – Non-turn-based prototype with ECS – Turn it turn-based
  • 6. Turn Based Game Loop 6 — Continuous update — Poll all events — Change game world Game world continuously changes every frame while (true) { processInput(); update(elapsed); render(); } System A System Y System Z … …
  • 7. 7 — Frame != Turn — We have continuous systems: update every frame — We have turn based systems: update every turn Turn Based Game Loop
  • 8. Turn Based Game Loop 8 — Continuous update — Poll all events — Wait for player action — Update other actors only when player makes a move Game world only changes in response to player action while (true) { action = processInput(); if(action != null) { update(elapsed); } render(); } Player Action TB System TB System …
  • 9. — Composition vs Inheritance — Atomic parts — Filtering — Systematic design Entity Component System Unity ECS 9 DATA DATA
  • 10. Unity ECS 10 — Quite useable today — High degree of control over gameworld (Savegames!) — Performance and data layout — Path into future awesomeness of Unity
  • 12. Example Project Setup 12 — Unity 2019.2.6f1 — 3D Template — Packages: – Entities 0.1.1 [preview] – Hybrid Renderer 0.1.1 [preview] — Content from different assets: – PolyWorks FullPack, GalaxyBox1, SurrounDead - Free Sample, Bitgem Texture pack — https://github.com/floAr/UniteCPH_TurnBasedPrototypeECS
  • 13. “Flying Gardener” 13 — Grid based world — Two kind of actors: – Player(s) – NPCs (Evil Snails) — 2DOF Movement
  • 14. Creating actors 14 — Actors move on grid in game world — Current- and target position [Serializable] public struct ActorComponent : IComponentData { public float3 position; public float3 target_positon; } public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem) { float3 grid_pos = new float3( math.floor(transform.position.x), 0, math.floor(transform.position.z) ); dstManager.AddComponentData(entity, new ActorComponent() { position = grid_pos, target_positon = grid_pos }); }
  • 15. Creating actors 15 — Actors move on grid in game world — Current- and target position [Serializable] public struct ActorComponent : IComponentData { public float3 position; public float3 target_positon; } public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem) { float3 grid_pos = new float3( math.floor(transform.position.x), 0, math.floor(transform.position.z) ); dstManager.AddComponentData(entity, new ActorComponent() { position = grid_pos, target_positon = grid_pos }); }
  • 16. Creating actors 16 — Actors move on grid in game world — Current- and target position — Convert from MonoBehaviour to Component [Serializable] public struct ActorComponent : IComponentData { public float3 position; public float3 target_positon; } public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem) { float3 grid_pos = new float3( math.floor(transform.position.x), 0, math.floor(transform.position.z) ); dstManager.AddComponentData(entity, new ActorComponent() { position = grid_pos, target_positon = grid_pos }); }
  • 17. Creating actors 17 — Actors move on grid in game world — Current- and target position — Convert from MonoBehaviour to Component [Serializable] public struct ActorComponent : IComponentData { public float3 position; public float3 target_positon; } public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem) { float3 grid_pos = new float3( math.floor(transform.position.x), 0, math.floor(transform.position.z) ); dstManager.AddComponentData(entity, new ActorComponent() { position = grid_pos, target_positon = grid_pos }); }
  • 18. — Show player prefab with scripts be converted to entity — Highlight entity debugger [Editor / Live Code] 18
  • 19. Moving actors 19 — Interpolate between current and target position struct ActorSystemJob : IJobForEach<Translation, Rotation, ActorComponent> { public float deltaTime; public void Execute(ref Translation translation, ref Rotation rotation, ref ActorComponent actor ) { var direction = actor.target_positon - actor.position; if (math.length(direction) > 0.1f) { direction = math.normalize(direction); rotation.Value= quaternion.LookRotation(direction, math.up()); translation.Value = translation.Value + direction * deltaTime; actor.position = translation.Value; } else { translation.Value = actor.target_positon; actor.position = translation.Value; } } }
  • 20. Moving actors 20 — Interpolate between current and target position — Change Translation and Rotation accordingly struct ActorSystemJob : IJobForEach<Translation, Rotation, ActorComponent> { public float deltaTime; public void Execute(ref Translation translation, ref Rotation rotation, ref ActorComponent actor ) { var direction = actor.target_positon - actor.position; if (math.length(direction) > 0.1f) { direction = math.normalize(direction); rotation.Value= quaternion.LookRotation(direction, math.up()); translation.Value = translation.Value + direction * deltaTime; actor.position = translation.Value; } else { translation.Value = actor.target_positon; actor.position = translation.Value; } } }
  • 21. Moving actors 21 — Interpolate between current and target position — Change Translation and Rotation accordingly — Execute on all entities with Translation, Rotation and ActorComponent struct ActorSystemJob : IJobForEach<Translation, Rotation, ActorComponent> { public float deltaTime; public void Execute(ref Translation translation, ref Rotation rotation, ref ActorComponent actor ) { var direction = actor.target_positon - actor.position; if (math.length(direction) > 0.1f) { direction = math.normalize(direction); rotation.Value= quaternion.LookRotation(direction, math.up()); translation.Value = translation.Value + direction * deltaTime; actor.position = translation.Value; } else { translation.Value = actor.target_positon; actor.position = translation.Value; } } }
  • 22. Moving actors 22 — Interpolate between current and target position — Change Translation and Rotation accordingly — Execute on all entities with Translation, Rotation and ActorComponent struct ActorSystemJob : IJobForEach<Translation, Rotation, ActorComponent> { public float deltaTime; public void Execute(ref Translation translation, ref Rotation rotation, ref ActorComponent actor ) { var direction = actor.target_positon - actor.position; if (math.length(direction) > 0.1f) { direction = math.normalize(direction); rotation.Value= quaternion.LookRotation(direction, math.up()); translation.Value = translation.Value + direction * deltaTime; actor.position = translation.Value; } else { translation.Value = actor.target_positon; actor.position = translation.Value; } } }
  • 23. Moving actors 23 — Interpolate between current and target position — Change Translation and Rotation accordingly — Execute on all entities with Translation, Rotation and ActorComponent — Pipe external data in and schedule the job public class ActorSystem : JobComponentSystem { [BurstCompile] struct ActorSystemJob { […] } protected override JobHandle OnUpdate(JobHandle inputDependencies) { var job = new ActorSystemJob(); job.deltaTime = UnityEngine.Time.deltaTime; return job.Schedule(this, inputDependencies); } }
  • 24. public struct MoveIntention : IComponentData { public int2 direction_xz; } protected override void OnUpdate() { Entities.WithAll<ActorComponent>().ForEach((Entity id) => { var direction = generateMove(); var intent = new MoveIntention() { direction_xz = direction }; PostUpdateCommands.AddComponent<MoveIntention>(id, intent); }); } Moving actors 24 — Create MoveIntention
  • 25. public struct MoveIntention : IComponentData { public int2 direction_xz; } protected override void OnUpdate() { Entities.WithAll<ActorComponent>().ForEach((Entity id) => { var direction = generateMove(); var intent = new MoveIntention() { direction_xz = direction }; PostUpdateCommands.AddComponent<MoveIntention>(id, intent); }); } Moving actors 25 — Create MoveIntention
  • 26. public struct MoveIntention : IComponentData { public int2 direction_xz; } protected override void OnUpdate() { Entities.WithAll<ActorComponent>().ForEach((Entity id) => { var direction = generateMove(); var intent = new MoveIntention() { direction_xz = direction }; PostUpdateCommands.AddComponent<MoveIntention>(id, intent); }); } Moving actors 26 — Create MoveIntention — System that generate random intentions (for our non-player actors)
  • 27. public struct MoveIntention : IComponentData { public int2 direction_xz; } protected override void OnUpdate() { Entities.WithAll<ActorComponent>().ForEach((Entity id) => { var direction = generateMove(); var intent = new MoveIntention() { direction_xz = direction }; PostUpdateCommands.AddComponent<MoveIntention>(id, intent); }); } Moving actors 27 — Create MoveIntention — System that generate random intentions (for our non-player actors)
  • 28. public struct MoveIntention : IComponentData { public int2 direction_xz; } protected override void OnUpdate() { Entities.WithAll<ActorComponent>().ForEach((Entity id) => { var direction = generateMove(); var intent = new MoveIntention() { direction_xz = direction }; PostUpdateCommands.AddComponent<MoveIntention>(id, intent); }); } Moving actors 28 — Create MoveIntention — System that generate random intentions (for our non-player actors)
  • 29. public struct MoveIntention : IComponentData { public int2 direction_xz; } protected override void OnUpdate() { Entities.WithAll<ActorComponent>().ForEach((Entity id) => { var direction = generateMove(); var intent = new MoveIntention() { direction_xz = direction }; PostUpdateCommands.AddComponent<MoveIntention>(id, intent); }); } Moving actors 29 — Create MoveIntention — System that generate random intentions (for our non-player actors) — Component changes need synchronization
  • 30. public struct MoveIntention : IComponentData { public int2 direction_xz; } protected override void OnUpdate() { Entities.WithAll<ActorComponent>().ForEach((Entity id) => { var direction = generateMove(); var intent = new MoveIntention() { direction_xz = direction }; PostUpdateCommands.AddComponent<MoveIntention>(id, intent); }); } Moving actors 30 — Create MoveIntention — System that generate random intentions (for our non-player actors) — Component changes need synchronization
  • 31. New scene -> grid + player + enemies [Editor / Live Code] 31
  • 32. — Player input / way to pass the turn — Communicate between systems when allowed to run — Variant 1: Manual control — Variant 2: Component based control Changes: — PlayerComponent — InputSystem: WASD -> MoveIntention Bringing things in order. The player update lock 32
  • 33. Show InputSystem Filter Player Entities from RandomMoveSystem [Editor / Live Code] 33
  • 34. Variant 1: Manual Update 34 — Remove turn-based systems from ECS update loop — Manually trigger update after each turn — [DisableAutoCreation] of turn based systems — World.GetOrCreateSystem() — System.Update() TB System TB System Player Action …
  • 35. Variant 1: Manual Update 35 — Remove turn-based systems from ECS update loop — Manually trigger update after each turn — [DisableAutoCreation] of turn based systems — World.GetOrCreateSystem() — System.Update() TB System TB System Player Action …
  • 36. [Disable on Load] Move and RandomMove Manually call them from TurnBasedGameLoop [Editor / Live Code] 36
  • 37. Variant 1: Manual Update 37 One column body text lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc lacinia, nisi ac vehicula pellentesque, justo tellus dignissim velit, nec rhoncus tellus lorem id sapien. Fine grained control Low footprint Manual effort for each system Does not scale well
  • 38. Variant 2: Interlocked Components 38 — Components to communicate — Player has AwaitAction — InputSystem consumes AwaitAction — If no player is waiting, TurnBasedSystem hand out ReadyToHandle flag — After one frame every other system updated so we remove the flag again Player Action TB System TB System …
  • 39. Variant 2: Interlocked Components 39 — Components to communicate — Player has AwaitAction — InputSystem consumes AwaitAction — If no player is waiting, TurnBasedSystem hand out ReadyToHandle flag — After one frame every other system updated so we remove the flag again Player Action TB System TB System …
  • 40. [Editor / Live Code] 40 Player gets await flag Input system consumes await flag Talk about Query If no one is waiting: Hand out ready to handle tokens Refresh await action token Include handle token in move system
  • 41. Variant 2: Interlocked Components 41 One column body text lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc lacinia, nisi ac vehicula pellentesque, justo tellus dignissim velit, nec rhoncus tellus lorem id sapien. Scales nicely Frontloading effort Extensible Multiple players out of the box Complexity Order related problems
  • 42. What could be next? 42 — Gameplay! — Alternative turn modes: Timed, multiple actions — Integrate awesome Unity packages: – Deterministic, stateless Physics – Mulitplayer – Live Play – …
  • 43. Recap 43 — ECS building blocks — MonoBehaviours ECS — Turn based game loop — Player action triggers world change — Variants of turn based systems
  • 45. Bonus Slide! Local Multiplayer — Give ID to PlayerComponent — Filter InputSystem based on ID — MoveSystem -> WithAny
  • 46. Bonus Slide! Update Groups 46 — Use attributes to sort systems into update order — [UpdateAfter] [UpdateBefore] Player Action TB System TB System …