Checking/getting custom objects from a collection


(Adriano Ferreira) #1

Hi everyone!

I’m experimenting with Entity-Component Systems <https://en.wikipedia.org/wiki/Entity_component_system> and I’d appreciate if you could help me working on how to check/get custom objects from a collection.

The idea is to verify if an entity contains a particular component and, if so, retrieve it.

Here’s the API I’d like work on:

// Entity Library
class Character: Entity {}

// Component Library
class HealthComponent: Component {
    var health = 100.0
    var isDead = false
}

class AttackComponent: Component {
    var range = 0
    var damage = 0
}

// Usage
var healthComponent = HealthComponent()
var attackComponent = AttackComponent()

var components: [ComponentType] = [healthComponent, attackComponent]
var char = Character(components: components)

let hc = char.get(component: HealthComponent)
let ac = char.get(component: AttackComponent)

So, what are your thoughts on the TODOs below?

···

import Foundation

protocol ComponentType {
    var entity: EntityType? { get }
}

protocol EntityType {
    var components: [ComponentType] { get }
    func get<T: ComponentType>(component c: T.Type) -> T?
    func add(component c: ComponentType)
    func remove(component c: ComponentType)
}

class Component: ComponentType {
    var entity: EntityType?
}

class Entity: EntityType {
    var components = [ComponentType]()

    init(components: [ComponentType]) {
        for component in components {
            self.add(component: component)
        }
    }

    func get<T: ComponentType>(component c: T.Type) -> T? {
        // TODO: - not sure how to work the types here
        // if `self` contains component of given type, return it
        // otherwise, return nil
    }

    func add(component c: ComponentType) {
        // TODO: - depends on the `get` function
        // if `self` already contains component, just return
        // otherwise, self.components += [component]
    }

    func remove(component c: ComponentType) {
        // TODO: - also depends on the `get` function
        // if `self` contains component, remove it
        // otherwise, just return
    }
}

Best,

—A


(Jens Alfke) #2

I’m not familiar with this design pattern, but it looks like Entity would just contain an array of Component. That makes add() and remove() straightforward.

I’m not sure about your get() method. It sounds as though the implication is that an entity could only contain a single component of a given class, but that sounds awfully limiting. (If Limb is a Component, then a Spider entity needs eight of them…) I also don’t believe that Swift is dynamic enough to be able to interrogate the classes of components that way at runtime. You could implement that at a higher level by adding a `type` property to ComponentType, that returns some component-type enumeration or maybe a string, and checking component types via that property.

—Jens


(David Hart) #3

Unity, the game engine, uses a component system heavily. It uses C# and retrieves components using generic functions. And no, it doesn't force having a single instance of each component class:

http://swiftlang.ng.bluemix.net/#/repl/7be36f2d70a31da3b6ab09b7b89277a4463c23b40c28e1663e56c959a1f3eca8

Crashes the compiler, but that's how I would implement it.

···

Sent from my iPad

On 08 Apr 2016, at 02:29, Jens Alfke via swift-users <swift-users@swift.org> wrote:

I’m not familiar with this design pattern, but it looks like Entity would just contain an array of Component. That makes add() and remove() straightforward.

I’m not sure about your get() method. It sounds as though the implication is that an entity could only contain a single component of a given class, but that sounds awfully limiting. (If Limb is a Component, then a Spider entity needs eight of them…) I also don’t believe that Swift is dynamic enough to be able to interrogate the classes of components that way at runtime. You could implement that at a higher level by adding a `type` property to ComponentType, that returns some component-type enumeration or maybe a string, and checking component types via that property.

—Jens
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Milos Rankovic) #4

This is just a sketch. There may be issues down the line (I’ve indicated some with `TODO`s), but it works and you can try it in the playground:

// Swift 2.2

// utility:
extension Array {
  func first <T> (_: T.Type) -> T? {
    for e in self where e is T { return e as? T }
    return nil
  }
}

// Entity-Component System (sketch):
protocol Component { }

protocol Entity {
  static func with(_: Component...) -> Self
  // TODO: make get only
  // also should be a set-by-type really, but that would
  // force `Component` to be a class (which may be worth it)
  var components: [Component] { get set }
  init()
  func component<T: Component>(_: T.Type) -> T?
}

extension Entity {
  static func with(components: Component...) -> Self {
    var entity = self.init()
    // TODO: enforce uniquely typed elements
    entity.components = components
    return entity
  }
  func component<T: Component>(_: T.Type) -> T? {
    return self.components.first(T)
  }
}

// game:
struct Character: Entity {
  // TODO: make private
  var components: [Component] = []
}

struct Health: Component {
  var percent = 100.0
  var dead = false
}

struct Attack: Component {
  var range = 0, damage = 0
}

// use:
var health = Health()
var attack = Attack()

var character = Character.with(health, attack)

character.component(Health)?.percent // 100

Hope this helps,

milos

···

On 8 Apr 2016, at 00:47, Adriano Ferreira via swift-users <swift-users@swift.org> wrote:

Hi everyone!

I’m experimenting with Entity-Component Systems <https://en.wikipedia.org/wiki/Entity_component_system> and I’d appreciate if you could help me working on how to check/get custom objects from a collection.

The idea is to verify if an entity contains a particular component and, if so, retrieve it.

Here’s the API I’d like work on:

// Entity Library
class Character: Entity {}

// Component Library
class HealthComponent: Component {
    var health = 100.0
    var isDead = false
}

class AttackComponent: Component {
    var range = 0
    var damage = 0
}

// Usage
var healthComponent = HealthComponent()
var attackComponent = AttackComponent()

var components: [ComponentType] = [healthComponent, attackComponent]
var char = Character(components: components)

let hc = char.get(component: HealthComponent)
let ac = char.get(component: AttackComponent)

So, what are your thoughts on the TODOs below?

import Foundation

protocol ComponentType {
    var entity: EntityType? { get }
}

protocol EntityType {
    var components: [ComponentType] { get }
    func get<T: ComponentType>(component c: T.Type) -> T?
    func add(component c: ComponentType)
    func remove(component c: ComponentType)
}

class Component: ComponentType {
    var entity: EntityType?
}

class Entity: EntityType {
    var components = [ComponentType]()

    init(components: [ComponentType]) {
        for component in components {
            self.add(component: component)
        }
    }

    func get<T: ComponentType>(component c: T.Type) -> T? {
        // TODO: - not sure how to work the types here
        // if `self` contains component of given type, return it
        // otherwise, return nil
    }

    func add(component c: ComponentType) {
        // TODO: - depends on the `get` function
        // if `self` already contains component, just return
        // otherwise, self.components += [component]
    }

    func remove(component c: ComponentType) {
        // TODO: - also depends on the `get` function
        // if `self` contains component, remove it
        // otherwise, just return
    }
}

Best,

—A
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Milos Rankovic) #5

A type-uniquing alternative (see my previous message):

// Swift 2.2

// Entity-Component System (sketch):
protocol Component {
  static var name: String { get } // not necessary (see comments below)
}

extension Component {
  // can be overridden with `let` by conforming types
  static var name: String { return String(self.dynamicType) }
}

protocol Entity {
  static func with(_: Component...) -> Self
  var components: [String:Component] { get set }
  init()
  func component<T: Component>(_: T.Type) -> T?
}

extension Entity {
  static func with(components: Component...) -> Self {
    var d: [String:Component] = [:]
    for c in components { d[c.dynamicType.name/* String(c.dynamicType) */] = c }
    var entity = self.init()
    entity.components = d
    return entity
  }
  func component<T: Component>(_: T.Type) -> T? {
    return self.components[T.name/* String(T) */] as? T
  }
  // TODO: mutating func add<T: Component>(_: T)
  // TODO: mutating func remove<T: Component>(_: T.Type)
}

// game:
struct Character: Entity {
  var components: [String:Component] = [:]
}

struct Health: Component {
  var percent = 100.0
  var dead: Bool { return percent <= 0 }
}

struct Attack: Component {
  var range = 0, damage = 0
}

// use:
let health = Health()
let attack = Attack()

let character = Character.with(health, attack)

character.component(Health)?.percent // 100

milos

···

On 8 Apr 2016, at 11:05, Milos Rankovic via swift-users <swift-users@swift.org> wrote:

This is just a sketch. There may be issues down the line (I’ve indicated some with `TODO`s), but it works and you can try it in the playground:

// Swift 2.2

// utility:
extension Array {
  func first <T> (_: T.Type) -> T? {
    for e in self where e is T { return e as? T }
    return nil
  }
}

// Entity-Component System (sketch):
protocol Component { }

protocol Entity {
  static func with(_: Component...) -> Self
  // TODO: make get only
  // also should be a set-by-type really, but that would
  // force `Component` to be a class (which may be worth it)
  var components: [Component] { get set }
  init()
  func component<T: Component>(_: T.Type) -> T?
}

extension Entity {
  static func with(components: Component...) -> Self {
    var entity = self.init()
    // TODO: enforce uniquely typed elements
    entity.components = components
    return entity
  }
  func component<T: Component>(_: T.Type) -> T? {
    return self.components.first(T)
  }
}

// game:
struct Character: Entity {
  // TODO: make private
  var components: [Component] = []
}

struct Health: Component {
  var percent = 100.0
  var dead = false
}

struct Attack: Component {
  var range = 0, damage = 0
}

// use:
var health = Health()
var attack = Attack()

var character = Character.with(health, attack)

character.component(Health)?.percent // 100

Hope this helps,

milos

On 8 Apr 2016, at 00:47, Adriano Ferreira via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

Hi everyone!

I’m experimenting with Entity-Component Systems <https://en.wikipedia.org/wiki/Entity_component_system> and I’d appreciate if you could help me working on how to check/get custom objects from a collection.

The idea is to verify if an entity contains a particular component and, if so, retrieve it.

Here’s the API I’d like work on:

// Entity Library
class Character: Entity {}

// Component Library
class HealthComponent: Component {
    var health = 100.0
    var isDead = false
}

class AttackComponent: Component {
    var range = 0
    var damage = 0
}

// Usage
var healthComponent = HealthComponent()
var attackComponent = AttackComponent()

var components: [ComponentType] = [healthComponent, attackComponent]
var char = Character(components: components)

let hc = char.get(component: HealthComponent)
let ac = char.get(component: AttackComponent)

So, what are your thoughts on the TODOs below?

import Foundation

protocol ComponentType {
    var entity: EntityType? { get }
}

protocol EntityType {
    var components: [ComponentType] { get }
    func get<T: ComponentType>(component c: T.Type) -> T?
    func add(component c: ComponentType)
    func remove(component c: ComponentType)
}

class Component: ComponentType {
    var entity: EntityType?
}

class Entity: EntityType {
    var components = [ComponentType]()

    init(components: [ComponentType]) {
        for component in components {
            self.add(component: component)
        }
    }

    func get<T: ComponentType>(component c: T.Type) -> T? {
        // TODO: - not sure how to work the types here
        // if `self` contains component of given type, return it
        // otherwise, return nil
    }

    func add(component c: ComponentType) {
        // TODO: - depends on the `get` function
        // if `self` already contains component, just return
        // otherwise, self.components += [component]
    }

    func remove(component c: ComponentType) {
        // TODO: - also depends on the `get` function
        // if `self` contains component, remove it
        // otherwise, just return
    }
}

Best,

—A
_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Adriano Ferreira) #6

Milos,

Thanks for taking a look at it, I appreciate you suggestion.

It’s protocol-oriented, quite different from the idea I was trying to emulate — the one provided by GameplayKit.

Well, I tried it and it works great.

Now, would you implement those methods differently?

mutating func add<T: Component>(component: T) {
    self.components[T.name] = component
}

mutating func remove<T: Component>(_: T.Type) {
    self.components[T.name] = nil
}

Also, since the key to the components dictionary is the name, adding a component of the same type will replace the exiting one.

How would you change that so it would be possible to add some components more than once, as Jens mentioned?

Best,

—A

···

On Apr 8, 2016, at 7:12 AM, Milos Rankovic <milos@milos-and-slavica.net> wrote:

A type-uniquing alternative (see my previous message):

// Swift 2.2

// Entity-Component System (sketch):
protocol Component {
  static var name: String { get } // not necessary (see comments below)
}

extension Component {
  // can be overridden with `let` by conforming types
  static var name: String { return String(self.dynamicType) }
}

protocol Entity {
  static func with(_: Component...) -> Self
  var components: [String:Component] { get set }
  init()
  func component<T: Component>(_: T.Type) -> T?
}

extension Entity {
  static func with(components: Component...) -> Self {
    var d: [String:Component] = [:]
    for c in components { d[c.dynamicType.name/* String(c.dynamicType) */] = c }
    var entity = self.init()
    entity.components = d
    return entity
  }
  func component<T: Component>(_: T.Type) -> T? {
    return self.components[T.name/* String(T) */] as? T
  }
  // TODO: mutating func add<T: Component>(_: T)
  // TODO: mutating func remove<T: Component>(_: T.Type)
}

// game:
struct Character: Entity {
  var components: [String:Component] = [:]
}

struct Health: Component {
  var percent = 100.0
  var dead: Bool { return percent <= 0 }
}

struct Attack: Component {
  var range = 0, damage = 0
}

// use:
let health = Health()
let attack = Attack()

let character = Character.with(health, attack)

character.component(Health)?.percent // 100

milos

On 8 Apr 2016, at 11:05, Milos Rankovic via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

This is just a sketch. There may be issues down the line (I’ve indicated some with `TODO`s), but it works and you can try it in the playground:

// Swift 2.2

// utility:
extension Array {
  func first <T> (_: T.Type) -> T? {
    for e in self where e is T { return e as? T }
    return nil
  }
}

// Entity-Component System (sketch):
protocol Component { }

protocol Entity {
  static func with(_: Component...) -> Self
  // TODO: make get only
  // also should be a set-by-type really, but that would
  // force `Component` to be a class (which may be worth it)
  var components: [Component] { get set }
  init()
  func component<T: Component>(_: T.Type) -> T?
}

extension Entity {
  static func with(components: Component...) -> Self {
    var entity = self.init()
    // TODO: enforce uniquely typed elements
    entity.components = components
    return entity
  }
  func component<T: Component>(_: T.Type) -> T? {
    return self.components.first(T)
  }
}

// game:
struct Character: Entity {
  // TODO: make private
  var components: [Component] = []
}

struct Health: Component {
  var percent = 100.0
  var dead = false
}

struct Attack: Component {
  var range = 0, damage = 0
}

// use:
var health = Health()
var attack = Attack()

var character = Character.with(health, attack)

character.component(Health)?.percent // 100

Hope this helps,

milos

On 8 Apr 2016, at 00:47, Adriano Ferreira via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

Hi everyone!

I’m experimenting with Entity-Component Systems <https://en.wikipedia.org/wiki/Entity_component_system> and I’d appreciate if you could help me working on how to check/get custom objects from a collection.

The idea is to verify if an entity contains a particular component and, if so, retrieve it.

Here’s the API I’d like work on:

// Entity Library
class Character: Entity {}

// Component Library
class HealthComponent: Component {
    var health = 100.0
    var isDead = false
}

class AttackComponent: Component {
    var range = 0
    var damage = 0
}

// Usage
var healthComponent = HealthComponent()
var attackComponent = AttackComponent()

var components: [ComponentType] = [healthComponent, attackComponent]
var char = Character(components: components)

let hc = char.get(component: HealthComponent)
let ac = char.get(component: AttackComponent)

So, what are your thoughts on the TODOs below?

import Foundation

protocol ComponentType {
    var entity: EntityType? { get }
}

protocol EntityType {
    var components: [ComponentType] { get }
    func get<T: ComponentType>(component c: T.Type) -> T?
    func add(component c: ComponentType)
    func remove(component c: ComponentType)
}

class Component: ComponentType {
    var entity: EntityType?
}

class Entity: EntityType {
    var components = [ComponentType]()

    init(components: [ComponentType]) {
        for component in components {
            self.add(component: component)
        }
    }

    func get<T: ComponentType>(component c: T.Type) -> T? {
        // TODO: - not sure how to work the types here
        // if `self` contains component of given type, return it
        // otherwise, return nil
    }

    func add(component c: ComponentType) {
        // TODO: - depends on the `get` function
        // if `self` already contains component, just return
        // otherwise, self.components += [component]
    }

    func remove(component c: ComponentType) {
        // TODO: - also depends on the `get` function
        // if `self` contains component, remove it
        // otherwise, just return
    }
}

Best,

—A
_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users


(Adriano Ferreira) #7

Hi Milos,

Thanks for getting back to me this quickly.

Well, this part specifically was more of a curiosity based on Jens’ comment:

“If Limb is a Component, then a Spider entity needs eight of them…”

I also confess I’m unsure about this… so please don’t bother then.

Looking forward to hearing your thoughts on the functions though.

Cheers,

— A

···

On Apr 8, 2016, at 10:23 AM, Milos Rankovic <milos@milos-and-slavica.net> wrote:

Hi Adriano,

I’m glad if you are finding this useful. I’ll get back to you on `add` and `remove`, but just let me confirm with you: You actually want to allow multiple, say, `Health` components to be added to your `Character`? Most of the complication in my suggested code comes from trying to prevent that! Things would be simpler if this is not your requirement. I just need to double check that with you, since I can not think of a reason that would be a good thing. I expressly sketched out the implementation so that you can keep `Health` and such immutable, so that you can update the character’s state simply by `add`ing (and therefore replacing) the existing `Health` value.

milos

On 8 Apr 2016, at 15:12, Adriano Ferreira <adriano.ferreira@me.com <mailto:adriano.ferreira@me.com>> wrote:

Milos,

Thanks for taking a look at it, I appreciate you suggestion.

It’s protocol-oriented, quite different from the idea I was trying to emulate — the one provided by GameplayKit.

Well, I tried it and it works great.

Now, would you implement those methods differently?

mutating func add<T: Component>(component: T) {
    self.components[T.name] = component
}

mutating func remove<T: Component>(_: T.Type) {
    self.components[T.name] = nil
}

Also, since the key to the components dictionary is the name, adding a component of the same type will replace the exiting one.

How would you change that so it would be possible to add some components more than once, as Jens mentioned?

Best,

—A

On Apr 8, 2016, at 7:12 AM, Milos Rankovic <milos@milos-and-slavica.net <mailto:milos@milos-and-slavica.net>> wrote:

A type-uniquing alternative (see my previous message):

// Swift 2.2

// Entity-Component System (sketch):
protocol Component {
  static var name: String { get } // not necessary (see comments below)
}

extension Component {
  // can be overridden with `let` by conforming types
  static var name: String { return String(self.dynamicType) }
}

protocol Entity {
  static func with(_: Component...) -> Self
  var components: [String:Component] { get set }
  init()
  func component<T: Component>(_: T.Type) -> T?
}

extension Entity {
  static func with(components: Component...) -> Self {
    var d: [String:Component] = [:]
    for c in components { d[c.dynamicType.name/* String(c.dynamicType) */] = c }
    var entity = self.init()
    entity.components = d
    return entity
  }
  func component<T: Component>(_: T.Type) -> T? {
    return self.components[T.name/* String(T) */] as? T
  }
  // TODO: mutating func add<T: Component>(_: T)
  // TODO: mutating func remove<T: Component>(_: T.Type)
}

// game:
struct Character: Entity {
  var components: [String:Component] = [:]
}

struct Health: Component {
  var percent = 100.0
  var dead: Bool { return percent <= 0 }
}

struct Attack: Component {
  var range = 0, damage = 0
}

// use:
let health = Health()
let attack = Attack()

let character = Character.with(health, attack)

character.component(Health)?.percent // 100

milos

On 8 Apr 2016, at 11:05, Milos Rankovic via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

This is just a sketch. There may be issues down the line (I’ve indicated some with `TODO`s), but it works and you can try it in the playground:

// Swift 2.2

// utility:
extension Array {
  func first <T> (_: T.Type) -> T? {
    for e in self where e is T { return e as? T }
    return nil
  }
}

// Entity-Component System (sketch):
protocol Component { }

protocol Entity {
  static func with(_: Component...) -> Self
  // TODO: make get only
  // also should be a set-by-type really, but that would
  // force `Component` to be a class (which may be worth it)
  var components: [Component] { get set }
  init()
  func component<T: Component>(_: T.Type) -> T?
}

extension Entity {
  static func with(components: Component...) -> Self {
    var entity = self.init()
    // TODO: enforce uniquely typed elements
    entity.components = components
    return entity
  }
  func component<T: Component>(_: T.Type) -> T? {
    return self.components.first(T)
  }
}

// game:
struct Character: Entity {
  // TODO: make private
  var components: [Component] = []
}

struct Health: Component {
  var percent = 100.0
  var dead = false
}

struct Attack: Component {
  var range = 0, damage = 0
}

// use:
var health = Health()
var attack = Attack()

var character = Character.with(health, attack)

character.component(Health)?.percent // 100

Hope this helps,

milos

On 8 Apr 2016, at 00:47, Adriano Ferreira via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

Hi everyone!

I’m experimenting with Entity-Component Systems <https://en.wikipedia.org/wiki/Entity_component_system> and I’d appreciate if you could help me working on how to check/get custom objects from a collection.

The idea is to verify if an entity contains a particular component and, if so, retrieve it.

Here’s the API I’d like work on:

// Entity Library
class Character: Entity {}

// Component Library
class HealthComponent: Component {
    var health = 100.0
    var isDead = false
}

class AttackComponent: Component {
    var range = 0
    var damage = 0
}

// Usage
var healthComponent = HealthComponent()
var attackComponent = AttackComponent()

var components: [ComponentType] = [healthComponent, attackComponent]
var char = Character(components: components)

let hc = char.get(component: HealthComponent)
let ac = char.get(component: AttackComponent)

So, what are your thoughts on the TODOs below?

import Foundation

protocol ComponentType {
    var entity: EntityType? { get }
}

protocol EntityType {
    var components: [ComponentType] { get }
    func get<T: ComponentType>(component c: T.Type) -> T?
    func add(component c: ComponentType)
    func remove(component c: ComponentType)
}

class Component: ComponentType {
    var entity: EntityType?
}

class Entity: EntityType {
    var components = [ComponentType]()

    init(components: [ComponentType]) {
        for component in components {
            self.add(component: component)
        }
    }

    func get<T: ComponentType>(component c: T.Type) -> T? {
        // TODO: - not sure how to work the types here
        // if `self` contains component of given type, return it
        // otherwise, return nil
    }

    func add(component c: ComponentType) {
        // TODO: - depends on the `get` function
        // if `self` already contains component, just return
        // otherwise, self.components += [component]
    }

    func remove(component c: ComponentType) {
        // TODO: - also depends on the `get` function
        // if `self` contains component, remove it
        // otherwise, just return
    }
}

Best,

—A
_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users


(Milos Rankovic) #8

Hi Adriano,

I’m glad if you are finding this useful. I’ll get back to you on `add` and `remove`, but just let me confirm with you: You actually want to allow multiple, say, `Health` components to be added to your `Character`? Most of the complication in my suggested code comes from trying to prevent that! Things would be simpler if this is not your requirement. I just need to double check that with you, since I can not think of a reason that would be a good thing. I expressly sketched out the implementation so that you can keep `Health` and such immutable, so that you can update the character’s state simply by `add`ing (and therefore replacing) the existing `Health` value.

milos

···

On 8 Apr 2016, at 15:12, Adriano Ferreira <adriano.ferreira@me.com> wrote:

Milos,

Thanks for taking a look at it, I appreciate you suggestion.

It’s protocol-oriented, quite different from the idea I was trying to emulate — the one provided by GameplayKit.

Well, I tried it and it works great.

Now, would you implement those methods differently?

mutating func add<T: Component>(component: T) {
    self.components[T.name] = component
}

mutating func remove<T: Component>(_: T.Type) {
    self.components[T.name] = nil
}

Also, since the key to the components dictionary is the name, adding a component of the same type will replace the exiting one.

How would you change that so it would be possible to add some components more than once, as Jens mentioned?

Best,

—A


(Milos Rankovic) #9

“If Limb is a Component, then a Spider entity needs eight of them…”

That’s a fair question to ask.

My intuition is that sporting a `Component` answers a question, “does it have this feature?” In the case of self-propelling creatures, you’d probably ask, “does it have legs”. On the other hand, asking, “does it (still) have a front left leg?” assumes that in general this entity has legs you can ask questions about. All of which would suggest that `SpiderLegs` component would be sufficient for most spider games…

As for the `add` and `remove` methods, I’d probably also return the removed component (if any):

  mutating func remove<T: Component>(_: T.Type) -> T? {
    let c = self.components[T.name]
    self.components[T.name] = nil
    return c as? T
  }

milos

···

On 8 Apr 2016, at 15:30, Adriano Ferreira <adriano.ferreira@me.com> wrote:

Hi Milos,

Thanks for getting back to me this quickly.

Well, this part specifically was more of a curiosity based on Jens’ comment:

“If Limb is a Component, then a Spider entity needs eight of them…”

I also confess I’m unsure about this… so please don’t bother then.

Looking forward to hearing your thoughts on the functions though.

Cheers,

— A

On Apr 8, 2016, at 10:23 AM, Milos Rankovic <milos@milos-and-slavica.net <mailto:milos@milos-and-slavica.net>> wrote:

Hi Adriano,

I’m glad if you are finding this useful. I’ll get back to you on `add` and `remove`, but just let me confirm with you: You actually want to allow multiple, say, `Health` components to be added to your `Character`? Most of the complication in my suggested code comes from trying to prevent that! Things would be simpler if this is not your requirement. I just need to double check that with you, since I can not think of a reason that would be a good thing. I expressly sketched out the implementation so that you can keep `Health` and such immutable, so that you can update the character’s state simply by `add`ing (and therefore replacing) the existing `Health` value.

milos

On 8 Apr 2016, at 15:12, Adriano Ferreira <adriano.ferreira@me.com <mailto:adriano.ferreira@me.com>> wrote:

Milos,

Thanks for taking a look at it, I appreciate you suggestion.

It’s protocol-oriented, quite different from the idea I was trying to emulate — the one provided by GameplayKit.

Well, I tried it and it works great.

Now, would you implement those methods differently?

mutating func add<T: Component>(component: T) {
    self.components[T.name] = component
}

mutating func remove<T: Component>(_: T.Type) {
    self.components[T.name] = nil
}

Also, since the key to the components dictionary is the name, adding a component of the same type will replace the exiting one.

How would you change that so it would be possible to add some components more than once, as Jens mentioned?

Best,

—A

On Apr 8, 2016, at 7:12 AM, Milos Rankovic <milos@milos-and-slavica.net <mailto:milos@milos-and-slavica.net>> wrote:

A type-uniquing alternative (see my previous message):

// Swift 2.2

// Entity-Component System (sketch):
protocol Component {
  static var name: String { get } // not necessary (see comments below)
}

extension Component {
  // can be overridden with `let` by conforming types
  static var name: String { return String(self.dynamicType) }
}

protocol Entity {
  static func with(_: Component...) -> Self
  var components: [String:Component] { get set }
  init()
  func component<T: Component>(_: T.Type) -> T?
}

extension Entity {
  static func with(components: Component...) -> Self {
    var d: [String:Component] = [:]
    for c in components { d[c.dynamicType.name/* String(c.dynamicType) */] = c }
    var entity = self.init()
    entity.components = d
    return entity
  }
  func component<T: Component>(_: T.Type) -> T? {
    return self.components[T.name/* String(T) */] as? T
  }
  // TODO: mutating func add<T: Component>(_: T)
  // TODO: mutating func remove<T: Component>(_: T.Type)
}

// game:
struct Character: Entity {
  var components: [String:Component] = [:]
}

struct Health: Component {
  var percent = 100.0
  var dead: Bool { return percent <= 0 }
}

struct Attack: Component {
  var range = 0, damage = 0
}

// use:
let health = Health()
let attack = Attack()

let character = Character.with(health, attack)

character.component(Health)?.percent // 100

milos

On 8 Apr 2016, at 11:05, Milos Rankovic via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

This is just a sketch. There may be issues down the line (I’ve indicated some with `TODO`s), but it works and you can try it in the playground:

// Swift 2.2

// utility:
extension Array {
  func first <T> (_: T.Type) -> T? {
    for e in self where e is T { return e as? T }
    return nil
  }
}

// Entity-Component System (sketch):
protocol Component { }

protocol Entity {
  static func with(_: Component...) -> Self
  // TODO: make get only
  // also should be a set-by-type really, but that would
  // force `Component` to be a class (which may be worth it)
  var components: [Component] { get set }
  init()
  func component<T: Component>(_: T.Type) -> T?
}

extension Entity {
  static func with(components: Component...) -> Self {
    var entity = self.init()
    // TODO: enforce uniquely typed elements
    entity.components = components
    return entity
  }
  func component<T: Component>(_: T.Type) -> T? {
    return self.components.first(T)
  }
}

// game:
struct Character: Entity {
  // TODO: make private
  var components: [Component] = []
}

struct Health: Component {
  var percent = 100.0
  var dead = false
}

struct Attack: Component {
  var range = 0, damage = 0
}

// use:
var health = Health()
var attack = Attack()

var character = Character.with(health, attack)

character.component(Health)?.percent // 100

Hope this helps,

milos

On 8 Apr 2016, at 00:47, Adriano Ferreira via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

Hi everyone!

I’m experimenting with Entity-Component Systems <https://en.wikipedia.org/wiki/Entity_component_system> and I’d appreciate if you could help me working on how to check/get custom objects from a collection.

The idea is to verify if an entity contains a particular component and, if so, retrieve it.

Here’s the API I’d like work on:

// Entity Library
class Character: Entity {}

// Component Library
class HealthComponent: Component {
    var health = 100.0
    var isDead = false
}

class AttackComponent: Component {
    var range = 0
    var damage = 0
}

// Usage
var healthComponent = HealthComponent()
var attackComponent = AttackComponent()

var components: [ComponentType] = [healthComponent, attackComponent]
var char = Character(components: components)

let hc = char.get(component: HealthComponent)
let ac = char.get(component: AttackComponent)

So, what are your thoughts on the TODOs below?

import Foundation

protocol ComponentType {
    var entity: EntityType? { get }
}

protocol EntityType {
    var components: [ComponentType] { get }
    func get<T: ComponentType>(component c: T.Type) -> T?
    func add(component c: ComponentType)
    func remove(component c: ComponentType)
}

class Component: ComponentType {
    var entity: EntityType?
}

class Entity: EntityType {
    var components = [ComponentType]()

    init(components: [ComponentType]) {
        for component in components {
            self.add(component: component)
        }
    }

    func get<T: ComponentType>(component c: T.Type) -> T? {
        // TODO: - not sure how to work the types here
        // if `self` contains component of given type, return it
        // otherwise, return nil
    }

    func add(component c: ComponentType) {
        // TODO: - depends on the `get` function
        // if `self` already contains component, just return
        // otherwise, self.components += [component]
    }

    func remove(component c: ComponentType) {
        // TODO: - also depends on the `get` function
        // if `self` contains component, remove it
        // otherwise, just return
    }
}

Best,

—A
_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users


(Milos Rankovic) #10

My message bounced on account of size. I’m sorry if you receive multiple copies of this:

“If Limb is a Component, then a Spider entity needs eight of them…”

That’s a fair question to ask.

My intuition is that sporting a `Component` answers a question, “does it have this feature?” In the case of self-propelling creatures, you’d probably ask, “does it have legs”. On the other hand, asking, “does it (still) have a front left leg?” assumes that in general this entity has legs you can ask questions about. All of which would suggest that `SpiderLegs` component would be sufficient for most spider games…

As for the `add` and `remove` methods, I’d probably also return the removed component (if any):

  mutating func remove<T: Component>(_: T.Type) -> T? {
    return components.removeValueForKey(T.name) as? T
  }

milos

···

On 8 Apr 2016, at 15:30, Adriano Ferreira <adriano.ferreira@me.com> wrote:

Hi Milos,

Thanks for getting back to me this quickly.

Well, this part specifically was more of a curiosity based on Jens’ comment:

“If Limb is a Component, then a Spider entity needs eight of them…”

I also confess I’m unsure about this… so please don’t bother then.

Looking forward to hearing your thoughts on the functions though.

Cheers,

— A


(Milos Rankovic) #11

Though you are very welcome, please note that I was merely implementing the target API you asked about. A better use of Swift’s protocols would be to declare various “component” conformances for each entity type, which answers the question, “does this type of entity have this feature in general”, leaving to the entity instances to answer the question, “what is the current state of this feature?”. In all, I’d definitely reconsider GameplayKit and the related advice.

milos

···

On 8 Apr 2016, at 15:12, Adriano Ferreira <adriano.ferreira@me.com> wrote:

Milos,

Thanks for taking a look at it, I appreciate you suggestion.

It’s protocol-oriented, quite different from the idea I was trying to emulate — the one provided by GameplayKit.

Well, I tried it and it works great.