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