diff --git a/Sources/FirebladeECS/ManagedContiguousArray.swift b/Sources/FirebladeECS/ManagedContiguousArray.swift index f9a985d..59bc5bb 100644 --- a/Sources/FirebladeECS/ManagedContiguousArray.swift +++ b/Sources/FirebladeECS/ManagedContiguousArray.swift @@ -5,82 +5,82 @@ // Created by Christian Treffs on 28.10.17. // -private let pow2: [Int] = [ 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608, 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824, 2147483648, 4294967296 -] - -private func nearestToPow2(_ value: Int) -> Int { - let exp = (value.bitWidth-value.leadingZeroBitCount) - return pow2[exp] -} - -public protocol ManagedContiguousArrayProtocol: class { +public protocol UniformStorage: class { associatedtype Element - static var chunkSize: Int { get } - init(minCount: Int) + associatedtype Index + var count: Int { get } - func insert(_ element: Element, at index: Int) - func has(_ index: Int) -> Bool - func get(at index: Int) -> Element? - func remove(at index: Int) + func add(_ element: Element, at index: Index) + func has(_ index: Index) -> Bool + func get(at index: Index) -> Element? + func remove(at index: Index) + func clear(keepingCapacity: Bool) } -public class ManagedContiguousArray: ManagedContiguousArrayProtocol { +public class ManagedContiguousArray: UniformStorage { public static var chunkSize: Int = 4096 + public typealias Index = Int public typealias Element = Any - var _count: Int = 0 + var _size: Int = 0 var _store: ContiguousArray = [] - public required init(minCount: Int = chunkSize) { + public init(minCount: Int = chunkSize) { _store = ContiguousArray(repeating: nil, count: minCount) } + deinit { + clear() + } public var count: Int { - return _count + return _size } - public func insert(_ element: Element, at index: Int) { + public func add(_ element: Element, at index: Index) { if needsToGrow(index) { grow(including: index) } if _store[index] == nil { - _count += 1 + _size += 1 } _store[index] = element } - public func has(_ index: Int) -> Bool { + public func has(_ index: Index) -> Bool { if _store.count <= index { return false } return _store[index] != nil } - public func get(at index: Int) -> Element? { + public func get(at index: Index) -> Element? { return _store[index] } - public func remove(at index: Int) { + public func remove(at index: Index) { if _store[index] != nil { - _count -= 1 + _size -= 1 + } + _store[index] = nil + if _size == 0 { + clear() } - return _store[index] = nil } - internal func needsToGrow(_ index: Int) -> Bool { + public func clear(keepingCapacity: Bool = false) { + _size = 0 + _store.removeAll(keepingCapacity: keepingCapacity) + } + + internal func needsToGrow(_ index: Index) -> Bool { return index > _store.count - 1 } - internal func grow(including index: Int) { - //var t = Timer() - //t.start() + internal func grow(including index: Index) { let newCapacity: Int = nearest(to: index) - let count: Int = newCapacity-_store.count - //_store.reserveCapacity(newCapacity) - for _ in 0.. Int { + internal func nearest(to index: Index) -> Int { let delta = Float(index) / Float(ManagedContiguousArray.chunkSize) let multiplier = Int(delta) + 1 return multiplier * ManagedContiguousArray.chunkSize @@ -88,44 +88,10 @@ public class ManagedContiguousArray: ManagedContiguousArrayProtocol { } public class ContiguousComponentArray: ManagedContiguousArray { - public typealias Element = Component + public typealias Element = Component + public typealias Index = EntityIndex } public class ContiguousEntityIdArray: ManagedContiguousArray { public typealias Element = EntityIdentifier } - -/* - public func insert(_ element: Element, at entityIdx: EntityIndex) { - super.insert(element, at: entityIdx) - } - - public func has(_ entityIdx: EntityIndex) -> Bool { - if _store.count <= entityIdx { return false } - return _store[entityIdx] != nil - } - - public func get(at entityIdx: EntityIndex) -> Element? { - return _store[entityIdx] - } - - public func remove(at entityIdx: EntityIndex) { - return _store[entityIdx] = nil - } - - fileprivate func needsToGrow(_ entityId: EntityIndex) -> Bool { - return entityId > _store.count - 1 - } - - fileprivate func grow(to minIndex: Int) { - - let newCapacity: Int = nearestToPow2(minIndex) - let count: Int = newCapacity-_store.count - let nilElements: ContiguousArray = ContiguousArray.init(repeating: nil, count: count) - - _store.reserveCapacity(newCapacity) - _store.append(contentsOf: nilElements) - - } - -*/ diff --git a/Sources/FirebladeECS/Nexus+Component.swift b/Sources/FirebladeECS/Nexus+Component.swift index b4d7d00..152300b 100644 --- a/Sources/FirebladeECS/Nexus+Component.swift +++ b/Sources/FirebladeECS/Nexus+Component.swift @@ -17,7 +17,7 @@ extension Nexus { public func has(componentId: ComponentIdentifier, entityIdx: EntityIndex) -> Bool { guard let uniforms = componentsByType[componentId] else { return false } - return uniforms.contains(entityIdx) + return uniforms.has(entityIdx) } public func count(components entityId: EntityIdentifier) -> Int { @@ -39,12 +39,10 @@ extension Nexus { // TODO: replace component?! copy properties?! return } - if componentsByType[componentId] != nil { - componentsByType[componentId]!.add(component, with: entityIdx) - } else { - componentsByType[componentId] = SparseComponentSet() - componentsByType[componentId]!.add(component, with: entityIdx) + if componentsByType[componentId] == nil { + componentsByType[componentId] = UniformComponents() } + componentsByType[componentId]!.add(component, at: entityIdx) // assigns the component id to the entity id if componentIdsByEntity[entityIdx] != nil { @@ -70,8 +68,8 @@ extension Nexus { } public func get(component componentId: ComponentIdentifier, for entityId: EntityIdentifier) -> Component? { - guard let uniformComponents: SparseComponentSet = componentsByType[componentId] else { return nil } - return uniformComponents.get(at: entityId.index) + guard let uniformComponents: UniformComponents = componentsByType[componentId] else { return nil } + return uniformComponents.get(at: entityId.index) as? Component } public func get(for entityId: EntityIdentifier) -> C? where C: Component { @@ -80,7 +78,7 @@ extension Nexus { } fileprivate func get(componentId: ComponentIdentifier, entityIdx: EntityIndex) -> C? where C: Component { - guard let uniformComponents: SparseComponentSet = componentsByType[componentId] else { return nil } + guard let uniformComponents: UniformComponents = componentsByType[componentId] else { return nil } return uniformComponents.get(at: entityIdx) as? C } @@ -94,7 +92,7 @@ extension Nexus { let hash: EntityComponentHash = componentId.hashValue(using: entityIdx) // MARK: delete component instance - componentsByType[componentId]?.remove(entityIdx) + componentsByType[componentId]?.remove(at: entityIdx) // MARK: unassign component guard let removeIndex: ComponentIdsByEntityIndex = componentIdsByEntityLookup.removeValue(forKey: hash) else { diff --git a/Sources/FirebladeECS/Nexus.swift b/Sources/FirebladeECS/Nexus.swift index cddcdb2..2279b22 100644 --- a/Sources/FirebladeECS/Nexus.swift +++ b/Sources/FirebladeECS/Nexus.swift @@ -9,7 +9,7 @@ public typealias EntityComponentHash = Int public typealias ComponentIdsByEntityIndex = Int public typealias ComponentTypeHash = Int // component object identifier hash value - +//public typealias UniformComponents = SparseComponentSet public typealias UniformComponents = ContiguousComponentArray public typealias ComponentIdentifiers = ContiguousArray public typealias ComponentSet = Set @@ -28,7 +28,7 @@ public class Nexus { /// - Key: component type identifier /// - Value: each element is a component instance of the same type (uniform). New component instances are appended. - var componentsByType: [ComponentIdentifier: SparseComponentSet] + var componentsByType: [ComponentIdentifier: UniformComponents] /// - Key: entity id as index /// - Value: each element is a component identifier associated with this entity diff --git a/Sources/FirebladeECS/SparseSet.swift b/Sources/FirebladeECS/SparseSet.swift index 8248e84..e7fe8f6 100644 --- a/Sources/FirebladeECS/SparseSet.swift +++ b/Sources/FirebladeECS/SparseSet.swift @@ -5,8 +5,10 @@ // Created by Christian Treffs on 30.10.17. // -public class SparseComponentSet { - public typealias Element = Component +public class SparseSet: UniformStorage { + + public typealias Element = Any + public typealias Index = Int fileprivate typealias ComponentIdx = Int fileprivate var size: Int = 0 fileprivate var dense: ContiguousArray @@ -26,42 +28,37 @@ public class SparseComponentSet { internal var capacitySparse: Int { return sparse.capacity } internal var capacityDense: Int { return dense.capacity } - public func contains(_ entityIdx: EntityIndex ) -> Bool { - return sparse[entityIdx] != nil && - sparse[entityIdx]! < count && - dense[sparse[entityIdx]!] != nil + public func has(_ index: EntityIndex) -> Bool { + return sparse[index] ?? Int.max < count && + dense[sparse[index]!] != nil } - @discardableResult - public func add(_ element: Element, with entityIdx: EntityIndex ) -> Bool { - if contains(entityIdx) { return false } - sparse[entityIdx] = count - let entry: Pair = Pair(key: entityIdx, value: element) + public func add(_ element: Element, at index: EntityIndex) { + if has(index) { return } + sparse[index] = count + let entry: Pair = Pair(key: index, value: element) dense.append(entry) size += 1 - return true } public func get(at entityIdx: EntityIndex) -> Element? { - guard contains(entityIdx) else { return nil } + guard has(entityIdx) else { return nil } return dense[sparse[entityIdx]!]!.value } - @discardableResult - public func remove(_ entityIdx: EntityIndex ) -> Element? { - guard contains(entityIdx) else { return nil } - let compIdx: ComponentIdx = sparse[entityIdx]! + public func remove(at index: EntityIndex) { + guard has(index) else { return } + let compIdx: ComponentIdx = sparse[index]! let lastIdx: ComponentIdx = count-1 dense.swapAt(compIdx, lastIdx) - sparse[entityIdx] = nil + sparse[index] = nil let swapped: Pair = dense[compIdx]! sparse[swapped.key] = compIdx - let removed: Pair = dense.popLast()!! + _ = dense.popLast()!! size -= 1 if size == 0 { clear(keepingCapacity: false) } - return removed.value } public func clear(keepingCapacity: Bool = false) { @@ -72,7 +69,7 @@ public class SparseComponentSet { } -extension SparseComponentSet: Sequence { +extension SparseSet: Sequence { public func makeIterator() -> AnyIterator { var iterator = dense.makeIterator() @@ -81,3 +78,8 @@ extension SparseComponentSet: Sequence { } } } + +public class SparseComponentSet: SparseSet { + public typealias Element = Component + public typealias Index = EntityIndex +} diff --git a/Tests/FirebladeECSTests/Base.swift b/Tests/FirebladeECSTests/Base.swift index 7d81e95..78cff5b 100644 --- a/Tests/FirebladeECSTests/Base.swift +++ b/Tests/FirebladeECSTests/Base.swift @@ -47,11 +47,10 @@ class ExampleSystem { } func update(deltaT: Double) { - family.iterate(components: Position.self, Velocity.self, Name.self) { (_, positionGetter, velocityGetter, nameGetter) in + family.iterate(components: Position.self, Velocity.self, Name.self) { (_, positionGetter, velocityGetter, _) in let position: Position = positionGetter! let velocity: Velocity = velocityGetter! - let name: Name? = nameGetter position.x *= 2 velocity.a *= 2 diff --git a/Tests/FirebladeECSTests/NexusTests.swift b/Tests/FirebladeECSTests/NexusTests.swift index d368034..ec65992 100644 --- a/Tests/FirebladeECSTests/NexusTests.swift +++ b/Tests/FirebladeECSTests/NexusTests.swift @@ -162,12 +162,4 @@ class NexusTests: XCTestCase { } - func testComponentStorage() { - let nexus = Nexus() - let a = nexus.create() - let b = nexus.create() - let c = nexus.create() - - } - } diff --git a/Tests/FirebladeECSTests/SparseComponentSetTests.swift b/Tests/FirebladeECSTests/SparseComponentSetTests.swift index 4249594..b32f498 100644 --- a/Tests/FirebladeECSTests/SparseComponentSetTests.swift +++ b/Tests/FirebladeECSTests/SparseComponentSetTests.swift @@ -16,7 +16,7 @@ class SparseComponentSetTests: XCTestCase { let num: Int = 100 for i in 0..