diff --git a/Sources/FirebladeECS/Family+Members.swift b/Sources/FirebladeECS/Family+Members.swift index b305b86..a31ca49 100644 --- a/Sources/FirebladeECS/Family+Members.swift +++ b/Sources/FirebladeECS/Family+Members.swift @@ -5,55 +5,137 @@ // Created by Christian Treffs on 20.10.17. // -public protocol FamilyIterable { - func forEachMember(_ applyToMember: (Entity) -> Void) - func forEachMember(_ applyToMember: (Entity, A) -> Void) where A: Component - func forEachMember(_ applyToMember: (Entity, A, B) -> Void) where A: Component, B: Component - func forEachMember(_ applyToMember: (Entity, A, B, C) -> Void) where A: Component, B: Component, C: Component - func forEachMember(_ applyToMember: (Entity, A, B, C, D) -> Void) where A: Component, B: Component, C: Component, D: Component - func forEachMember(_ applyToMember: (Entity, A, B, C, D, E) -> Void) where A: Component, B: Component, C: Component, D: Component, E: Component - func forEachMember(_ applyToMember: (Entity, A, B, C, D, E, F) -> Void) where A: Component, B: Component, C: Component, D: Component, E: Component, F: Component - +extension Family { + public func iterateMembers(_ apply: @escaping (EntityIdentifier, () -> Entity) -> Void) { + memberIds.forEach { (entityId: EntityIdentifier) -> Void in + func getEntity() -> Entity { + return nexus.get(entity: entityId) + } + apply(entityId, getEntity) + } + } } -extension Family/*: FamilyIterable*/ { - public func forEachMember(_ applyToMember: (Entity) -> Void) { - members.forEach { applyToMember(nexus.get(entity: $0)!) } - } +extension Family { - public func forEachMember(_ applyToMember: (Entity, () -> A?) -> Void) where A: Component { - forEachMember { (entity: Entity) in - applyToMember(entity, entity.component()) + public func iterate(components _: A.Type, _ apply: @escaping (() -> Entity, () -> A?) -> Void) + where A: Component { + iterateMembers { (entityId, getEntityInstance) in + func getComponent() -> Z? where Z: Component { + return self.nexus.get(component: Z.identifier, for: entityId) as? Z + } + apply(getEntityInstance, getComponent) } } - public func forEachMember(_ applyToMember: (Entity, () -> A?, () -> B?) -> Void) where A: Component, B: Component { - forEachMember { (entity: Entity) in - applyToMember(entity, entity.component(), entity.component()) + public func iterate(components _: A.Type, _: B.Type, _ apply: @escaping (() -> Entity, () -> A?, () -> B?) -> Void) + where A: Component, B: Component { + iterateMembers { (entityId, getEntityInstance) in + func getComponent() -> Z? where Z: Component { + return self.nexus.get(component: Z.identifier, for: entityId) as? Z + } + apply(getEntityInstance, getComponent, getComponent) } } - public func forEachMember(_ applyToMember: (Entity, () -> A?, () -> B?, () -> C?) -> Void) where A: Component, B: Component, C: Component { - forEachMember { (entity: Entity) in - applyToMember(entity, entity.component(), entity.component(), entity.component()) + public func iterate(components _: A.Type, _: B.Type, _: C.Type, _ apply: @escaping (() -> Entity, () -> A?, () -> B?, () -> C?) -> Void) + where A: Component, B: Component, C: Component { + iterateMembers { (entityId, getEntityInstance) in + func getComponent() -> Z? where Z: Component { + return self.nexus.get(component: Z.identifier, for: entityId) as? Z + } + apply(getEntityInstance, getComponent, getComponent, getComponent) } } - public func forEachMember(_ applyToMember: (Entity, () -> A?, () -> B?, () -> C?, () -> D?) -> Void) where A: Component, B: Component, C: Component, D: Component { - forEachMember { (entity: Entity) in - applyToMember(entity, entity.component(), entity.component(), entity.component(), entity.component()) + public func iterate(components _: A.Type, _: B.Type, _: C.Type, _: D.Type, _ apply: @escaping (() -> Entity, () -> A?, () -> B?, () -> C?, () -> D?) -> Void) + where A: Component, B: Component, C: Component, D: Component { + iterateMembers { (entityId, getEntityInstance) in + func getComponent() -> Z? where Z: Component { + return self.nexus.get(component: Z.identifier, for: entityId) as? Z + } + apply(getEntityInstance, getComponent, getComponent, getComponent, getComponent) + } + } + public func iterate(components _: A.Type, _: B.Type, _: C.Type, _: D.Type, _: E.Type, _ apply: @escaping (() -> Entity, () -> A?, () -> B?, () -> C?, () -> D?, () -> E?) -> Void) + where A: Component, B: Component, C: Component, D: Component, E: Component { + iterateMembers { (entityId, getEntityInstance) in + func getComponent() -> Z? where Z: Component { + return self.nexus.get(component: Z.identifier, for: entityId) as? Z + } + apply(getEntityInstance, getComponent, getComponent, getComponent, getComponent, getComponent) } } - public func forEachMember(_ applyToMember: (Entity, () -> A?, () -> B?, () -> C?, () -> D?, () -> E?) -> Void) where A: Component, B: Component, C: Component, D: Component, E: Component { - forEachMember { (entity: Entity) in - applyToMember(entity, entity.component(), entity.component(), entity.component(), entity.component(), entity.component()) + public func iterate(components _: A.Type, _: B.Type, _: C.Type, _: D.Type, _: E.Type, _: F.Type, + _ apply: @escaping (() -> Entity, () -> A?, () -> B?, () -> C?, () -> D?, () -> E?, () -> F?) -> Void) + where A: Component, B: Component, C: Component, D: Component, E: Component, F: Component { + iterateMembers { (entityId, getEntityInstance) in + func getComponent() -> Z? where Z: Component { + return self.nexus.get(component: Z.identifier, for: entityId) as? Z + } + apply(getEntityInstance, getComponent, getComponent, getComponent, getComponent, getComponent, getComponent) + } + } +} + +extension Family { + + public func iterate(_ apply: @escaping (() -> Entity, () -> A?) -> Void) where A: Component { + iterateMembers { (entityId, getEntityInstance) in + func getComponent() -> Z? where Z: Component { + return self.nexus.get(component: Z.identifier, for: entityId) as? Z + } + apply(getEntityInstance, getComponent) } } - public func forEachMember(_ applyToMember: (Entity, () -> A?, () -> B?, () -> C?, () -> D?, () -> E?, () -> F?) -> Void) where A: Component, B: Component, C: Component, D: Component, E: Component, F: Component { - forEachMember { (entity: Entity) in - applyToMember(entity, entity.component(), entity.component(), entity.component(), entity.component(), entity.component(), entity.component()) + public func iterate(_ apply: @escaping (() -> Entity, () -> A?, () -> B?) -> Void) + where A: Component, B: Component { + iterateMembers { (entityId, getEntityInstance) in + func getComponent() -> Z? where Z: Component { + return self.nexus.get(component: Z.identifier, for: entityId) as? Z + } + apply(getEntityInstance, getComponent, getComponent) + } + } + + public func iterate(_ apply: @escaping (() -> Entity, () -> A?, () -> B?, () -> C?) -> Void) + where A: Component, B: Component, C: Component { + iterateMembers { (entityId, getEntityInstance) in + func getComponent() -> Z? where Z: Component { + return self.nexus.get(component: Z.identifier, for: entityId) as? Z + } + apply(getEntityInstance, getComponent, getComponent, getComponent) + } + } + + public func iterate(_ apply: @escaping (() -> Entity, () -> A?, () -> B?, () -> C?, () -> D?) -> Void) + where A: Component, B: Component, C: Component, D: Component { + iterateMembers { (entityId, getEntityInstance) in + func getComponent() -> Z? where Z: Component { + return self.nexus.get(component: Z.identifier, for: entityId) as? Z + } + apply(getEntityInstance, getComponent, getComponent, getComponent, getComponent) + } + } + + public func iterate(_ apply: @escaping (() -> Entity, () -> A?, () -> B?, () -> C?, () -> D?, () -> E?) -> Void) + where A: Component, B: Component, C: Component, D: Component, E: Component { + iterateMembers { (entityId, getEntityInstance) in + func getComponent() -> Z? where Z: Component { + return self.nexus.get(component: Z.identifier, for: entityId) as? Z + } + apply(getEntityInstance, getComponent, getComponent, getComponent, getComponent, getComponent) + } + } + + public func iterate(_ apply: @escaping (() -> Entity, () -> A?, () -> B?, () -> C?, () -> D?, () -> E?, () -> F?) -> Void) where A: Component, B: Component, C: Component, D: Component, E: Component, F: Component { + iterateMembers { (entityId, getEntityInstance) in + func getComponent() -> Z? where Z: Component { + return self.nexus.get(component: Z.identifier, for: entityId) as? Z + } + apply(getEntityInstance, getComponent, getComponent, getComponent, getComponent, getComponent, getComponent) } } diff --git a/Sources/FirebladeECS/Family.swift b/Sources/FirebladeECS/Family.swift index c524428..0ffde41 100644 --- a/Sources/FirebladeECS/Family.swift +++ b/Sources/FirebladeECS/Family.swift @@ -30,7 +30,10 @@ extension Family { return nexus.isMember(entity, in: self) } - internal var members: EntitySet { + internal var members: LazyMapCollection>, Entity> { + return nexus.members(of: self) + } + internal var memberIds: EntityIdSet { return nexus.members(of: self) } } diff --git a/Sources/FirebladeECS/Nexus+Component.swift b/Sources/FirebladeECS/Nexus+Component.swift index d18e3ff..481d51f 100644 --- a/Sources/FirebladeECS/Nexus+Component.swift +++ b/Sources/FirebladeECS/Nexus+Component.swift @@ -71,6 +71,13 @@ extension Nexus { return get(hash) } + public func get(component componentId: ComponentIdentifier, for entityId: EntityIdentifier) -> Component? { + let hash: EntityComponentHash = componentId.hashValue(using: entityId.index) + guard let componentIdx: ComponentIndex = componentIndexByEntityComponentHash[hash] else { return nil } + guard let uniformComponents: UniformComponents = componentsByType[componentId] else { return nil } + return uniformComponents[componentIdx] + } + fileprivate func get(_ hash: EntityComponentHash) -> C? where C: Component { Log.info("GETTING: \(C.self)") let componentId: ComponentIdentifier = C.identifier diff --git a/Sources/FirebladeECS/Nexus+Entity.swift b/Sources/FirebladeECS/Nexus+Entity.swift index fb5ecec..0fc534c 100644 --- a/Sources/FirebladeECS/Nexus+Entity.swift +++ b/Sources/FirebladeECS/Nexus+Entity.swift @@ -51,9 +51,7 @@ extension Nexus { return isValid(entity: entityId) } - public func get(entity entityId: EntityIdentifier) -> Entity? { - Log.info("GETTING ENTITY: \(entityId)") - guard has(entity: entityId) else { return nil } + public func get(entity entityId: EntityIdentifier) -> Entity { return entities[entityId.index] } diff --git a/Sources/FirebladeECS/Nexus+Family.swift b/Sources/FirebladeECS/Nexus+Family.swift index f265539..fdbd5b2 100644 --- a/Sources/FirebladeECS/Nexus+Family.swift +++ b/Sources/FirebladeECS/Nexus+Family.swift @@ -33,11 +33,15 @@ extension Nexus { return family.traits.isMatch(components: componentSet) } - public func members(of family: Family) -> EntitySet { + public func members(of family: Family) -> EntityIdSet { let traitHash: FamilyTraitSetHash = family.traits.hashValue return familyMembersByTraitHash[traitHash] ?? [] // FIXME: fail? } + public func members(of family: Family) -> LazyMapCollection>, Entity> { + return members(of: family).lazy.flatMap { self.get(entity: $0) } + } + public func isMember(_ entity: Entity, in family: Family) -> Bool { let traitHash: FamilyTraitSetHash = family.traits.hashValue let entityId = entity.identifier @@ -82,7 +86,7 @@ extension Nexus { let (inserted, _) = familyMembersByTraitHash[traitHash]!.insert(entityId) assert(inserted, "entity with id \(entityId) already in family") } else { - familyMembersByTraitHash[traitHash] = EntitySet(minimumCapacity: 2) + familyMembersByTraitHash[traitHash] = EntityIdSet(minimumCapacity: 2) familyMembersByTraitHash[traitHash]!.insert(entityId) } diff --git a/Sources/FirebladeECS/Nexus.swift b/Sources/FirebladeECS/Nexus.swift index 04de58d..3dd81f7 100644 --- a/Sources/FirebladeECS/Nexus.swift +++ b/Sources/FirebladeECS/Nexus.swift @@ -18,7 +18,7 @@ public typealias UniformComponents = ContiguousArray public typealias ComponentIdentifiers = ContiguousArray public typealias ComponentSet = Set public typealias Entities = ContiguousArray -public typealias EntitySet = Set +public typealias EntityIdSet = Set public typealias FamilyTraitSetHash = Int public class Nexus { @@ -47,7 +47,7 @@ public class Nexus { var freeEntities: ContiguousArray var familiyByTraitHash: [FamilyTraitSetHash: Family] - var familyMembersByTraitHash: [FamilyTraitSetHash: EntitySet] + var familyMembersByTraitHash: [FamilyTraitSetHash: EntityIdSet] var componentIdsSetByEntity: [EntityIndex: ComponentSet] public init() { @@ -67,7 +67,7 @@ public class Nexus { extension Nexus { func notify(_ event: Event) { - Log.debug(event) + //Log.debug(event) // TODO: implement } diff --git a/Tests/FirebladeECSTests/FamilyTests.swift b/Tests/FirebladeECSTests/FamilyTests.swift index aed2203..d8853c8 100644 --- a/Tests/FirebladeECSTests/FamilyTests.swift +++ b/Tests/FirebladeECSTests/FamilyTests.swift @@ -77,11 +77,12 @@ class FamilyTests: XCTestCase { var index: Int = 0 - family.forEachMember { (e: Entity, p: () -> Position!, v: () -> Velocity!, n: () -> Name?) in + family.iterate { (e: () -> Entity, p: () -> Position!, v: () -> Velocity!, n: () -> Name?) in - p()!.x = 10 + let pos: Position = p() + pos.x = 10 - print(e, p(), n()) + print(e(), pos, n()) if index == 0 { print(v()) } @@ -89,9 +90,19 @@ class FamilyTests: XCTestCase { index += 1 } - family.forEachMember { (e: Entity, p: () -> Position!, v: () -> Velocity!, n: () -> Name?) in + family.iterate(components: Position.self, Velocity.self, Name.self) { (_, pos, vel, nm) in + let position: Position = pos()! + let velocity: Velocity = vel()! + let name: Name? = nm() - print(e, p().x, n()) + _ = position + _ = velocity + _ = name + } + + family.iterate { (e: () -> Entity, p: () -> Position!, v: () -> Velocity!, n: () -> Name?) in + + print(e(), p().x, n()) if index == 0 { print(v()) } @@ -101,4 +112,83 @@ class FamilyTests: XCTestCase { } + func testMeasureFamilyIteration() { + let nexus = Nexus() + let number: Int = 10_000 + + for i in 0.. Entity, pos: () -> Position!, vel: () -> Velocity!, nm: () -> Name?) in + let name: Name? = nm() + let velocity: Velocity = vel()! + let position: Position = pos()! + + _ = position + _ = velocity + _ = name + } + } + + } + + func testMeasureEntityIteration() { + let nexus = Nexus() + let number: Int = 10_000 + + for i in 0..