diff --git a/Sources/FirebladeECS/Entity.swift b/Sources/FirebladeECS/Entity.swift index 8c15e5d..e38f67e 100644 --- a/Sources/FirebladeECS/Entity.swift +++ b/Sources/FirebladeECS/Entity.swift @@ -68,7 +68,9 @@ public extension Entity { @discardableResult public final func assign(_ components: Component...) -> Entity { - components.forEach { assign($0) } + for component: Component in components { + assign(component) + } return self } diff --git a/Sources/FirebladeECS/Family+Members.swift b/Sources/FirebladeECS/Family+Members.swift index 327427f..6289db4 100644 --- a/Sources/FirebladeECS/Family+Members.swift +++ b/Sources/FirebladeECS/Family+Members.swift @@ -7,7 +7,7 @@ extension Family { public func iterateMembers(_ apply: @escaping (EntityIdentifier) -> Void) { - memberIds.forEach { (entityId: EntityIdentifier) -> Void in + for entityId in memberIds { apply(entityId) } } @@ -17,8 +17,7 @@ extension Family { public func iterate(components _: A.Type, _ apply: @escaping (EntityIdentifier, A?) -> Void) where A: Component { - var iter = memberIds.makeIterator() - while let entityId: EntityIdentifier = iter.next() { + for entityId in memberIds { let a: A? = self.nexus.get(for: entityId) apply(entityId, a) } @@ -26,8 +25,7 @@ extension Family { public func iterate(components _: A.Type, _: B.Type, _ apply: @escaping (EntityIdentifier, A?, B?) -> Void) where A: Component, B: Component { - var iter = memberIds.makeIterator() - while let entityId: EntityIdentifier = iter.next() { + for entityId in memberIds { let a: A? = self.nexus.get(for: entityId) let b: B? = self.nexus.get(for: entityId) apply(entityId, a, b) @@ -36,8 +34,7 @@ extension Family { public func iterate(components _: A.Type, _: B.Type, _: C.Type, _ apply: @escaping (EntityIdentifier, A?, B?, C?) -> Void) where A: Component, B: Component, C: Component { - var iter = memberIds.makeIterator() - while let entityId: EntityIdentifier = iter.next() { + for entityId in memberIds { let a: A? = self.nexus.get(for: entityId) let b: B? = self.nexus.get(for: entityId) let c: C? = self.nexus.get(for: entityId) @@ -47,8 +44,7 @@ extension Family { public func iterate(components _: A.Type, _: B.Type, _: C.Type, _: D.Type, _ apply: @escaping (EntityIdentifier, A?, B?, C?, D?) -> Void) where A: Component, B: Component, C: Component, D: Component { - var iter = memberIds.makeIterator() - while let entityId: EntityIdentifier = iter.next() { + for entityId in memberIds { let a: A? = self.nexus.get(for: entityId) let b: B? = self.nexus.get(for: entityId) let c: C? = self.nexus.get(for: entityId) @@ -58,8 +54,7 @@ extension Family { } public func iterate(components _: A.Type, _: B.Type, _: C.Type, _: D.Type, _: E.Type, _ apply: @escaping (EntityIdentifier, A?, B?, C?, D?, E?) -> Void) where A: Component, B: Component, C: Component, D: Component, E: Component { - var iter = memberIds.makeIterator() - while let entityId: EntityIdentifier = iter.next() { + for entityId in memberIds { let a: A? = self.nexus.get(for: entityId) let b: B? = self.nexus.get(for: entityId) let c: C? = self.nexus.get(for: entityId) @@ -72,8 +67,7 @@ extension Family { public func iterate(components _: A.Type, _: B.Type, _: C.Type, _: D.Type, _: E.Type, _: F.Type, _ apply: @escaping (EntityIdentifier, A?, B?, C?, D?, E?, F?) -> Void) where A: Component, B: Component, C: Component, D: Component, E: Component, F: Component { - var iter = memberIds.makeIterator() - while let entityId: EntityIdentifier = iter.next() { + for entityId in memberIds { let a: A? = self.nexus.get(for: entityId) let b: B? = self.nexus.get(for: entityId) let c: C? = self.nexus.get(for: entityId) diff --git a/Sources/FirebladeECS/Nexus+Component.swift b/Sources/FirebladeECS/Nexus+Component.swift index 0bafa61..8c245a9 100644 --- a/Sources/FirebladeECS/Nexus+Component.swift +++ b/Sources/FirebladeECS/Nexus+Component.swift @@ -29,7 +29,12 @@ extension Nexus { let componentId = component.identifier let entityIdx = entity.identifier.index let hash: EntityComponentHash = componentId.hashValue(using: entityIdx) - assert(!has(hash), "ComponentAdd collision: \(entityIdx) already has a component \(component)") + /// test if component is already assigned + guard !has(hash) else { + report("ComponentAdd collision: \(entityIdx) already has a component \(component)") + // TODO: replace component?! copy properties?! + return + } var newComponentIndex: ComponentIndex = ComponentIndex.invalid if componentsByType[componentId] != nil { newComponentIndex = componentsByType[componentId]!.count // TODO: get next free index @@ -40,17 +45,11 @@ extension Nexus { } // assigns the component id to the entity id - // FIXME: expensive if componentIdsByEntity[entityIdx] != nil { - let (inserted, _) = componentIdsSetByEntity[entityIdx]!.insert(componentId) - assert(inserted) - let newIndex = componentIdsByEntity[entityIdx]!.count - componentIdsByEntity[entityIdx]!.insert(componentId, at: newIndex) - componentIdsByEntityLookup[hash] = newIndex + let endIndex: Int = componentIdsByEntity[entityIdx]!.count + componentIdsByEntity[entityIdx]!.append(componentId) // Amortized O(1) + componentIdsByEntityLookup[hash] = endIndex } else { - componentIdsSetByEntity[entityIdx] = Set(minimumCapacity: 2) - let (inserted, _) = componentIdsSetByEntity[entityIdx]!.insert(componentId) - assert(inserted) componentIdsByEntity[entityIdx] = ComponentIdentifiers(arrayLiteral: componentId) componentIdsByEntityLookup[hash] = 0 } @@ -60,7 +59,7 @@ extension Nexus { // FIXME: this is costly for many families let entityId: EntityIdentifier = entity.identifier - familiyByTraitHash.forEach { (_, family) in + for (_, family) in familiyByTraitHash { update(membership: family, for: entityId) } @@ -111,13 +110,6 @@ extension Nexus { } // MARK: unassign component - - guard componentIdsSetByEntity[entityId.index]?.remove(componentId) != nil else { - assert(false, "ComponentRemove failure: no component found to be removed in set") - report("ComponentRemove failure: no component found to be removed in set") - return false - } - guard let removeIndex: ComponentIdsByEntityIndex = componentIdsByEntityLookup.removeValue(forKey: hash) else { assert(false, "ComponentRemove failure: no component found to be removed") report("ComponentRemove failure: no component found to be removed") @@ -140,7 +132,7 @@ extension Nexus { } // FIXME: this is costly for many families - familiyByTraitHash.forEach { (_, family) in + for (_, family) in familiyByTraitHash { update(membership: family, for: entityId) } diff --git a/Sources/FirebladeECS/Nexus+Family.swift b/Sources/FirebladeECS/Nexus+Family.swift index 9317367..455e87b 100644 --- a/Sources/FirebladeECS/Nexus+Family.swift +++ b/Sources/FirebladeECS/Nexus+Family.swift @@ -14,10 +14,14 @@ extension Nexus { /// - noneComponents: none component type may appear in this family. /// - oneComponents: at least one of component types must appear in this family. /// - Returns: family with given traits. - public func family(requiresAll allComponents: [Component.Type], excludesAll noneComponents: [Component.Type], needsAtLeastOne oneComponents: [Component.Type] = []) -> Family { - + public func family(requiresAll allComponents: [Component.Type], + excludesAll noneComponents: [Component.Type], + needsAtLeastOne oneComponents: [Component.Type] = []) -> Family { let traits = FamilyTraitSet(requiresAll: allComponents, excludesAll: noneComponents, needsAtLeastOne: oneComponents) + return family(with: traits) + } + public func family(with traits: FamilyTraitSet) -> Family { guard let family: Family = get(family: traits) else { return create(family: traits) } @@ -26,10 +30,12 @@ extension Nexus { public func canBecomeMember(_ entity: Entity, in family: Family) -> Bool { let entityIdx: EntityIndex = entity.identifier.index - guard let componentSet: ComponentSet = componentIdsSetByEntity[entityIdx] else { + guard let componentIds: ComponentIdentifiers = componentIdsByEntity[entityIdx] else { assert(false, "no component set defined for entity: \(entity)") return false } + // FIXME: may be a bottle neck + let componentSet: ComponentSet = ComponentSet.init(componentIds) return family.traits.isMatch(components: componentSet) } @@ -64,8 +70,8 @@ extension Nexus { assert(replaced == nil, "Family with exact trait hash already exists: \(traitHash)") // FIXME: this is costly for many entities - entities.forEach { - update(membership: family, for: $0.identifier) + for entity: Entity in entities { + update(membership: family, for: entity.identifier) } notify(FamilyCreated(family: traits)) @@ -78,7 +84,9 @@ extension Nexus { func update(membership family: Family, for entityId: EntityIdentifier) { let entityIdx: EntityIndex = entityId.index - guard let componentsSet: ComponentSet = componentIdsSetByEntity[entityIdx] else { return } + guard let componentIds: ComponentIdentifiers = componentIdsByEntity[entityIdx] else { return } + // FIXME: bottle neck + let componentsSet: ComponentSet = ComponentSet.init(componentIds) let isMember: Bool = family.isMember(entityId) let isMatch: Bool = family.traits.isMatch(components: componentsSet) switch (isMatch, isMember) { diff --git a/Sources/FirebladeECS/Nexus.swift b/Sources/FirebladeECS/Nexus.swift index 39bd832..599873b 100644 --- a/Sources/FirebladeECS/Nexus.swift +++ b/Sources/FirebladeECS/Nexus.swift @@ -53,14 +53,12 @@ public class Nexus { var familiyByTraitHash: [FamilyTraitSetHash: Family] var trashMap: TraitEntityIdHashSet var familyMembersByTraitHash: [FamilyTraitSetHash: EntityIds] - var componentIdsSetByEntity: [EntityIndex: ComponentSet] public init() { entities = Entities() componentsByType = [:] componentIndexByEntityComponentHash = [:] componentIdsByEntity = [:] - componentIdsSetByEntity = [:] componentIdsByEntityLookup = [:] freeEntities = ContiguousArray() familiyByTraitHash = [:] diff --git a/Tests/FirebladeECSTests/NexusTests.swift b/Tests/FirebladeECSTests/NexusTests.swift index 151da86..ced81bf 100644 --- a/Tests/FirebladeECSTests/NexusTests.swift +++ b/Tests/FirebladeECSTests/NexusTests.swift @@ -78,6 +78,7 @@ class NexusTests: XCTestCase { let p0 = Position(x: 1, y: 2) + e0.assign(p0) e0.assign(p0) XCTAssert(e0.isValid)