From 96ef53cf6d9ad4e0e5c5a8343f4d65902fe8ff28 Mon Sep 17 00:00:00 2001 From: Christian Treffs Date: Thu, 30 Jul 2020 21:35:44 +0200 Subject: [PATCH] Refine multiple component creation --- Sources/FirebladeECS/Nexus+Component.swift | 26 +--- Sources/FirebladeECS/Nexus+Entity.swift | 4 +- Sources/FirebladeECS/Nexus+FamilyUpdate.swift | 70 --------- Sources/FirebladeECS/Nexus+Internal.swift | 136 ++++++++++++++++++ 4 files changed, 140 insertions(+), 96 deletions(-) delete mode 100644 Sources/FirebladeECS/Nexus+FamilyUpdate.swift create mode 100644 Sources/FirebladeECS/Nexus+Internal.swift diff --git a/Sources/FirebladeECS/Nexus+Component.swift b/Sources/FirebladeECS/Nexus+Component.swift index 11da234..4635a97 100644 --- a/Sources/FirebladeECS/Nexus+Component.swift +++ b/Sources/FirebladeECS/Nexus+Component.swift @@ -22,31 +22,9 @@ extension Nexus { } public final func assign(component: Component, to entity: Entity) { - let componentId: ComponentIdentifier = component.identifier let entityId: EntityIdentifier = entity.identifier - - // test if component is already assigned - guard !has(componentId: componentId, entityId: entityId) else { - delegate?.nexusNonFatalError("ComponentAdd collision: \(entityId) already has a component \(component)") - assertionFailure("ComponentAdd collision: \(entityId) already has a component \(component)") - return - } - - // add component instances to uniform component stores - if componentsByType[componentId] == nil { - componentsByType[componentId] = ManagedContiguousArray() - } - componentsByType[componentId]?.insert(component, at: entityId.id) - - // assigns the component id to the entity id - if componentIdsByEntity[entityId] == nil { - componentIdsByEntity[entityId] = Set() - } - componentIdsByEntity[entityId]?.insert(componentId) //, at: componentId.hashValue) - - update(familyMembership: entityId) - - delegate?.nexusEvent(ComponentAdded(component: componentId, toEntity: entity.identifier)) + assign(component: component, entityId: entityId) + delegate?.nexusEvent(ComponentAdded(component: component.identifier, toEntity: entity.identifier)) } public final func assign(component: C, to entity: Entity) where C: Component { diff --git a/Sources/FirebladeECS/Nexus+Entity.swift b/Sources/FirebladeECS/Nexus+Entity.swift index 389883f..7e12e80 100644 --- a/Sources/FirebladeECS/Nexus+Entity.swift +++ b/Sources/FirebladeECS/Nexus+Entity.swift @@ -17,14 +17,14 @@ extension Nexus { @discardableResult public func createEntity(with components: Component...) -> Entity { let newEntity = createEntity() - components.forEach { newEntity.assign($0) } + assign(components: components, to: newEntity.identifier) return newEntity } @discardableResult public func createEntity(with components: C) -> Entity where C: Collection, C.Element == Component { let entity = self.createEntity() - components.forEach { entity.assign($0) } + assign(components: components, to: entity.identifier) return entity } diff --git a/Sources/FirebladeECS/Nexus+FamilyUpdate.swift b/Sources/FirebladeECS/Nexus+FamilyUpdate.swift deleted file mode 100644 index a669077..0000000 --- a/Sources/FirebladeECS/Nexus+FamilyUpdate.swift +++ /dev/null @@ -1,70 +0,0 @@ -// -// Nexus+FamilyUpdate.swift -// FirebladeECS -// -// Created by Christian Treffs on 14.02.19. -// - -extension Nexus { - /// will be called on family init - final func onFamilyInit(traits: FamilyTraitSet) { - guard familyMembersByTraits[traits] == nil else { - return - } - - familyMembersByTraits[traits] = UnorderedSparseSet() - update(familyMembership: traits) - } - - final func update(familyMembership traits: FamilyTraitSet) { - // FIXME: iterating all entities is costly for many entities - var iter = entityStorage.makeIterator() - while let entityId = iter.next() { - update(membership: traits, for: entityId) - } - } - - final func update(familyMembership entityId: EntityIdentifier) { - // FIXME: iterating all families is costly for many families - var iter = familyMembersByTraits.keys.makeIterator() - while let traits = iter.next() { - update(membership: traits, for: entityId) - } - } - - final func update(membership traits: FamilyTraitSet, for entityId: EntityIdentifier) { - guard let componentIds = componentIdsByEntity[entityId] else { - // no components - so skip - return - } - - let isMember: Bool = self.isMember(entity: entityId, inFamilyWithTraits: traits) - if !exists(entity: entityId) && isMember { - remove(entityWithId: entityId, fromFamilyWithTraits: traits) - return - } - - let isMatch: Bool = traits.isMatch(components: componentIds) - - switch (isMatch, isMember) { - case (true, false): - add(entityWithId: entityId, toFamilyWithTraits: traits) - delegate?.nexusEvent(FamilyMemberAdded(member: entityId, toFamily: traits)) - - case (false, true): - remove(entityWithId: entityId, fromFamilyWithTraits: traits) - delegate?.nexusEvent(FamilyMemberRemoved(member: entityId, from: traits)) - - default: - break - } - } - - final func add(entityWithId entityId: EntityIdentifier, toFamilyWithTraits traits: FamilyTraitSet) { - familyMembersByTraits[traits]!.insert(entityId, at: entityId.id) - } - - final func remove(entityWithId entityId: EntityIdentifier, fromFamilyWithTraits traits: FamilyTraitSet) { - familyMembersByTraits[traits]!.remove(at: entityId.id) - } -} diff --git a/Sources/FirebladeECS/Nexus+Internal.swift b/Sources/FirebladeECS/Nexus+Internal.swift new file mode 100644 index 0000000..2ba5263 --- /dev/null +++ b/Sources/FirebladeECS/Nexus+Internal.swift @@ -0,0 +1,136 @@ +// +// Nexus+Internal.swift +// FirebladeECS +// +// Created by Christian Treffs on 14.02.19. +// + +extension Nexus { + func assign(components: C, to entityId: EntityIdentifier) where C: Collection, C.Element == Component { + var iter = components.makeIterator() + while let component = iter.next() { + let componentId = component.identifier + // test if component is already assigned + guard !has(componentId: componentId, entityId: entityId) else { + delegate?.nexusNonFatalError("ComponentAdd collision: \(entityId) already has a component \(component)") + assertionFailure("ComponentAdd collision: \(entityId) already has a component \(component)") + return + } + + // add component instances to uniform component stores + insertComponentInstance(component, componentId, entityId) + + // assigns the component id to the entity id + assign(componentId, entityId) + } + + // Update entity membership + update(familyMembership: entityId) + } + + func assign(component: Component, entityId: EntityIdentifier) { + let componentId = component.identifier + + // test if component is already assigned + guard !has(componentId: componentId, entityId: entityId) else { + delegate?.nexusNonFatalError("ComponentAdd collision: \(entityId) already has a component \(component)") + assertionFailure("ComponentAdd collision: \(entityId) already has a component \(component)") + return + } + + // add component instances to uniform component stores + insertComponentInstance(component, componentId, entityId) + + // assigns the component id to the entity id + assign(componentId, entityId) + + // Update entity membership + update(familyMembership: entityId) + } + + func insertComponentInstance(_ component: Component, _ componentId: ComponentIdentifier, _ entityId: EntityIdentifier) { + if componentsByType[componentId] == nil { + componentsByType[componentId] = ManagedContiguousArray() + } + componentsByType[componentId]?.insert(component, at: entityId.id) + } + + func assign(_ componentId: ComponentIdentifier, _ entityId: EntityIdentifier) { + if componentIdsByEntity[entityId] == nil { + componentIdsByEntity[entityId] = Set(arrayLiteral: componentId) + } else { + componentIdsByEntity[entityId]?.insert(componentId) + } + } + + func assign(_ componentIds: Set, _ entityId: EntityIdentifier) { + if componentIdsByEntity[entityId] == nil { + componentIdsByEntity[entityId] = componentIds + } else { + componentIdsByEntity[entityId]?.formUnion(componentIds) + } + } + + func update(familyMembership entityId: EntityIdentifier) { + // FIXME: iterating all families is costly for many families + // FIXME: this could be parallelized + var iter = familyMembersByTraits.keys.makeIterator() + while let traits = iter.next() { + update(membership: traits, for: entityId) + } + } + + /// will be called on family init + func onFamilyInit(traits: FamilyTraitSet) { + guard familyMembersByTraits[traits] == nil else { + return + } + + familyMembersByTraits[traits] = UnorderedSparseSet() + update(familyMembership: traits) + } + + func update(familyMembership traits: FamilyTraitSet) { + // FIXME: iterating all entities is costly for many entities + var iter = entityStorage.makeIterator() + while let entityId = iter.next() { + update(membership: traits, for: entityId) + } + } + + func update(membership traits: FamilyTraitSet, for entityId: EntityIdentifier) { + guard let componentIds = componentIdsByEntity[entityId] else { + // no components - so skip + return + } + + let isMember: Bool = self.isMember(entity: entityId, inFamilyWithTraits: traits) + if !exists(entity: entityId) && isMember { + remove(entityWithId: entityId, fromFamilyWithTraits: traits) + return + } + + let isMatch: Bool = traits.isMatch(components: componentIds) + + switch (isMatch, isMember) { + case (true, false): + add(entityWithId: entityId, toFamilyWithTraits: traits) + delegate?.nexusEvent(FamilyMemberAdded(member: entityId, toFamily: traits)) + + case (false, true): + remove(entityWithId: entityId, fromFamilyWithTraits: traits) + delegate?.nexusEvent(FamilyMemberRemoved(member: entityId, from: traits)) + + default: + break + } + } + + func add(entityWithId entityId: EntityIdentifier, toFamilyWithTraits traits: FamilyTraitSet) { + familyMembersByTraits[traits]!.insert(entityId, at: entityId.id) + } + + func remove(entityWithId entityId: EntityIdentifier, fromFamilyWithTraits traits: FamilyTraitSet) { + familyMembersByTraits[traits]!.remove(at: entityId.id) + } +}