[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