[swift-users] Checking/getting custom objects from a collection
Milos Rankovic
milos at milos-and-slavica.net
Fri Apr 8 10:09:56 CDT 2016
> “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 at 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 at milos-and-slavica.net <mailto:milos at 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 at me.com <mailto:adriano.ferreira at 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 at milos-and-slavica.net <mailto:milos at 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 at swift.org <mailto:swift-users at 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 at swift.org <mailto:swift-users at 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 at swift.org <mailto:swift-users at swift.org>
>>>>>> https://lists.swift.org/mailman/listinfo/swift-users <https://lists.swift.org/mailman/listinfo/swift-users>
>>>>>
>>>>> _______________________________________________
>>>>> swift-users mailing list
>>>>> swift-users at swift.org <mailto:swift-users at swift.org>
>>>>> https://lists.swift.org/mailman/listinfo/swift-users <https://lists.swift.org/mailman/listinfo/swift-users>
>>>>
>>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20160408/0cacf4c1/attachment.html>
More information about the swift-users
mailing list