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)