Conform Component + Entity Identifier to Identifiable protocol

This commit is contained in:
Christian Treffs 2019-10-05 10:03:08 +02:00
parent 2270191f2e
commit 031db2c1e3
10 changed files with 66 additions and 31 deletions

View File

@ -6,11 +6,11 @@
//
/// Identifies a component by it's meta type
public struct ComponentIdentifier {
@usableFromInline let objectIdentifier: ObjectIdentifier
public struct ComponentIdentifier: Identifiable {
public let id: ObjectIdentifier
init<T>(_ type: T.Type) where T: Component {
self.objectIdentifier = ObjectIdentifier(type)
self.id = ObjectIdentifier(type)
}
}

View File

@ -5,16 +5,17 @@
// Created by Christian Treffs on 08.10.17.
//
public struct EntityIdentifier {
public static let invalid = EntityIdentifier(.max)
public struct EntityIdentifier: Identifiable {
/// provides 4294967295 unique identifiers since it's constrained to UInt32 - invalid.
public let index: Int
public let id: Int
public init(_ uint32: UInt32) {
self.index = Int(uint32)
self.id = Int(uint32)
}
}
extension EntityIdentifier {
public static let invalid = EntityIdentifier(.max)
}
extension EntityIdentifier: Equatable { }
extension EntityIdentifier: Hashable { }
@ -22,6 +23,6 @@ extension EntityIdentifier: Codable { }
extension EntityIdentifier: Comparable {
@inlinable
public static func < (lhs: EntityIdentifier, rhs: EntityIdentifier) -> Bool {
return lhs.index < rhs.index
return lhs.id < rhs.id
}
}

View File

@ -66,14 +66,14 @@ public func hash<H: Sequence>(combine hashables: H) -> Int where H.Element: Hash
// MARK: - entity component hash
extension EntityComponentHash {
internal static func compose(entityId: EntityIdentifier, componentTypeHash: ComponentTypeHash) -> EntityComponentHash {
let entityIdSwapped = UInt(entityId.index).byteSwapped // needs to be 64 bit
let entityIdSwapped = UInt(entityId.id).byteSwapped // needs to be 64 bit
let componentTypeHashUInt = UInt(bitPattern: componentTypeHash)
let hashUInt: UInt = componentTypeHashUInt ^ entityIdSwapped
return Int(bitPattern: hashUInt)
}
internal static func decompose(_ hash: EntityComponentHash, with entityId: EntityIdentifier) -> ComponentTypeHash {
let entityIdSwapped = UInt(entityId.index).byteSwapped
let entityIdSwapped = UInt(entityId.id).byteSwapped
let entityIdSwappedInt = Int(bitPattern: entityIdSwapped)
return hash ^ entityIdSwappedInt
}

View File

@ -0,0 +1,34 @@
//
// Identifiable.swift
//
//
// Created by Christian Treffs on 05.10.19.
//
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2019 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#if swift(<5.1)
/// A class of types whose instances hold the value of an entity with stable identity.
public protocol Identifiable {
/// A type representing the stable identity of the entity associated with `self`.
associatedtype ID: Hashable
/// The stable identity of the entity associated with `self`.
var id: ID { get }
}
extension Identifiable where Self: AnyObject {
public var id: ObjectIdentifier {
return ObjectIdentifier(self)
}
}
#endif

View File

@ -14,7 +14,7 @@ extension Nexus {
guard let uniforms = componentsByType[componentId] else {
return false
}
return uniforms.contains(entityId.index)
return uniforms.contains(entityId.id)
}
public final func count(components entityId: EntityIdentifier) -> Int {
@ -36,7 +36,7 @@ extension Nexus {
if componentsByType[componentId] == nil {
componentsByType[componentId] = ManagedContiguousArray<Component>()
}
componentsByType[componentId]?.insert(component, at: entityId.index)
componentsByType[componentId]?.insert(component, at: entityId.id)
// assigns the component id to the entity id
if componentIdsByEntity[entityId] == nil {
@ -58,13 +58,13 @@ extension Nexus {
guard let uniformComponents = componentsByType[componentId] else {
return nil
}
return uniformComponents.get(at: entityId.index)
return uniformComponents.get(at: entityId.id)
}
@inlinable
public final func get(unsafeComponent componentId: ComponentIdentifier, for entityId: EntityIdentifier) -> Component {
let uniformComponents = componentsByType[componentId].unsafelyUnwrapped
return uniformComponents.get(unsafeAt: entityId.index)
return uniformComponents.get(unsafeAt: entityId.id)
}
@inlinable
@ -88,7 +88,7 @@ extension Nexus {
@discardableResult
public final func remove(component componentId: ComponentIdentifier, from entityId: EntityIdentifier) -> Bool {
// delete component instance
componentsByType[componentId]?.remove(at: entityId.index)
componentsByType[componentId]?.remove(at: entityId.id)
// unasign component from entity
componentIdsByEntity[entityId]?.remove(componentId)
@ -117,6 +117,6 @@ extension Nexus {
guard let uniformComponents = componentsByType[componentId] else {
return nil
}
return uniformComponents.get(at: entityId.index) as? C
return uniformComponents.get(at: entityId.id) as? C
}
}

View File

@ -18,7 +18,7 @@ extension Nexus {
public func createEntity() -> Entity {
let newEntityIdentifier: EntityIdentifier = nextEntityId()
let newEntity = Entity(nexus: self, id: newEntityIdentifier)
entityStorage.insert(newEntity, at: newEntityIdentifier.index)
entityStorage.insert(newEntity, at: newEntityIdentifier.id)
delegate?.nexusEvent(EntityCreated(entityId: newEntityIdentifier))
return newEntity
}
@ -36,22 +36,22 @@ extension Nexus {
}
public func exists(entity entityId: EntityIdentifier) -> Bool {
return entityStorage.contains(entityId.index)
return entityStorage.contains(entityId.id)
}
public func get(entity entityId: EntityIdentifier) -> Entity? {
return entityStorage.get(at: entityId.index)
return entityStorage.get(at: entityId.id)
}
public func get(unsafeEntity entityId: EntityIdentifier) -> Entity {
return entityStorage.get(unsafeAt: entityId.index)
return entityStorage.get(unsafeAt: entityId.id)
}
@discardableResult
public func destroy(entity: Entity) -> Bool {
let entityId: EntityIdentifier = entity.identifier
guard entityStorage.remove(at: entityId.index) != nil else {
guard entityStorage.remove(at: entityId.id) != nil else {
delegate?.nexusNonFatalError("EntityRemove failure: no entity \(entityId) to remove")
return false
}

View File

@ -31,6 +31,6 @@ extension Nexus {
}
public func isMember(entity entityId: EntityIdentifier, inFamilyWithTraits traits: FamilyTraitSet) -> Bool {
return members(withFamilyTraits: traits).contains(entityId.index)
return members(withFamilyTraits: traits).contains(entityId.id)
}
}

View File

@ -64,11 +64,11 @@ extension Nexus {
final func add(entityWithId entityId: EntityIdentifier, toFamilyWithTraits traits: FamilyTraitSet) {
precondition(familyMembersByTraits[traits] != nil)
familyMembersByTraits[traits].unsafelyUnwrapped.insert(entityId, at: entityId.index)
familyMembersByTraits[traits].unsafelyUnwrapped.insert(entityId, at: entityId.id)
}
final func remove(entityWithId entityId: EntityIdentifier, fromFamilyWithTraits traits: FamilyTraitSet) {
precondition(familyMembersByTraits[traits] != nil)
familyMembersByTraits[traits].unsafelyUnwrapped.remove(at: entityId.index)
familyMembersByTraits[traits].unsafelyUnwrapped.remove(at: entityId.id)
}
}

View File

@ -11,15 +11,15 @@ import XCTest
class EntityTests: XCTestCase {
func testEntityIdentifierAndIndex() {
let min = EntityIdentifier(.min)
XCTAssertEqual(min.index, Int(UInt32.min))
XCTAssertEqual(min.id, Int(UInt32.min))
let uRand = UInt32.random(in: UInt32.min...UInt32.max)
let rand = EntityIdentifier(uRand)
XCTAssertEqual(rand.index, Int(uRand))
XCTAssertEqual(rand.id, Int(uRand))
let max = EntityIdentifier(.max)
XCTAssertEqual(max, EntityIdentifier.invalid)
XCTAssertEqual(max.index, Int(UInt32.max))
XCTAssertEqual(max.id, Int(UInt32.max))
}
func testEntityIdentifierComparison() {

View File

@ -26,12 +26,12 @@ class NexusTests: XCTestCase {
let e0 = nexus.createEntity()
XCTAssertEqual(e0.identifier.index, 0)
XCTAssertEqual(e0.identifier.id, 0)
XCTAssertEqual(nexus.numEntities, 1)
let e1 = nexus.createEntity(with: Name(name: "Entity 1"))
XCTAssert(e1.identifier.index == 1)
XCTAssert(e1.identifier.id == 1)
XCTAssert(nexus.numEntities == 2)
//FIXME: XCTAssertNil(e0.name)
@ -43,7 +43,7 @@ class NexusTests: XCTestCase {
XCTAssertEqual(nexus.numEntities, 2)
let e1: Entity = nexus.get(entity: EntityIdentifier(1))!
XCTAssertEqual(e1.identifier.index, 1)
XCTAssertEqual(e1.identifier.id, 1)
XCTAssertTrue(nexus.destroy(entity: e1))
XCTAssertFalse(nexus.destroy(entity: e1))