Cleanups and unification of storage protocol
This commit is contained in:
parent
4ada634130
commit
555e2088bb
|
|
@ -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<Element?> = []
|
||||
public required init(minCount: Int = chunkSize) {
|
||||
public init(minCount: Int = chunkSize) {
|
||||
_store = ContiguousArray<Element?>(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..<count {
|
||||
let newCount: Int = newCapacity-_store.count
|
||||
for _ in 0..<newCount {
|
||||
_store.append(nil)
|
||||
}
|
||||
//t.stop()
|
||||
//print("did grow to \(newCapacity) in \(t.milliSeconds)ms")
|
||||
}
|
||||
|
||||
internal func nearest(to index: Int) -> 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<Element?> = ContiguousArray<Element?>.init(repeating: nil, count: count)
|
||||
|
||||
_store.reserveCapacity(newCapacity)
|
||||
_store.append(contentsOf: nilElements)
|
||||
|
||||
}
|
||||
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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<C>(for entityId: EntityIdentifier) -> C? where C: Component {
|
||||
|
|
@ -80,7 +78,7 @@ extension Nexus {
|
|||
}
|
||||
|
||||
fileprivate func get<C>(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 {
|
||||
|
|
|
|||
|
|
@ -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<ComponentIdentifier>
|
||||
public typealias ComponentSet = Set<ComponentIdentifier>
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<Pair?>
|
||||
|
|
@ -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<Element> {
|
||||
var iterator = dense.makeIterator()
|
||||
|
|
@ -81,3 +78,8 @@ extension SparseComponentSet: Sequence {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class SparseComponentSet: SparseSet {
|
||||
public typealias Element = Component
|
||||
public typealias Index = EntityIndex
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -162,12 +162,4 @@ class NexusTests: XCTestCase {
|
|||
|
||||
}
|
||||
|
||||
func testComponentStorage() {
|
||||
let nexus = Nexus()
|
||||
let a = nexus.create()
|
||||
let b = nexus.create()
|
||||
let c = nexus.create()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ class SparseComponentSetTests: XCTestCase {
|
|||
let num: Int = 100
|
||||
|
||||
for i in 0..<num {
|
||||
s.add(Position(x: i, y: i), with: EntityIndex(i))
|
||||
s.add(Position(x: i, y: i), at: EntityIndex(i))
|
||||
}
|
||||
|
||||
XCTAssert(s.count == num)
|
||||
|
|
@ -28,7 +28,7 @@ class SparseComponentSetTests: XCTestCase {
|
|||
}
|
||||
|
||||
for i in 0..<num {
|
||||
s.remove(EntityIndex(i))
|
||||
s.remove(at: EntityIndex(i))
|
||||
}
|
||||
|
||||
XCTAssert(s.count == 0)
|
||||
|
|
|
|||
Loading…
Reference in New Issue