entity component system - for app developers

35
Entity Component Sys tem f or App developer s @iceX33

Upload: maxim-zaks

Post on 10-Jan-2017

207 views

Category:

Education


1 download

TRANSCRIPT

Page 1: Entity Component System - for App developers

Entity Component System

for App developers@iceX33

Page 2: Entity Component System - for App developers

History & Facts

4 First known game 1998 Thief: The Dark Project

4 My first encounter 2012 With ECS

4 Articles that convinced me "What is an entity system framework for game development?" by Richard Lord

4 ObjectiveC library I used: Entitas

4 Two Apps I built with Entitas UIKonf-App & Resi App

Page 3: Entity Component System - for App developers

DEMO

Page 4: Entity Component System - for App developers

Entity | Component | System----> State <------> Behaviour

Page 5: Entity Component System - for App developers

What is a Component

public protocol Component {}

public protocol UniqueComopnent : Component {}

Page 6: Entity Component System - for App developers

Component is a value type

struct TickComponent : UniqueComopnent{ let value : UInt64}struct ElixirComponent : UniqueComopnent{ let value : Float}struct ConsumeElixirComponent : Component{ let value : Int}struct PauseComponent : UniqueComopnent{}struct JumpIntTimeComponent : UniqueComopnent{ let value : UInt64}

Page 7: Entity Component System - for App developers

Entity is a collection of components

public final class Entity { private var _components : [ComponentId:Component] ...}

e.set(ConsumeElixirComponent(value:2))let amount = e.get(ConsumeElixirComponent.self)?.valuee.remove(ConsumeElixirComponent.self)

Page 8: Entity Component System - for App developers

Components = Lego pieces

Entites = things you build with them

Page 9: Entity Component System - for App developers
Page 10: Entity Component System - for App developers
Page 11: Entity Component System - for App developers
Page 12: Entity Component System - for App developers
Page 13: Entity Component System - for App developers
Page 14: Entity Component System - for App developers

let ctx = Context()

let e = ctx.createEntity()e.set(NameComponent(value:"Maxim"))

let g = ctx.getEntityGroup(NameComponent.matcher)assert(g.count == 1)

e.set(NameComponent(value:"Leo"), overwrite:true)assert(g.count == 1)

e.remove(NameComponent.self)assert(g.count == 0)

Page 15: Entity Component System - for App developers

What about behaviour

Page 16: Entity Component System - for App developers
Page 17: Entity Component System - for App developers

What is a System

public protocol ASystem : class {}

public protocol InitialiseSystem : ASystem { func initialize()}

public protocol ExecuteSystem : ASystem { func execute()}

public protocol CleanupSystem : ASystem { func cleanup()}

Page 18: Entity Component System - for App developers

System Example

class TickUpdateSystem : InitialiseSystem, ExecuteSystem { let ctx : Context init(ctx : Context) { self.ctx = ctx } func initialize() { ctx.setUniqueEntityWith(TickComponent(value: 0)) } func execute() { guard ctx.hasUniqueComponent(PauseComponent.self) == false, let currentTick = ctx.uniqueComponent(TickComponent.self)?.value else { return } ctx.setUniqueEntityWith(TickComponent(value: currentTick + 1)) }}

Page 19: Entity Component System - for App developers

Reactive System

Page 20: Entity Component System - for App developers

Reactive System

public protocol ReactiveSystem : ExecuteSystem { var collector : Collector! {get set} var limit : Int {get} func execute(input : ArraySlice<Entity>)}public extension ReactiveSystem { func execute(){ if let collector = collector { let entities = collector.pull(limit) if entities.count > 0 { execute(input: entities) } } } var limit : Int { return -1 }}

Page 21: Entity Component System - for App developers

Reactive System Exampleclass ElixirProduceSystem : InitialiseSystem, ReactiveSystem { var collector: Collector! let ctx : Context private let productionFrequency = 3 private let elixirCapacity : Float = 10 private let productionStep : Float = 0.01 private(set) var limit: Int = 1 init(ctx : Context) { self.ctx = ctx collector = Collector(group: ctx.entityGroup(TickComponent.matcher), changeType: .added) } func initialize() { ctx.setUniqueEntityWith(ElixirComponent(value: 0)) } func execute(input: ArraySlice<Entity>) { guard let tick = input.first?.get(TickComponent.self)?.value, (tick % UInt64(productionFrequency)) == 0, let elixirAmount = ctx.uniqueComponent(ElixirComponent.self)?.value else{ return } let newAmount = min(elixirCapacity, elixirAmount + productionStep) ctx.setUniqueEntityWith(ElixirComponent(value: newAmount)) }}

Page 22: Entity Component System - for App developers

What about UI

Page 23: Entity Component System - for App developers
Page 24: Entity Component System - for App developers

UI Action

@IBAction func pauseResume(_ sender: UIButton) { if ctx.hasUniqueComponent(PauseComponent.self) { ctx.destroyUniqueEntity(PauseComponent.matcher) } else { ctx.setUniqueEntityWith(PauseComponent()) }}@IBAction func consumeAction(_ sender: UIButton) { ctx.createEntity().set(ConsumeElixirComponent(value: sender.tag))}@IBAction func timeTravel(_ sender: UISlider) { ctx.setUniqueEntityWith(JumpIntTimeComponent(value: UInt64(sender.value)))}

Page 25: Entity Component System - for App developers

What about Reactive UI

Page 26: Entity Component System - for App developers
Page 27: Entity Component System - for App developers

protocol TickListener { func tickChanged(tick : UInt64)}struct TickListenerComponent : Component { let ref : TickListener}

protocol PauseListener { func pauseStateChanged(paused : Bool)}struct PauseListenerComponent : Component { let ref : PauseListener}

protocol ElixirListener { func elixirChanged(amount : Float)}struct ElixirListenerComponent : Component { let ref : ElixirListener}

Page 28: Entity Component System - for App developers

ViewController is a listener

class ViewController: UIViewController, TickListener, ElixirListener, PauseListener {

...

override func viewDidLoad() { ctx.createEntity() .set(TickListenerComponent(ref: self)) .set(ElixirListenerComponent(ref: self)) .set(PauseListenerComponent(ref: self)) }

...}

Page 29: Entity Component System - for App developers

Or if you like it less monolithic

struct ConsumeButtonController : PauseListener, ElixirListener { let consumeButton : UIButton let consumeButtonProgress: UIProgressView let ctx : Context

func pauseStateChanged(paused: Bool) { consumeButton.isEnabled = !paused }

func elixirChanged(amount: Float) { let paused = ctx.hasUniqueComponent(PauseComponent.self) consumeButton.isEnabled = consumeButton.tag <= Int(amount) && !paused consumeButtonProgress.progress = 1 - min(1, (amount / Float(consumeButton.tag))) }}

Page 30: Entity Component System - for App developers

Reactive System + UIKitclass NotifyPauseListenersSystem : ReactiveSystem { var collector: Collector! let ctx : Context var limit: Int = 1 let listeners : Group init(ctx : Context) { self.ctx = ctx collector = Collector(group: ctx.entityGroup(PauseComponent.matcher), changeType: .addedAndRemoved) listeners = ctx.entityGroup(PauseListenerComponent.matcher) }

func execute(input: ArraySlice<Entity>) { let paused = ctx.hasUniqueComponent(PauseComponent.self) for e in listeners { e.get(PauseListenerComponent.self)? .ref.pauseStateChanged(paused: paused) } }}

Page 31: Entity Component System - for App developers

Show me the code & tests

Page 32: Entity Component System - for App developers

ECSvs.

MVC | MVVM | Rx | ReSwift

Page 33: Entity Component System - for App developers

Bonus material

Page 34: Entity Component System - for App developers

Questions?@iceX33

Page 35: Entity Component System - for App developers

Thank you!