Generalize entity identifier generator
This commit is contained in:
parent
d210fe378b
commit
552665edd0
|
|
@ -6,15 +6,15 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
public struct EntityIdentifier {
|
public struct EntityIdentifier {
|
||||||
static let invalid = EntityIdentifier(.max)
|
public static let invalid = EntityIdentifier(.max)
|
||||||
|
|
||||||
public typealias Idx = Int
|
public typealias Idx = Int
|
||||||
|
|
||||||
/// provides 4294967295 unique identifiers since it's constrained to UInt32 - invalid.
|
/// provides 4294967295 unique identifiers since it's constrained to UInt32 - invalid.
|
||||||
@usableFromInline let id: Idx
|
@usableFromInline let id: Idx
|
||||||
|
|
||||||
@usableFromInline
|
@inlinable
|
||||||
init(_ uint32: UInt32) {
|
public init(_ uint32: UInt32) {
|
||||||
self.id = Idx(uint32)
|
self.id = Idx(uint32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,31 +5,78 @@
|
||||||
// Created by Christian Treffs on 26.06.20.
|
// Created by Christian Treffs on 26.06.20.
|
||||||
//
|
//
|
||||||
|
|
||||||
internal final class EntityIdentifierGenerator {
|
/// An entity identifier generator provides new entity
|
||||||
private var stack: [UInt32]
|
/// identifiers on entity creation.
|
||||||
|
/// It also allows entity ids to be marked for re-use.
|
||||||
|
/// Entity identifiers must be unique.
|
||||||
|
public protocol EntityIdentifierGenerator {
|
||||||
|
/// Initialize the generator with entity ids already in use.
|
||||||
|
/// - Parameter entityIds: The entity ids already in use. Default should be an empty array.
|
||||||
|
init(inUse entityIds: [EntityIdentifier])
|
||||||
|
|
||||||
var count: Int {
|
/// Provides the next unused entity identifier.
|
||||||
stack.count
|
///
|
||||||
}
|
/// The provided entity identifier is at least unique during runtime.
|
||||||
|
func nextId() -> EntityIdentifier
|
||||||
|
|
||||||
convenience init() {
|
/// Marks the given entity identifier as free and ready for re-use.
|
||||||
self.init([EntityIdentifier(0)])
|
///
|
||||||
}
|
/// Unused entity identifiers will again be provided with `nextId()`.
|
||||||
|
/// - Parameter entityId: The entity id to be marked as unused.
|
||||||
|
func markUnused(entityId: EntityIdentifier)
|
||||||
|
}
|
||||||
|
|
||||||
init(_ entityIds: [EntityIdentifier]) {
|
/// A default entity identifier generator implementation.
|
||||||
stack = entityIds.reversed().map { UInt32($0.id) }
|
///
|
||||||
}
|
/// Provides entity ids starting at `0` incrementing until `UInt32.max`.
|
||||||
|
public struct DefaultEntityIdGenerator: EntityIdentifierGenerator {
|
||||||
|
@usableFromInline
|
||||||
|
final class Storage {
|
||||||
|
@usableFromInline var stack: [UInt32]
|
||||||
|
@usableFromInline var count: Int { stack.count }
|
||||||
|
|
||||||
func nextId() -> EntityIdentifier {
|
@usableFromInline
|
||||||
if stack.count == 1 {
|
init(inUse entityIds: [EntityIdentifier]) {
|
||||||
defer { stack[0] += 1 }
|
stack = entityIds.reversed().map { UInt32($0.id) }
|
||||||
return EntityIdentifier(stack[0])
|
}
|
||||||
} else {
|
|
||||||
return EntityIdentifier(stack.removeLast())
|
@usableFromInline
|
||||||
|
func nextId() -> EntityIdentifier {
|
||||||
|
if stack.count == 1 {
|
||||||
|
defer { stack[0] += 1 }
|
||||||
|
return EntityIdentifier(stack[0])
|
||||||
|
} else {
|
||||||
|
return EntityIdentifier(stack.removeLast())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@usableFromInline
|
||||||
|
func markUnused(entityId: EntityIdentifier) {
|
||||||
|
stack.append(UInt32(entityId.id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func freeId(_ entityId: EntityIdentifier) {
|
@usableFromInline let storage: Storage
|
||||||
stack.append(UInt32(entityId.id))
|
|
||||||
|
@usableFromInline var count: Int { storage.count }
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
public init() {
|
||||||
|
self.init(inUse: [EntityIdentifier(0)])
|
||||||
|
}
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
public init(inUse entityIds: [EntityIdentifier]) {
|
||||||
|
self.storage = Storage(inUse: entityIds)
|
||||||
|
}
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
public func nextId() -> EntityIdentifier {
|
||||||
|
storage.nextId()
|
||||||
|
}
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
public func markUnused(entityId: EntityIdentifier) {
|
||||||
|
storage.markUnused(entityId: entityId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ extension Nexus {
|
||||||
update(familyMembership: entityId)
|
update(familyMembership: entityId)
|
||||||
}
|
}
|
||||||
|
|
||||||
entityIdGenerator.freeId(entityId)
|
entityIdGenerator.markUnused(entityId: entityId)
|
||||||
|
|
||||||
delegate?.nexusEvent(EntityDestroyed(entityId: entityId))
|
delegate?.nexusEvent(EntityDestroyed(entityId: entityId))
|
||||||
return true
|
return true
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,6 @@ public final class Nexus {
|
||||||
/// Entities are tightly packed by EntityIdentifier.
|
/// Entities are tightly packed by EntityIdentifier.
|
||||||
@usableFromInline final var entityStorage: UnorderedSparseSet<EntityIdentifier, EntityIdentifier.Idx>
|
@usableFromInline final var entityStorage: UnorderedSparseSet<EntityIdentifier, EntityIdentifier.Idx>
|
||||||
|
|
||||||
/// Entity ids that are currently not used.
|
|
||||||
let entityIdGenerator: EntityIdentifierGenerator
|
|
||||||
|
|
||||||
/// - Key: ComponentIdentifier aka component type.
|
/// - Key: ComponentIdentifier aka component type.
|
||||||
/// - Value: Array of component instances of same type (uniform).
|
/// - Value: Array of component instances of same type (uniform).
|
||||||
/// New component instances are appended.
|
/// New component instances are appended.
|
||||||
|
|
@ -27,6 +24,16 @@ public final class Nexus {
|
||||||
/// - Value: Tightly packed EntityIdentifiers that represent the association of an entity to the family.
|
/// - Value: Tightly packed EntityIdentifiers that represent the association of an entity to the family.
|
||||||
@usableFromInline final var familyMembersByTraits: [FamilyTraitSet: UnorderedSparseSet<EntityIdentifier, EntityIdentifier.Idx>]
|
@usableFromInline final var familyMembersByTraits: [FamilyTraitSet: UnorderedSparseSet<EntityIdentifier, EntityIdentifier.Idx>]
|
||||||
|
|
||||||
|
/// The entity identifier generator responsible for providing unique ids for entities during runtime.
|
||||||
|
///
|
||||||
|
/// Provide a custom implementation prior to entity creation.
|
||||||
|
/// Defaults to `DefaultEntityIdGenerator`.
|
||||||
|
public final var entityIdGenerator: EntityIdentifierGenerator
|
||||||
|
|
||||||
|
/// The coding strategy used to encode/decode entities from/into families.
|
||||||
|
///
|
||||||
|
/// Provide a custom implementation prior to encoding/decoding.
|
||||||
|
/// Defaults to `DefaultCodingStrategy`.
|
||||||
public final var codingStrategy: CodingStrategy
|
public final var codingStrategy: CodingStrategy
|
||||||
|
|
||||||
public final weak var delegate: NexusEventDelegate?
|
public final weak var delegate: NexusEventDelegate?
|
||||||
|
|
@ -35,7 +42,7 @@ public final class Nexus {
|
||||||
self.init(entityStorage: UnorderedSparseSet<EntityIdentifier, EntityIdentifier.Idx>(),
|
self.init(entityStorage: UnorderedSparseSet<EntityIdentifier, EntityIdentifier.Idx>(),
|
||||||
componentsByType: [:],
|
componentsByType: [:],
|
||||||
componentsByEntity: [:],
|
componentsByEntity: [:],
|
||||||
entityIdGenerator: EntityIdentifierGenerator(),
|
entityIdGenerator: DefaultEntityIdGenerator(),
|
||||||
familyMembersByTraits: [:],
|
familyMembersByTraits: [:],
|
||||||
codingStrategy: DefaultCodingStrategy())
|
codingStrategy: DefaultCodingStrategy())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ class EntityTests: XCTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testEntityIdGenerator() {
|
func testEntityIdGenerator() {
|
||||||
let generator = EntityIdentifierGenerator()
|
let generator = DefaultEntityIdGenerator()
|
||||||
|
|
||||||
XCTAssertEqual(generator.count, 1)
|
XCTAssertEqual(generator.count, 1)
|
||||||
|
|
||||||
|
|
@ -70,7 +70,7 @@ class EntityTests: XCTestCase {
|
||||||
XCTAssertEqual(generator.count, 1)
|
XCTAssertEqual(generator.count, 1)
|
||||||
|
|
||||||
for i in 10..<60 {
|
for i in 10..<60 {
|
||||||
generator.freeId(EntityIdentifier(UInt32(i)))
|
generator.markUnused(entityId: EntityIdentifier(UInt32(i)))
|
||||||
}
|
}
|
||||||
|
|
||||||
XCTAssertEqual(generator.count, 51)
|
XCTAssertEqual(generator.count, 51)
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,6 @@ class SystemsTests: XCTestCase {
|
||||||
XCTAssertEqual(nexus.numEntities, 0)
|
XCTAssertEqual(nexus.numEntities, 0)
|
||||||
XCTAssertEqual(colorSystem.colors.memberIds.count, 0)
|
XCTAssertEqual(colorSystem.colors.memberIds.count, 0)
|
||||||
XCTAssertEqual(positionSystem.positions.memberIds.count, 0)
|
XCTAssertEqual(positionSystem.positions.memberIds.count, 0)
|
||||||
XCTAssertEqual(nexus.entityIdGenerator.count, 1)
|
|
||||||
XCTAssertEqual(nexus.familyMembersByTraits[posTraits]?.count, 0)
|
XCTAssertEqual(nexus.familyMembersByTraits[posTraits]?.count, 0)
|
||||||
|
|
||||||
batchCreateEntities(count: num)
|
batchCreateEntities(count: num)
|
||||||
|
|
@ -47,7 +46,6 @@ class SystemsTests: XCTestCase {
|
||||||
XCTAssertEqual(nexus.familyMembersByTraits[posTraits]?.count, num)
|
XCTAssertEqual(nexus.familyMembersByTraits[posTraits]?.count, num)
|
||||||
XCTAssertEqual(colorSystem.colors.memberIds.count, num)
|
XCTAssertEqual(colorSystem.colors.memberIds.count, num)
|
||||||
XCTAssertEqual(positionSystem.positions.memberIds.count, num)
|
XCTAssertEqual(positionSystem.positions.memberIds.count, num)
|
||||||
XCTAssertEqual(nexus.entityIdGenerator.count, 1)
|
|
||||||
|
|
||||||
colorSystem.update()
|
colorSystem.update()
|
||||||
positionSystem.update()
|
positionSystem.update()
|
||||||
|
|
@ -56,7 +54,6 @@ class SystemsTests: XCTestCase {
|
||||||
XCTAssertEqual(nexus.familyMembersByTraits[posTraits]?.count, num)
|
XCTAssertEqual(nexus.familyMembersByTraits[posTraits]?.count, num)
|
||||||
XCTAssertEqual(colorSystem.colors.memberIds.count, num)
|
XCTAssertEqual(colorSystem.colors.memberIds.count, num)
|
||||||
XCTAssertEqual(positionSystem.positions.memberIds.count, num)
|
XCTAssertEqual(positionSystem.positions.memberIds.count, num)
|
||||||
XCTAssertEqual(nexus.entityIdGenerator.count, 1)
|
|
||||||
|
|
||||||
batchCreateEntities(count: num)
|
batchCreateEntities(count: num)
|
||||||
|
|
||||||
|
|
@ -64,7 +61,6 @@ class SystemsTests: XCTestCase {
|
||||||
XCTAssertEqual(nexus.familyMembersByTraits[posTraits]?.count, num * 2)
|
XCTAssertEqual(nexus.familyMembersByTraits[posTraits]?.count, num * 2)
|
||||||
XCTAssertEqual(colorSystem.colors.memberIds.count, num * 2)
|
XCTAssertEqual(colorSystem.colors.memberIds.count, num * 2)
|
||||||
XCTAssertEqual(positionSystem.positions.memberIds.count, num * 2)
|
XCTAssertEqual(positionSystem.positions.memberIds.count, num * 2)
|
||||||
XCTAssertEqual(nexus.entityIdGenerator.count, 1)
|
|
||||||
|
|
||||||
colorSystem.update()
|
colorSystem.update()
|
||||||
positionSystem.update()
|
positionSystem.update()
|
||||||
|
|
@ -73,12 +69,10 @@ class SystemsTests: XCTestCase {
|
||||||
XCTAssertEqual(nexus.familyMembersByTraits[posTraits]?.count, num * 2)
|
XCTAssertEqual(nexus.familyMembersByTraits[posTraits]?.count, num * 2)
|
||||||
XCTAssertEqual(colorSystem.colors.memberIds.count, num * 2)
|
XCTAssertEqual(colorSystem.colors.memberIds.count, num * 2)
|
||||||
XCTAssertEqual(positionSystem.positions.memberIds.count, num * 2)
|
XCTAssertEqual(positionSystem.positions.memberIds.count, num * 2)
|
||||||
XCTAssertEqual(nexus.entityIdGenerator.count, 1)
|
|
||||||
|
|
||||||
batchDestroyEntities(count: num)
|
batchDestroyEntities(count: num)
|
||||||
|
|
||||||
XCTAssertEqual(nexus.familyMembersByTraits[posTraits]?.count, num)
|
XCTAssertEqual(nexus.familyMembersByTraits[posTraits]?.count, num)
|
||||||
XCTAssertEqual(nexus.entityIdGenerator.count, 1 + num)
|
|
||||||
XCTAssertEqual(nexus.numEntities, num)
|
XCTAssertEqual(nexus.numEntities, num)
|
||||||
XCTAssertEqual(colorSystem.colors.memberIds.count, num)
|
XCTAssertEqual(colorSystem.colors.memberIds.count, num)
|
||||||
XCTAssertEqual(positionSystem.positions.memberIds.count, num)
|
XCTAssertEqual(positionSystem.positions.memberIds.count, num)
|
||||||
|
|
@ -90,7 +84,6 @@ class SystemsTests: XCTestCase {
|
||||||
XCTAssertEqual(nexus.numEntities, num)
|
XCTAssertEqual(nexus.numEntities, num)
|
||||||
XCTAssertEqual(colorSystem.colors.memberIds.count, num)
|
XCTAssertEqual(colorSystem.colors.memberIds.count, num)
|
||||||
XCTAssertEqual(positionSystem.positions.memberIds.count, num)
|
XCTAssertEqual(positionSystem.positions.memberIds.count, num)
|
||||||
XCTAssertEqual(nexus.entityIdGenerator.count, 1 + num)
|
|
||||||
|
|
||||||
batchCreateEntities(count: num)
|
batchCreateEntities(count: num)
|
||||||
|
|
||||||
|
|
@ -98,7 +91,6 @@ class SystemsTests: XCTestCase {
|
||||||
XCTAssertEqual(nexus.numEntities, num * 2)
|
XCTAssertEqual(nexus.numEntities, num * 2)
|
||||||
XCTAssertEqual(colorSystem.colors.memberIds.count, num * 2)
|
XCTAssertEqual(colorSystem.colors.memberIds.count, num * 2)
|
||||||
XCTAssertEqual(positionSystem.positions.memberIds.count, num * 2)
|
XCTAssertEqual(positionSystem.positions.memberIds.count, num * 2)
|
||||||
XCTAssertEqual(nexus.entityIdGenerator.count, 1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func createDefaultEntity() {
|
func createDefaultEntity() {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue