Reworked Entitiy storage as SparseSet
This commit is contained in:
parent
555e2088bb
commit
19cab7af5d
|
|
@ -40,10 +40,7 @@ extension Family {
|
|||
return nexus.isMember(entityId, in: self)
|
||||
}
|
||||
|
||||
/*public var members: LazyMapCollection<LazyFilterCollection<LazyMapCollection<EntityIdSet, Entity?>>, Entity> {
|
||||
return nexus.members(of: self)
|
||||
}*/
|
||||
internal var memberIds: [EntityIdentifier] {
|
||||
internal var memberIds: UniformEntityIdentifiers {
|
||||
return nexus.members(of: self)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,24 +39,19 @@ extension Nexus {
|
|||
return family.traits.isMatch(components: componentSet)
|
||||
}
|
||||
|
||||
public func members(of family: Family) -> [EntityIdentifier] {
|
||||
public func members(of family: Family) -> UniformEntityIdentifiers {
|
||||
let traitHash: FamilyTraitSetHash = family.traits.hashValue
|
||||
return familyMembersByTraitHash[traitHash] ?? [] // FIXME: fail?
|
||||
return familyMembersByTraitHash[traitHash] ?? UniformEntityIdentifiers() // FIXME: fail?
|
||||
}
|
||||
|
||||
public func isMember(_ entity: Entity, in family: Family) -> Bool {
|
||||
return isMember(entity.identifier, in: family)
|
||||
}
|
||||
|
||||
public func isMember(byHash traitSetEntityIdHash: TraitEntityIdHash) -> Bool {
|
||||
return familyContainsEntityId[traitSetEntityIdHash] ?? false
|
||||
}
|
||||
|
||||
public func isMember(_ entityId: EntityIdentifier, in family: Family) -> Bool {
|
||||
let traitHash: FamilyTraitSetHash = family.traits.hashValue
|
||||
// FIXME: this is costly!
|
||||
guard let members: [EntityIdentifier] = familyMembersByTraitHash[traitHash] else { return false }
|
||||
return members.contains(entityId)
|
||||
guard let members: UniformEntityIdentifiers = familyMembersByTraitHash[traitHash] else { return false }
|
||||
return members.has(entityId.index)
|
||||
}
|
||||
|
||||
fileprivate func get(family traits: FamilyTraitSet) -> Family? {
|
||||
|
|
@ -89,52 +84,35 @@ extension Nexus {
|
|||
|
||||
func update(membership family: Family, for entityId: EntityIdentifier) {
|
||||
let entityIdx: EntityIndex = entityId.index
|
||||
let traitHash: FamilyTraitSetHash = family.traits.hashValue
|
||||
let traits: FamilyTraitSet = family.traits
|
||||
let traitHash: FamilyTraitSetHash = traits.hashValue
|
||||
guard let componentIds: ComponentIdentifiers = componentIdsByEntity[entityIdx] else { return }
|
||||
|
||||
let trash: TraitEntityIdHash = calculateTraitEntityIdHash(traitHash: traitHash, entityIdx: entityIdx)
|
||||
let is_Member: Bool = isMember(byHash: trash)
|
||||
let is_Member: Bool = isMember(entityId, in: family)
|
||||
|
||||
let componentsSet: ComponentSet = ComponentSet.init(componentIds)
|
||||
let isMatch: Bool = family.traits.isMatch(components: componentsSet)
|
||||
let isMatch: Bool = traits.isMatch(components: componentsSet)
|
||||
switch (isMatch, is_Member) {
|
||||
case (true, false):
|
||||
add(to: family, entityId: entityId, with: trash)
|
||||
add(to: traitHash, entityId: entityId, entityIdx: entityIdx)
|
||||
notify(FamilyMemberAdded(member: entityId, to: traits))
|
||||
case (false, true):
|
||||
remove(from: family, entityId: entityId, with: trash)
|
||||
remove(from: traitHash, entityId: entityId, entityIdx: entityIdx)
|
||||
notify(FamilyMemberRemoved(member: entityId, from: traits))
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func add(to family: Family, entityId: EntityIdentifier, with traitEntityIdHash: TraitEntityIdHash) {
|
||||
let traitHash: FamilyTraitSetHash = family.traits.hashValue
|
||||
if familyMembersByTraitHash[traitHash] != nil {
|
||||
// here we already checked if entity is a member
|
||||
familyMembersByTraitHash[traitHash]!.append(entityId)
|
||||
} else {
|
||||
familyMembersByTraitHash[traitHash] = [EntityIdentifier].init(arrayLiteral: entityId)
|
||||
familyMembersByTraitHash.reserveCapacity(4096)
|
||||
fileprivate func add(to traitHash: FamilyTraitSetHash, entityId: EntityIdentifier, entityIdx: EntityIndex) {
|
||||
if familyMembersByTraitHash[traitHash] == nil {
|
||||
familyMembersByTraitHash[traitHash] = UniformEntityIdentifiers()
|
||||
}
|
||||
familyMembersByTraitHash[traitHash]!.add(entityId, at: entityIdx)
|
||||
}
|
||||
|
||||
familyContainsEntityId[traitEntityIdHash] = true
|
||||
|
||||
notify(FamilyMemberAdded(member: entityId, to: family.traits))
|
||||
}
|
||||
|
||||
fileprivate func remove(from family: Family, entityId: EntityIdentifier, with traitEntityIdHash: TraitEntityIdHash) {
|
||||
let traitHash: FamilyTraitSetHash = family.traits.hashValue
|
||||
|
||||
// FIXME: index of is not cheep
|
||||
guard let indexInFamily = familyMembersByTraitHash[traitHash]?.index(of: entityId) else {
|
||||
assert(false, "removing entity id \(entityId) that is not in family \(family)")
|
||||
report("removing entity id \(entityId) that is not in family \(family)")
|
||||
return
|
||||
}
|
||||
|
||||
let removed: EntityIdentifier = familyMembersByTraitHash[traitHash]!.remove(at: indexInFamily)
|
||||
familyContainsEntityId[traitEntityIdHash] = false
|
||||
notify(FamilyMemberRemoved(member: removed, from: family.traits))
|
||||
fileprivate func remove(from traitHash: FamilyTraitSetHash, entityId: EntityIdentifier, entityIdx: EntityIndex) {
|
||||
familyMembersByTraitHash[traitHash]?.remove(at: entityIdx)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ public typealias ComponentIdsByEntityIndex = Int
|
|||
public typealias ComponentTypeHash = Int // component object identifier hash value
|
||||
//public typealias UniformComponents = SparseComponentSet
|
||||
public typealias UniformComponents = ContiguousComponentArray
|
||||
public typealias UniformEntityIdentifiers = SparseEntityIdentifierSet
|
||||
public typealias ComponentIdentifiers = ContiguousArray<ComponentIdentifier>
|
||||
public typealias ComponentSet = Set<ComponentIdentifier>
|
||||
public typealias Entities = ContiguousArray<Entity>
|
||||
|
|
@ -42,8 +43,7 @@ public class Nexus {
|
|||
var freeEntities: ContiguousArray<EntityIdentifier>
|
||||
|
||||
var familiyByTraitHash: [FamilyTraitSetHash: Family]
|
||||
var familyMembersByTraitHash: [FamilyTraitSetHash: [EntityIdentifier]] // SparseSet for EntityIdentifier
|
||||
var familyContainsEntityId: [TraitEntityIdHash: Bool]
|
||||
var familyMembersByTraitHash: [FamilyTraitSetHash: UniformEntityIdentifiers] // SparseSet for EntityIdentifier
|
||||
|
||||
public init() {
|
||||
entityStorage = Entities()
|
||||
|
|
@ -53,7 +53,7 @@ public class Nexus {
|
|||
freeEntities = ContiguousArray<EntityIdentifier>()
|
||||
familiyByTraitHash = [:]
|
||||
familyMembersByTraitHash = [:]
|
||||
familyContainsEntityId = [:]
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,19 +5,17 @@
|
|||
// Created by Christian Treffs on 30.10.17.
|
||||
//
|
||||
|
||||
public class SparseSet: UniformStorage {
|
||||
|
||||
public typealias Element = Any
|
||||
public class SparseSet<Element>: UniformStorage, Sequence {
|
||||
public typealias Index = Int
|
||||
fileprivate typealias ComponentIdx = Int
|
||||
fileprivate typealias DenseIndex = Int
|
||||
fileprivate var size: Int = 0
|
||||
fileprivate var dense: ContiguousArray<Pair?>
|
||||
fileprivate var sparse: [EntityIndex: ComponentIdx]
|
||||
fileprivate typealias Pair = (key: EntityIndex, value: Element)
|
||||
fileprivate var sparse: [Index: DenseIndex]
|
||||
fileprivate typealias Pair = (key: Index, value: Element)
|
||||
|
||||
public init() {
|
||||
dense = ContiguousArray<Pair?>()
|
||||
sparse = [EntityIndex: ComponentIdx]()
|
||||
sparse = [Index: DenseIndex]()
|
||||
}
|
||||
|
||||
deinit {
|
||||
|
|
@ -28,12 +26,12 @@ public class SparseSet: UniformStorage {
|
|||
internal var capacitySparse: Int { return sparse.capacity }
|
||||
internal var capacityDense: Int { return dense.capacity }
|
||||
|
||||
public func has(_ index: EntityIndex) -> Bool {
|
||||
public func has(_ index: Index) -> Bool {
|
||||
return sparse[index] ?? Int.max < count &&
|
||||
dense[sparse[index]!] != nil
|
||||
}
|
||||
|
||||
public func add(_ element: Element, at index: EntityIndex) {
|
||||
public func add(_ element: Element, at index: Index) {
|
||||
if has(index) { return }
|
||||
sparse[index] = count
|
||||
let entry: Pair = Pair(key: index, value: element)
|
||||
|
|
@ -41,15 +39,15 @@ public class SparseSet: UniformStorage {
|
|||
size += 1
|
||||
}
|
||||
|
||||
public func get(at entityIdx: EntityIndex) -> Element? {
|
||||
public func get(at entityIdx: Index) -> Element? {
|
||||
guard has(entityIdx) else { return nil }
|
||||
return dense[sparse[entityIdx]!]!.value
|
||||
}
|
||||
|
||||
public func remove(at index: EntityIndex) {
|
||||
public func remove(at index: Index) {
|
||||
guard has(index) else { return }
|
||||
let compIdx: ComponentIdx = sparse[index]!
|
||||
let lastIdx: ComponentIdx = count-1
|
||||
let compIdx: DenseIndex = sparse[index]!
|
||||
let lastIdx: DenseIndex = count-1
|
||||
dense.swapAt(compIdx, lastIdx)
|
||||
sparse[index] = nil
|
||||
let swapped: Pair = dense[compIdx]!
|
||||
|
|
@ -67,19 +65,23 @@ public class SparseSet: UniformStorage {
|
|||
sparse.removeAll(keepingCapacity: keepingCapacity)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension SparseSet: Sequence {
|
||||
|
||||
public func makeIterator() -> AnyIterator<Element> {
|
||||
var iterator = dense.makeIterator()
|
||||
var iter = dense.makeIterator()
|
||||
return AnyIterator<Element> {
|
||||
iterator.next()??.value
|
||||
guard let next: Pair? = iter.next() else { return nil }
|
||||
guard let pair: Pair = next else { return nil }
|
||||
return pair.value
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class SparseComponentSet: SparseSet {
|
||||
public typealias Element = Component
|
||||
public class SparseComponentSet: SparseSet<Component> {
|
||||
public typealias Index = EntityIndex
|
||||
|
||||
}
|
||||
|
||||
public class SparseEntityIdentifierSet: SparseSet<EntityIdentifier> {
|
||||
public typealias Index = EntityIndex
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue