[swift-users] Checking/getting custom objects from a collection
Adriano Ferreira
adriano.ferreira at me.com
Fri Apr 8 09:30:23 CDT 2016
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> 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/f42d8846/attachment-0001.html>
More information about the swift-users
mailing list