new implementation - first test working
This commit is contained in:
parent
e16759f9f1
commit
4e0522aa49
|
|
@ -10,12 +10,34 @@ public protocol Component: UniqueComponentIdentifiable {}
|
|||
// MARK: UCI
|
||||
extension Component {
|
||||
/// Uniquely identifies the component by its meta type
|
||||
public static var uct: UCT { return UCT(Self.self) }
|
||||
public static var identifier: ComponentIdentifier { return ComponentIdentifier(Self.self) }
|
||||
/// Uniquely identifies the component by its meta type
|
||||
public var uct: UCT { return Self.uct }
|
||||
public var identifier: ComponentIdentifier { return Self.identifier }
|
||||
}
|
||||
|
||||
// MARK: Equatable
|
||||
public func ==<A: Component, B: Component>(lhs: A, rhs: B) -> Bool {
|
||||
return A.uct == B.uct
|
||||
return A.identifier == B.identifier // FIXME: this may be wrong
|
||||
}
|
||||
|
||||
// MARK: - entity component hashable
|
||||
public extension Component {
|
||||
|
||||
/// Provides XOR hash value from component identifier (aka type) and entity index.
|
||||
/// Is only stable for app runtime.
|
||||
///
|
||||
/// - Parameter entityIdx: entity index
|
||||
/// - Returns: combinded entity component hash
|
||||
static func hashValue(using entityIdx: EntityIndex) -> EntityComponentHash {
|
||||
return Self.identifier.hashValue(using: entityIdx)
|
||||
}
|
||||
|
||||
/// Provides XOR hash value from component identifier (aka type) and entity index.
|
||||
/// Is only stable for app runtime.
|
||||
///
|
||||
/// - Parameter entityIdx: entity index
|
||||
/// - Returns: combinded entity component hash
|
||||
func hashValue(using entityIdx: EntityIndex) -> EntityComponentHash {
|
||||
return Self.hashValue(using: entityIdx)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// ComponentIdentifier.swift
|
||||
// FirebladeECS
|
||||
//
|
||||
// Created by Christian Treffs on 08.10.17.
|
||||
//
|
||||
|
||||
public typealias ComponentIdentifier = ObjectIdentifier
|
||||
|
||||
extension ComponentIdentifier {
|
||||
|
||||
/// Provides XOR hash value from component identifier (aka type) and entity index.
|
||||
/// Is only stable for app runtime.
|
||||
///
|
||||
/// - Parameter entityIdx: entity index
|
||||
/// - Returns: combinded entity component hash
|
||||
func hashValue(using entityIdx: EntityIndex) -> EntityComponentHash {
|
||||
return self.hashValue ^ entityIdx
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Unique Component Identifiable
|
||||
public protocol UniqueComponentIdentifiable {
|
||||
static var identifier: ComponentIdentifier { get }
|
||||
var identifier: ComponentIdentifier { get }
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
//
|
||||
// ComponentStorage.swift
|
||||
// FirebladeECS
|
||||
//
|
||||
// Created by Christian Treffs on 10.10.17.
|
||||
//
|
||||
|
||||
/*
|
||||
public typealias ComponentIndex = Int
|
||||
public protocol ComponentStorage {
|
||||
|
||||
@discardableResult func add<C: Component>(_ component: C) -> Bool
|
||||
|
||||
func makeIterator<C: Component>(_ componentType: C.Type) -> AnyIterator<C>
|
||||
func makeIterator(_ componentId: ComponentIdentifier) -> AnyIterator<Component>
|
||||
|
||||
func has<C: Component>(_ component: C) -> Bool
|
||||
func has<C: Component>(_ componentType: C.Type) -> Bool
|
||||
func has(_ componentId: ComponentIdentifier) -> Bool
|
||||
|
||||
func get<C: Component>(_ componentType: C.Type) -> Component?
|
||||
subscript<C: Component>(_ componentType: C.Type) -> Component? { get }
|
||||
|
||||
func get(_ componentId: ComponentIdentifier) -> Component?
|
||||
subscript(_ componentId: ComponentIdentifier) -> Component? { get }
|
||||
|
||||
@discardableResult func remove<C: Component>(_ component: C) -> Bool
|
||||
@discardableResult func remove<C: Component>(_ componentType: C.Type) -> Bool
|
||||
@discardableResult func remove(_ componentId: ComponentIdentifier) -> Bool
|
||||
|
||||
func clear()
|
||||
}
|
||||
|
||||
class GeneralComponentStorage: ComponentStorage {
|
||||
|
||||
fileprivate var componentMap: [ComponentIdentifier: ContiguousArray<Component>] = [:]
|
||||
|
||||
|
||||
func add<C>(_ component: C) -> Bool where C : Component {
|
||||
if var comps: ContiguousArray<Component> = componentMap[component.uct] {
|
||||
comps.append(component)
|
||||
} else {
|
||||
componentMap[component.uct] = ContiguousArray<Component>()
|
||||
componentMap[component.uct]!.append(component)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func makeIterator<C>(_ componentType: C.Type) -> AnyIterator<C> where C : Component {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
func makeIterator(_ componentId: ComponentIdentifier) -> AnyIterator<Component> {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
func has<C>(_ component: C) -> Bool where C : Component {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
func has<C>(_ componentType: C.Type) -> Bool where C : Component {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
func has(_ componentId: ComponentIdentifier) -> Bool {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
func get<C>(_ componentType: C.Type) -> Component? where C : Component {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
subscript<C>(componentType: C.Type) -> Component? where C: Component {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
func get(_ componentId: ComponentIdentifier) -> Component? {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
subscript(componentId: ComponentIdentifier) -> Component? {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
func remove<C>(_ component: C) -> Bool where C : Component {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
func remove<C>(_ componentType: C.Type) -> Bool where C : Component {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
func remove(_ componentId: ComponentIdentifier) -> Bool {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
func clear() {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
|
|
@ -6,109 +6,78 @@
|
|||
//
|
||||
|
||||
public final class Entity: UniqueEntityIdentifiable {
|
||||
public let uei: UEI
|
||||
|
||||
public let identifier: EntityIdentifier
|
||||
public var name: String?
|
||||
|
||||
@available(*, deprecated: 0.1, message: "replace this with core/context concept")
|
||||
fileprivate var eventDispatcher: EventDispatcher
|
||||
fileprivate let nexus: Nexus
|
||||
|
||||
public private(set) var componentMap: [UCT: Component]
|
||||
|
||||
init(uei: UEI, dispatcher: EventDispatcher) {
|
||||
self.uei = uei
|
||||
self.eventDispatcher = dispatcher
|
||||
componentMap = [UCT: Component]()
|
||||
componentMap.reserveCapacity(2)
|
||||
defer {
|
||||
notifyInit()
|
||||
}
|
||||
init(nexus: Nexus, id: EntityIdentifier, name: String? = nil) {
|
||||
self.nexus = nexus
|
||||
self.identifier = id
|
||||
self.name = name
|
||||
}
|
||||
|
||||
deinit {
|
||||
defer {
|
||||
destroy()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Equatable
|
||||
public func ==(lhs: Entity, rhs: Entity) -> Bool {
|
||||
return lhs.uei == rhs.uei
|
||||
return lhs.identifier == rhs.identifier
|
||||
}
|
||||
|
||||
// MARK: - number of components
|
||||
public extension Entity {
|
||||
public final var numComponents: Int { return componentMap.count }
|
||||
public final var numComponents: Int {
|
||||
return nexus.count(components: identifier)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - has component(s)
|
||||
public extension Entity {
|
||||
|
||||
public final func has(_ component: Component.Type) -> Bool {
|
||||
return has(UCT(component))
|
||||
public final func has<C>(_ type: C.Type) -> Bool where C: Component {
|
||||
return has(type.identifier)
|
||||
}
|
||||
|
||||
public final func has(_ component: UCT) -> Bool {
|
||||
return componentMap[component] != nil
|
||||
public final func has(_ uct: ComponentIdentifier) -> Bool {
|
||||
return nexus.has(component: uct, entity: identifier)
|
||||
}
|
||||
|
||||
public final var hasComponents: Bool { return !componentMap.isEmpty }
|
||||
public final var hasComponents: Bool {
|
||||
return nexus.count(components: identifier) > 0
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - add/push component(s)
|
||||
// MARK: - add component(s)
|
||||
public extension Entity {
|
||||
|
||||
@discardableResult
|
||||
public final func push<C: Component>(component: C) -> Entity {
|
||||
let previousComponent: C? = componentMap.updateValue(component, forKey: component.uct) as? C
|
||||
if let pComp: C = previousComponent {
|
||||
notify(update: component, previous: pComp)
|
||||
} else {
|
||||
notify(add: component)
|
||||
}
|
||||
public final func add<C>(_ component: C) -> Entity where C: Component {
|
||||
nexus.add(component: component, to: identifier)
|
||||
return self
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public final func add<C: Component>(component: C) -> Entity {
|
||||
return push(component: component)
|
||||
public static func += <C>(lhs: Entity, rhs: C) -> Entity where C: Component {
|
||||
return lhs.add(rhs)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public static func += <C: Component>(lhs: Entity, rhs: C) -> Entity {
|
||||
return lhs.push(component: rhs)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public static func << <C: Component>(lhs: Entity, rhs: C) -> Entity {
|
||||
return lhs.push(component: rhs)
|
||||
public static func << <C>(lhs: Entity, rhs: C) -> Entity where C: Component {
|
||||
return lhs.add(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - peek component
|
||||
// MARK: - get components
|
||||
public extension Entity {
|
||||
|
||||
public final func peekComponent<C: Component>() -> C? {
|
||||
return componentMap[C.uct] as? C
|
||||
public final func get<C>() -> C? where C: Component {
|
||||
return nexus.get(component: C.identifier, for: identifier)
|
||||
}
|
||||
|
||||
public final func peek<C: Component>(_ componentType: C.Type) -> C? {
|
||||
return componentMap[componentType.uct] as? C
|
||||
}
|
||||
|
||||
public final func peek<C: Component>(_ uct: UCT) -> C? {
|
||||
return componentMap[uct] as? C
|
||||
}
|
||||
|
||||
public final func peek<C: Component>(_ unwrapping: (C?) -> C) -> C {
|
||||
return unwrapping(peekComponent())
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public final func peek<C: Component, Result>(_ applying: (C?) -> Result) -> Result {
|
||||
return applying(peekComponent())
|
||||
public final func get<C>(_ componentType: C.Type) -> C? where C: Component {
|
||||
return get()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -116,40 +85,32 @@ public extension Entity {
|
|||
public extension Entity {
|
||||
|
||||
@discardableResult
|
||||
public final func remove(_ component: Component) -> Entity {
|
||||
return remove(component.uct)
|
||||
public final func remove<C>(_ component: C) -> Entity where C: Component {
|
||||
return remove(component.identifier)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public final func remove<C: Component>(_ componentType: C.Type) -> Entity {
|
||||
let removedComponent: C? = componentMap.removeValue(forKey: C.uct) as? C
|
||||
if let rComp: C = removedComponent {
|
||||
notify(removed: rComp)
|
||||
}
|
||||
public final func remove<C>(_ componentType: C.Type) -> Entity where C: Component {
|
||||
return remove(componentType.identifier)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public final func remove(_ uct: ComponentIdentifier) -> Entity {
|
||||
nexus.remove(component: uct, from: identifier)
|
||||
return self
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public final func remove(_ uct: UCT) -> Entity {
|
||||
let removedComponent = componentMap.removeValue(forKey: uct)
|
||||
if let rComp = removedComponent {
|
||||
assert(rComp.uct.type == uct.type)
|
||||
notify(removed: rComp)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
public final func removeAll() {
|
||||
componentMap.forEach { remove($0.key) }
|
||||
public final func clear() {
|
||||
nexus.clear(componentes: identifier)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public static func -= <C: Component>(lhs: Entity, rhs: C) -> Entity {
|
||||
public static func -= <C>(lhs: Entity, rhs: C) -> Entity where C: Component {
|
||||
return lhs.remove(rhs)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public static func -= <C: Component>(lhs: Entity, rhs: C.Type) -> Entity {
|
||||
public static func -= <C>(lhs: Entity, rhs: C.Type) -> Entity where C: Component {
|
||||
return lhs.remove(rhs)
|
||||
}
|
||||
}
|
||||
|
|
@ -157,12 +118,61 @@ public extension Entity {
|
|||
// MARK: - destroy/deinit entity
|
||||
extension Entity {
|
||||
final func destroy() {
|
||||
removeAll()
|
||||
UEI.free(uei)
|
||||
notifyDestoryed()
|
||||
clear()
|
||||
//TODO: notifyDestoryed()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - component tuple access
|
||||
public extension Entity {
|
||||
|
||||
public func component<A>(_: A.Type) -> A where A: Component {
|
||||
guard let a: A = nexus.get(component: A.identifier, for: identifier) else {
|
||||
fatalError("Component Mapping Error: '\(A.self)' component was not found in entity '\(self)'")
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
public func components<A, B>(_: A.Type, _: B.Type) -> (A, B) where A: Component, B: Component {
|
||||
let a: A = component(A.self)
|
||||
let b: B = component(B.self)
|
||||
return (a, b)
|
||||
}
|
||||
public func components<A, B, C>(_: A.Type, _: B.Type, _: C.Type) -> (A, B, C) where A: Component, B: Component, C: Component {
|
||||
let a: A = component(A.self)
|
||||
let b: B = component(B.self)
|
||||
let c: C = component(C.self)
|
||||
return (a, b, c)
|
||||
}
|
||||
public func components<A, B, C, D>(_: A.Type, _: B.Type, _: C.Type, _: D.Type) -> (A, B, C, D) where A: Component, B: Component, C: Component, D: Component {
|
||||
let a: A = component(A.self)
|
||||
let b: B = component(B.self)
|
||||
let c: C = component(C.self)
|
||||
let d: D = component(D.self)
|
||||
return (a, b, c, d)
|
||||
}
|
||||
public func components<A, B, C, D, E>(_: A.Type, _: B.Type, _: C.Type, _: D.Type, _: E.Type) -> (A, B, C, D, E) where A: Component, B: Component, C: Component, D: Component, E: Component {
|
||||
let a: A = component(A.self)
|
||||
let b: B = component(B.self)
|
||||
let c: C = component(C.self)
|
||||
let d: D = component(D.self)
|
||||
let e: E = component(E.self)
|
||||
return (a, b, c, d, e)
|
||||
}
|
||||
public func components<A, B, C, D, E, F>(_: A.Type, _: B.Type, _: C.Type, _: D.Type, _: E.Type, _: F.Type) -> (A, B, C, D, E, F) where A: Component, B: Component, C: Component, D: Component, E: Component, F: Component {
|
||||
let a: A = component(A.self)
|
||||
let b: B = component(B.self)
|
||||
let c: C = component(C.self)
|
||||
let d: D = component(D.self)
|
||||
let e: E = component(E.self)
|
||||
let f: F = component(F.self)
|
||||
return (a, b, c, d, e, f)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
extension Entity: EventDispatcher {
|
||||
public func dispatch<E>(_ event: E) where E: Event {
|
||||
eventDispatcher.dispatch(event)
|
||||
|
|
@ -188,25 +198,25 @@ extension Entity: EventDispatcher {
|
|||
}
|
||||
}
|
||||
|
||||
private func notify<C: Component>(add component: C) {
|
||||
private func notify<C>(add component: C) {
|
||||
unowned {
|
||||
$0.dispatch(ComponentAdded(to: $0))
|
||||
}
|
||||
}
|
||||
|
||||
private func notify<C: Component>(update newComponent: C, previous previousComponent: C) {
|
||||
private func notify<C>(update newComponent: C, previous previousComponent: C) {
|
||||
unowned {
|
||||
$0.dispatch(ComponentUpdated(at: $0))
|
||||
}
|
||||
}
|
||||
|
||||
private func notify<C: Component>(removed component: C) {
|
||||
private func notify<C>(removed component: C) {
|
||||
unowned {
|
||||
$0.dispatch(ComponentRemoved(from: $0))
|
||||
}
|
||||
}
|
||||
|
||||
private func notify(removed component: Component) {
|
||||
private func notify(removed component) {
|
||||
//unowned { /* this keeps a reference since we need it */
|
||||
dispatch(ComponentRemoved(from: self))
|
||||
//}
|
||||
|
|
@ -241,85 +251,4 @@ extension Entity: CustomPlaygroundQuickLookable {
|
|||
return .text(self.description)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - component tuple access
|
||||
public extension Entity {
|
||||
|
||||
public func component<A: Component>(_: A.Type) -> A {
|
||||
guard let a: A = componentMap[A.uct] as? A else {
|
||||
fatalError("Component Mapping Error: '\(A.self)' component was not found in entity '\(self)'")
|
||||
}
|
||||
return a
|
||||
|
||||
}
|
||||
public func components<A: Component, B: Component>(_: A.Type, _: B.Type) -> (A, B) {
|
||||
let a: A = component(A.self)
|
||||
let b: B = component(B.self)
|
||||
return (a, b)
|
||||
}
|
||||
public func components<A: Component, B: Component, C: Component>(_: A.Type, _: B.Type, _: C.Type) -> (A, B, C) {
|
||||
let a: A = component(A.self)
|
||||
let b: B = component(B.self)
|
||||
let c: C = component(C.self)
|
||||
return (a, b, c)
|
||||
}
|
||||
public func components<A: Component, B: Component, C: Component, D: Component>(_: A.Type, _: B.Type, _: C.Type, _: D.Type) -> (A, B, C, D) {
|
||||
let a: A = component(A.self)
|
||||
let b: B = component(B.self)
|
||||
let c: C = component(C.self)
|
||||
let d: D = component(D.self)
|
||||
return (a, b, c, d)
|
||||
}
|
||||
public func components<A: Component, B: Component, C: Component, D: Component, E: Component>(_: A.Type, _: B.Type, _: C.Type, _: D.Type, _: E.Type) -> (A, B, C, D, E) {
|
||||
let a: A = component(A.self)
|
||||
let b: B = component(B.self)
|
||||
let c: C = component(C.self)
|
||||
let d: D = component(D.self)
|
||||
let e: E = component(E.self)
|
||||
return (a, b, c, d, e)
|
||||
}
|
||||
public func components<A: Component, B: Component, C: Component, D: Component, E: Component, F: Component>(_: A.Type, _: B.Type, _: C.Type, _: D.Type, _: E.Type, _: F.Type) -> (A, B, C, D, E, F) {
|
||||
let a: A = component(A.self)
|
||||
let b: B = component(B.self)
|
||||
let c: C = component(C.self)
|
||||
let d: D = component(D.self)
|
||||
let e: E = component(E.self)
|
||||
let f: F = component(F.self)
|
||||
return (a, b, c, d, e, f)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - component closure access
|
||||
public extension Entity {
|
||||
|
||||
public func component<R, A: Component>(_ closure: (A) -> R) -> R { return closure(component(A.self)) }
|
||||
public func components<R, A: Component, B: Component>(_ closure: (A, B) -> R) -> R {
|
||||
return closure(component(A.self), component(B.self))
|
||||
}
|
||||
public func components<R, A: Component, B: Component, C: Component>(_ closure: (A, B, C) -> R) -> R {
|
||||
return closure(component(A.self), component(B.self), component(C.self))
|
||||
}
|
||||
public func components<R, A: Component, B: Component, C: Component, D: Component>(_ closure: (A, B, C, D) -> R) -> R {
|
||||
return closure(component(A.self),
|
||||
component(B.self),
|
||||
component(C.self),
|
||||
component(D.self))
|
||||
}
|
||||
|
||||
public func components<R, A: Component, B: Component, C: Component, D: Component, E: Component>(_ closure: (A, B, C, D, E) -> R) -> R {
|
||||
return closure(component(A.self),
|
||||
component(B.self),
|
||||
component(C.self),
|
||||
component(D.self),
|
||||
component(E.self))
|
||||
}
|
||||
|
||||
public func components<R, A: Component, B: Component, C: Component, D: Component, E: Component, F: Component>(_ closure: (A, B, C, D, E, F) -> R) -> R {
|
||||
return closure(component(A.self),
|
||||
component(B.self),
|
||||
component(C.self),
|
||||
component(D.self),
|
||||
component(E.self),
|
||||
component(F.self) )
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,118 +0,0 @@
|
|||
//
|
||||
// EntityHub.swift
|
||||
// FirebladeECS
|
||||
//
|
||||
// Created by Christian Treffs on 09.10.17.
|
||||
//
|
||||
|
||||
public class EntityHub: EventHandler {
|
||||
public weak var delegate: EventHub?
|
||||
|
||||
public lazy var eventHub: DefaultEventHub = { return DefaultEventHub() }()
|
||||
|
||||
private(set) var entities: EntityStorage
|
||||
private(set) var families: FamilyStorage
|
||||
|
||||
public init() {
|
||||
entities = DefaultEntityStorage()
|
||||
|
||||
families = DefaultFamilyStorage()
|
||||
|
||||
self.delegate = eventHub
|
||||
|
||||
subscribe(event: handleEntityCreated)
|
||||
subscribe(event: handleEntityDestroyed)
|
||||
|
||||
subscribe(event: handleComponentAdded)
|
||||
subscribe(event: handleComponentUpdated)
|
||||
subscribe(event: handleComponentRemoved)
|
||||
|
||||
subscribe(event: handleFamilyCreated)
|
||||
subscribe(event: handleFamilyMemberAdded)
|
||||
subscribe(event: handleFamilyMemberUpdated)
|
||||
subscribe(event: handleFamilyMemberRemoved)
|
||||
subscribe(event: handleFamilyDestroyed)
|
||||
}
|
||||
|
||||
deinit {
|
||||
unsubscribe(event: handleEntityCreated)
|
||||
unsubscribe(event: handleEntityDestroyed)
|
||||
|
||||
unsubscribe(event: handleComponentAdded)
|
||||
unsubscribe(event: handleComponentUpdated)
|
||||
unsubscribe(event: handleComponentRemoved)
|
||||
|
||||
unsubscribe(event: handleFamilyCreated)
|
||||
unsubscribe(event: handleFamilyMemberUpdated)
|
||||
unsubscribe(event: handleFamilyMemberAdded)
|
||||
unsubscribe(event: handleFamilyMemberRemoved)
|
||||
unsubscribe(event: handleFamilyDestroyed)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - creator entity
|
||||
extension EntityHub {
|
||||
|
||||
public func createEntity() -> Entity {
|
||||
let newEntity = Entity(uei: UEI.next, dispatcher: eventHub)
|
||||
// ^ dispatches entity creation event here ^
|
||||
let success: Bool = entities.add(newEntity)
|
||||
assert(success == true, "Entity with the exact identifier already exists")
|
||||
|
||||
return newEntity
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - create/get family
|
||||
extension EntityHub {
|
||||
|
||||
@discardableResult
|
||||
public func family(with traits: FamilyTraits) -> (new: Bool, family: Family) {
|
||||
|
||||
if let existingFamily: Family = families[traits] {
|
||||
return (new: false, family: existingFamily)
|
||||
}
|
||||
|
||||
let newFamily = Family(traits: traits, eventHub: eventHub)
|
||||
// ^ dispatches family creation event here ^
|
||||
let success = families.add(newFamily)
|
||||
assert(success, "Family with the exact traits already exists")
|
||||
|
||||
refreshFamilyCache()
|
||||
|
||||
return (new: true, family: newFamily)
|
||||
}
|
||||
|
||||
fileprivate func onFamilyCreated(_ newFamily: Family) {
|
||||
|
||||
newFamily.update(membership: entities.iterator)
|
||||
}
|
||||
|
||||
fileprivate func refreshFamilyCache() {
|
||||
// TODO:
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - event handler
|
||||
extension EntityHub {
|
||||
func handleEntityCreated(_ e: EntityCreated) { print(e) }
|
||||
func handleEntityDestroyed(_ e: EntityDestroyed) { print(e) }
|
||||
|
||||
func handleComponentAdded(_ e: ComponentAdded) { print(e) }
|
||||
func handleComponentUpdated(_ e: ComponentUpdated) { print(e) }
|
||||
func handleComponentRemoved(_ e: ComponentRemoved) { print(e) }
|
||||
|
||||
func handleFamilyCreated(_ e: FamilyCreated) {
|
||||
print(e)
|
||||
let newFamily: Family = e.family
|
||||
onFamilyCreated(newFamily)
|
||||
|
||||
}
|
||||
func handleFamilyMemberAdded(_ e: FamilyMemberAdded) { print(e) }
|
||||
func handleFamilyMemberUpdated(_ e: FamilyMemberUpdated) { print(e) }
|
||||
func handleFamilyMemberRemoved(_ e: FamilyMemberRemoved) { print(e) }
|
||||
func handleFamilyDestroyed(_ e: FamilyDestroyed) { print(e) }
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
//
|
||||
// EntityHub2.swift
|
||||
// FirebladeECS
|
||||
//
|
||||
// Created by Christian Treffs on 11.10.17.
|
||||
//
|
||||
|
||||
class EntityHub2 {
|
||||
/*
|
||||
fileprivate typealias CompType = ComponentIdentifier
|
||||
fileprivate typealias CompIdxInCompArrayForType = Int
|
||||
fileprivate typealias ComponentArray = ContiguousArray<Component>
|
||||
fileprivate typealias EntityID = Int // aka EntityIdentifier
|
||||
|
||||
fileprivate var componentsByType: [CompType: ComponentArray] = [:]
|
||||
fileprivate var entitiesById: [EntityID: Entity] = [:]
|
||||
fileprivate var componentLookupByEntityId: [EntityID: [CompType:CompIdxInCompArrayForType]] = [:]
|
||||
fileprivate var entityIdLookupByCompType: [CompType: [CompIdxInCompArrayForType:EntityID]] = [:]
|
||||
|
||||
|
||||
fileprivate func query(_ compTypes: Component.Type...) -> [Entity] {
|
||||
let types = compTypes.map{ $0.identifier }
|
||||
fatalError()
|
||||
}
|
||||
|
||||
fileprivate func queryTypes(_ types: [CompType]) -> [Entity] {
|
||||
var types = types
|
||||
|
||||
var minSize: Int = Int.max
|
||||
var minIndex: Int = 0
|
||||
var index: Int = 0
|
||||
|
||||
for t in types {
|
||||
guard let compArray = componentsByType[t] else {
|
||||
fatalError("querying for non existing type \(t.type)")
|
||||
}
|
||||
|
||||
let size: Int = compArray.count
|
||||
if size < minSize {
|
||||
minSize = size
|
||||
minIndex = index
|
||||
}
|
||||
index += 1
|
||||
}
|
||||
|
||||
let minType: CompType = types[minIndex]
|
||||
|
||||
if types.count >= 2 && minIndex != (types.count - 1) {
|
||||
types.swapAt(minIndex, (types.count-1))
|
||||
}
|
||||
types.removeLast()
|
||||
|
||||
return iterate(minType, types)
|
||||
}
|
||||
|
||||
fileprivate func iterate(_ minType: CompType, _ types: [CompType]) -> [Entity] {
|
||||
var entitiesWithTypes: [Entity] = []
|
||||
|
||||
guard let compArray = componentsByType[minType] else {
|
||||
fatalError("iterating non existing type \(minType.type)")
|
||||
}
|
||||
|
||||
for i: CompIdxInCompArrayForType in 0..<compArray.count {
|
||||
|
||||
let component = compArray[i]
|
||||
let compType = component.identifier
|
||||
|
||||
guard let ownerId: EntityID = entityIdLookupByCompType[compType]?[i]else {
|
||||
fatalError("could not find owner id")
|
||||
}
|
||||
|
||||
if has(allTypes: ownerId, types) {
|
||||
guard let entity = entitiesById[ownerId] else {
|
||||
fatalError("could not find entity")
|
||||
}
|
||||
entitiesWithTypes.append(entity)
|
||||
}
|
||||
}
|
||||
|
||||
return entitiesWithTypes
|
||||
|
||||
}
|
||||
|
||||
fileprivate func has(allTypes entityId: EntityID, _ types: [CompType]) -> Bool {
|
||||
guard let entityTypes = componentLookupByEntityId[entityId]?.keys else { return false }
|
||||
|
||||
for requiredType: CompType in types {
|
||||
if !entityTypes.contains(requiredType) { return false }
|
||||
}
|
||||
return true
|
||||
|
||||
|
||||
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// EntityIdentifier.swift
|
||||
// FirebladeECS
|
||||
//
|
||||
// Created by Christian Treffs on 08.10.17.
|
||||
//
|
||||
|
||||
// MARK: Unique Entity Index
|
||||
|
||||
public typealias EntityIdentifier = UInt64 // provides 18446744073709551615 unique identifiers
|
||||
public typealias EntityIndex = Int
|
||||
|
||||
public extension EntityIdentifier {
|
||||
public var index: EntityIndex { return EntityIndex(self & 0xffffffff) } // shifts entity identifier by UInt32.max
|
||||
}
|
||||
|
||||
public extension EntityIndex {
|
||||
public var identifier: EntityIdentifier { return EntityIdentifier(self & -0xffffffff) } // shifts entity identifier by -UInt32.max
|
||||
}
|
||||
|
||||
|
||||
// MARK: Unique Entity Identifiable
|
||||
public protocol UniqueEntityIdentifiable: Hashable {
|
||||
var identifier: EntityIdentifier { get }
|
||||
}
|
||||
|
||||
public extension UniqueEntityIdentifiable {
|
||||
public var hashValue: Int { return identifier.hashValue }
|
||||
}
|
||||
|
||||
|
|
@ -6,59 +6,67 @@
|
|||
//
|
||||
|
||||
public protocol EntityStorage {
|
||||
|
||||
func create(in nexus: Nexus) -> Entity
|
||||
|
||||
@discardableResult func add(_ entity: Entity) -> Bool
|
||||
|
||||
var iterator: AnyIterator<Entity> { get }
|
||||
|
||||
func has(_ entity: Entity) -> Bool
|
||||
func has(_ id: UEI) -> Bool
|
||||
func has(_ id: EntityIdentifier) -> Bool
|
||||
func has(_ named: String) -> Bool
|
||||
|
||||
func get(_ id: UEI) -> Entity?
|
||||
subscript(_ id: UEI) -> Entity? { get }
|
||||
func get(_ id: EntityIdentifier) -> Entity?
|
||||
subscript(_ id: EntityIdentifier) -> Entity? { get }
|
||||
|
||||
func get(_ named: String) -> Entity?
|
||||
subscript(_ named: String) -> Entity? { get }
|
||||
|
||||
@discardableResult func remove(_ id: UEI) -> Bool
|
||||
@discardableResult func remove(_ id: EntityIdentifier) -> Bool
|
||||
|
||||
func clear()
|
||||
|
||||
}
|
||||
|
||||
class DefaultEntityStorage: EntityStorage {
|
||||
class GeneralEntityStorage: EntityStorage {
|
||||
|
||||
fileprivate typealias Index = Set<Entity>.Index
|
||||
fileprivate var entities: Set<Entity> = Set<Entity>()
|
||||
fileprivate typealias Index = ContiguousArray<Entity>.Index
|
||||
fileprivate var entities: ContiguousArray<Entity> = ContiguousArray<Entity>()
|
||||
|
||||
var iterator: AnyIterator<Entity> {
|
||||
return AnyIterator(entities.makeIterator())
|
||||
}
|
||||
|
||||
func create(in nexus: Nexus) -> Entity {
|
||||
let nextIndex = EntityIdentifier(entities.count) // TODO: should be next free index -> reuse
|
||||
return Entity(nexus: nexus, id: nextIndex)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func add(_ entity: Entity) -> Bool {
|
||||
let (success, _) = entities.insert(entity)
|
||||
return success
|
||||
assert(!entities.contains(entity), "entity already present")
|
||||
entities.append(entity)
|
||||
return true
|
||||
}
|
||||
|
||||
func has(_ entity: Entity) -> Bool {
|
||||
return entities.contains(entity)
|
||||
}
|
||||
|
||||
func has(_ id: UEI) -> Bool {
|
||||
return entities.contains { $0.uei == id }
|
||||
func has(_ id: EntityIdentifier) -> Bool {
|
||||
return entities.contains { $0.identifier == id }
|
||||
}
|
||||
|
||||
func has(_ named: String) -> Bool {
|
||||
return entities.contains { $0.name == named }
|
||||
}
|
||||
|
||||
func get(_ id: UEI) -> Entity? {
|
||||
func get(_ id: EntityIdentifier) -> Entity? {
|
||||
guard let index = index(id) else { return nil }
|
||||
return entities[index]
|
||||
}
|
||||
|
||||
subscript(id: UEI) -> Entity? { return get(id) }
|
||||
subscript(id: EntityIdentifier) -> Entity? { return get(id) }
|
||||
|
||||
func get(_ named: String) -> Entity? {
|
||||
guard let index: Index = index(named) else { return nil }
|
||||
|
|
@ -68,7 +76,7 @@ class DefaultEntityStorage: EntityStorage {
|
|||
subscript(named: String) -> Entity? { return get(named) }
|
||||
|
||||
@discardableResult
|
||||
func remove(_ id: UEI) -> Bool {
|
||||
func remove(_ id: EntityIdentifier) -> Bool {
|
||||
guard let index: Index = index(id) else { return false }
|
||||
entities.remove(at: index)
|
||||
return true
|
||||
|
|
@ -80,8 +88,9 @@ class DefaultEntityStorage: EntityStorage {
|
|||
|
||||
// MARK: - internal
|
||||
|
||||
fileprivate func index(_ id: UEI) -> Index? {
|
||||
return entities.index { $0.uei == id }
|
||||
fileprivate func index(_ id: EntityIdentifier) -> Index? {
|
||||
return Index(id)
|
||||
//return entities.index { $0.uei == id }
|
||||
}
|
||||
|
||||
fileprivate func index(_ named: String) -> Index? {
|
||||
|
|
|
|||
|
|
@ -6,49 +6,41 @@
|
|||
//
|
||||
|
||||
public struct EntityCreated: Event {
|
||||
unowned let entity: Entity
|
||||
let entityId: EntityIdentifier
|
||||
}
|
||||
|
||||
public struct EntityDestroyed: Event {
|
||||
unowned let entity: Entity
|
||||
let entityId: EntityIdentifier
|
||||
}
|
||||
|
||||
public struct ComponentAdded: Event {
|
||||
//let component: Component
|
||||
unowned let to: Entity
|
||||
let component: ComponentIdentifier
|
||||
let to: EntityIdentifier
|
||||
}
|
||||
|
||||
public struct ComponentUpdated: Event {
|
||||
//let component: Component
|
||||
//let previous: Component
|
||||
unowned let at: Entity
|
||||
let at: EntityIdentifier
|
||||
}
|
||||
|
||||
public struct ComponentRemoved: Event {
|
||||
//let component: Component
|
||||
unowned let from: Entity
|
||||
let component: ComponentIdentifier
|
||||
let from: EntityIdentifier
|
||||
}
|
||||
|
||||
struct FamilyMemberAdded: Event {
|
||||
unowned let member: Entity
|
||||
unowned let to: Family
|
||||
}
|
||||
|
||||
struct FamilyMemberUpdated: Event {
|
||||
unowned let newMember: Entity
|
||||
unowned let oldMember: Entity
|
||||
unowned let `in`: Family
|
||||
let member: EntityIdentifier
|
||||
let to: FamilyTraits
|
||||
}
|
||||
|
||||
struct FamilyMemberRemoved: Event {
|
||||
unowned let member: Entity
|
||||
unowned let from: Family
|
||||
let member: EntityIdentifier
|
||||
let from: FamilyTraits
|
||||
}
|
||||
|
||||
struct FamilyCreated: Event {
|
||||
unowned let family: Family
|
||||
let family: FamilyTraits
|
||||
}
|
||||
|
||||
struct FamilyDestroyed: Event {
|
||||
unowned let family: Family
|
||||
let family: FamilyTraits
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,30 +8,28 @@
|
|||
// MARK: - family
|
||||
public final class Family {
|
||||
|
||||
public var delegate: EventHub?
|
||||
fileprivate var dispatcher: EventDispatcher
|
||||
public var nexus: Nexus
|
||||
|
||||
// members of this Family must conform to these traits
|
||||
public let traits: FamilyTraits
|
||||
|
||||
public private(set) var members: Set<Entity>
|
||||
public private(set) var members: ContiguousArray<EntityIdentifier>
|
||||
|
||||
public init(traits: FamilyTraits, eventHub: EventHub & EventDispatcher) {
|
||||
public init(_ nexus: Nexus, traits: FamilyTraits) {
|
||||
|
||||
members = Set<Entity>()
|
||||
members = ContiguousArray<EntityIdentifier>()
|
||||
members.reserveCapacity(64)
|
||||
|
||||
self.traits = traits
|
||||
|
||||
delegate = eventHub
|
||||
dispatcher = eventHub
|
||||
self.nexus = nexus
|
||||
|
||||
subscribe(event: handleComponentAddedToEntity)
|
||||
/*subscribe(event: handleComponentAddedToEntity)
|
||||
subscribe(event: handleComponentUpdatedAtEntity)
|
||||
subscribe(event: handleComponentRemovedFromEntity)
|
||||
|
||||
*/
|
||||
defer {
|
||||
notifyCreated()
|
||||
//notifyCreated()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -40,12 +38,12 @@ public final class Family {
|
|||
|
||||
members.removeAll()
|
||||
|
||||
unsubscribe(event: handleComponentAddedToEntity)
|
||||
/*unsubscribe(event: handleComponentAddedToEntity)
|
||||
unsubscribe(event: handleComponentUpdatedAtEntity)
|
||||
unsubscribe(event: handleComponentRemovedFromEntity)
|
||||
unsubscribe(event: handleComponentRemovedFromEntity)*/
|
||||
|
||||
defer {
|
||||
notifyDestroyed()
|
||||
// notifyDestroyed()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -54,73 +52,87 @@ public final class Family {
|
|||
// MARK: - update family membership
|
||||
extension Family {
|
||||
|
||||
func update(membership entityIds: AnyIterator<EntityIdentifier>) {
|
||||
while let entityId: EntityIdentifier = entityIds.next() {
|
||||
update(membership: entityId)
|
||||
}
|
||||
}
|
||||
|
||||
func update(membership entities: AnyIterator<Entity>) {
|
||||
while let entity: Entity = entities.next() {
|
||||
update(membership: entity)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func update(membership entityId: EntityIdentifier) {
|
||||
guard let entity = nexus.get(entity: entityId) else {
|
||||
fatalError("no entity with id \(entityId) in \(nexus)")
|
||||
}
|
||||
update(membership: entity)
|
||||
}
|
||||
|
||||
fileprivate func update(membership entity: Entity) {
|
||||
let isMatch: Bool = traits.isMatch(entity)
|
||||
switch isMatch {
|
||||
case true:
|
||||
push(entity)
|
||||
push(entity.identifier)
|
||||
case false:
|
||||
remove(entity)
|
||||
remove(entity.identifier)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func push(_ entity: Entity) {
|
||||
let (added, member) = members.insert(entity)
|
||||
switch added {
|
||||
case true:
|
||||
notify(added: member)
|
||||
case false:
|
||||
notify(update: entity, previous: member)
|
||||
}
|
||||
fileprivate func push(_ entityId: EntityIdentifier) {
|
||||
assert(!members.contains(entityId), "entity with id \(entityId) already in family")
|
||||
members.append(entityId)
|
||||
// TODO: notify(added: entityId)
|
||||
}
|
||||
|
||||
fileprivate func remove(_ entity: Entity) {
|
||||
let removed: Entity? = members.remove(entity)
|
||||
fileprivate func remove(_ entityId: EntityIdentifier) {
|
||||
guard let index: Int = members.index(of: entityId) else {
|
||||
fatalError("removing entity id \(entityId) that is not in family")
|
||||
}
|
||||
let removed: EntityIdentifier? = members.remove(at: index)
|
||||
assert(removed != nil)
|
||||
if let removedEntity: Entity = removed {
|
||||
notify(removed: removedEntity)
|
||||
if let removedEntity: EntityIdentifier = removed {
|
||||
// TODO: notify(removed: removedEntity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - entity accessors
|
||||
// MARK: - member iterator
|
||||
extension Family {
|
||||
public func entities(_ apply: (Entity) -> Void ) {
|
||||
members.lazy.forEach(apply)
|
||||
func makeIterator<A>() -> AnyIterator<(Entity, A)> where A: Component {
|
||||
var members = self.members.makeIterator()
|
||||
return AnyIterator<(Entity, A)> { [unowned self] in
|
||||
let entityId: EntityIdentifier = members.next()!
|
||||
let entity: Entity = self.nexus.get(entity: entityId)!
|
||||
let a: A = entity.get()!
|
||||
return (entity, a)
|
||||
}
|
||||
}
|
||||
|
||||
public func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Entity) throws -> Result) rethrows -> Result {
|
||||
return try members.lazy.reduce(initialResult, nextPartialResult)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - component accessors
|
||||
extension Family {
|
||||
public func component<A: Component>(_ apply: (A) -> Void) {
|
||||
members.lazy.forEach { $0.component(apply) }
|
||||
}
|
||||
public func components<A: Component, B: Component>(_ apply: (A, B) -> Void) {
|
||||
members.lazy.forEach { $0.components(apply) }
|
||||
}
|
||||
public func components<A: Component, B: Component, C: Component>(_ apply: (A, B, C) -> Void) {
|
||||
members.lazy.forEach { $0.components(apply) }
|
||||
}
|
||||
public func components<A: Component, B: Component, C: Component, D: Component>(_ apply: (A, B, C, D) -> Void) {
|
||||
members.lazy.forEach { $0.components(apply) }
|
||||
}
|
||||
public func components<A: Component, B: Component, C: Component, D: Component, E: Component>(_ apply: (A, B, C, D, E) -> Void) {
|
||||
members.lazy.forEach { $0.components(apply) }
|
||||
}
|
||||
public func components<A: Component, B: Component, C: Component, D: Component, E: Component, F: Component>(_ apply: (A, B, C, D, E, F) -> Void) {
|
||||
members.lazy.forEach { $0.components(apply) }
|
||||
func makeIterator<A, B>() -> AnyIterator<(Entity, A, B)> where A: Component, B: Component {
|
||||
var members = self.members.makeIterator()
|
||||
return AnyIterator<(Entity, A, B)> { [unowned self] in
|
||||
let entityId: EntityIdentifier = members.next()!
|
||||
let entity: Entity = self.nexus.get(entity: entityId)!
|
||||
let a: A = entity.get()!
|
||||
let b: B = entity.get()!
|
||||
return (entity, a, b)
|
||||
}
|
||||
}
|
||||
|
||||
func makeIterator<A, B, C>() -> AnyIterator<(Entity, A, B, C)> where A: Component, B: Component, C: Component {
|
||||
var members = self.members.makeIterator()
|
||||
return AnyIterator<(Entity, A, B, C)> { [unowned self] in
|
||||
let entityId: EntityIdentifier = members.next()!
|
||||
let entity: Entity = self.nexus.get(entity: entityId)!
|
||||
let a: A = entity.get()!
|
||||
let b: B = entity.get()!
|
||||
let c: C = entity.get()!
|
||||
return (entity, a, b, c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Equatable
|
||||
|
|
@ -137,46 +149,27 @@ extension Family: Hashable {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// MARK: - event dispatcher
|
||||
extension Family: EventDispatcher {
|
||||
public func dispatch<E>(_ event: E) where E: Event {
|
||||
dispatcher.dispatch(event)
|
||||
}
|
||||
|
||||
fileprivate func unowned(closure: @escaping (Family) -> Void) {
|
||||
let unownedClosure = { [unowned self] in
|
||||
closure(self)
|
||||
}
|
||||
unownedClosure()
|
||||
}
|
||||
|
||||
fileprivate func notifyCreated() {
|
||||
unowned {
|
||||
$0.dispatch(FamilyCreated(family: $0))
|
||||
}
|
||||
dispatch(FamilyCreated(family: traits))
|
||||
}
|
||||
|
||||
fileprivate func notify(added newEntity: Entity) {
|
||||
unowned { [unowned newEntity] in
|
||||
$0.dispatch(FamilyMemberAdded(member: newEntity, to: $0))
|
||||
}
|
||||
fileprivate func notify(added newEntity: EntityIdentifier) {
|
||||
dispatch(FamilyMemberAdded(member: newEntity, to: traits))
|
||||
}
|
||||
|
||||
fileprivate func notify(update newEntity: Entity, previous oldEntity: Entity) {
|
||||
unowned { [unowned newEntity, unowned oldEntity] in
|
||||
$0.dispatch(FamilyMemberUpdated(newMember: newEntity, oldMember: oldEntity, in: $0) )
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func notify(removed removedEntity: Entity) {
|
||||
unowned { [unowned removedEntity] in
|
||||
$0.dispatch(FamilyMemberRemoved(member: removedEntity, from: $0))
|
||||
}
|
||||
fileprivate func notify(removed removedEntity: EntityIdentifier) {
|
||||
dispatch(FamilyMemberRemoved(member: removedEntity, from: traits))
|
||||
}
|
||||
|
||||
fileprivate func notifyDestroyed() {
|
||||
//dispatch(event: FamilyDestroyed())
|
||||
// dispatch(FamilyDestroyed(family: self))
|
||||
dispatch(FamilyDestroyed(family: traits))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -184,18 +177,16 @@ extension Family: EventDispatcher {
|
|||
extension Family: EventHandler {
|
||||
|
||||
fileprivate final func handleComponentAddedToEntity(event: ComponentAdded) {
|
||||
unowned let entity: Entity = event.to
|
||||
update(membership: entity)
|
||||
update(membership: event.to)
|
||||
}
|
||||
|
||||
fileprivate final func handleComponentUpdatedAtEntity(event: ComponentUpdated) {
|
||||
unowned let entity: Entity = event.at
|
||||
update(membership: entity)
|
||||
update(membership: event.at)
|
||||
}
|
||||
|
||||
fileprivate final func handleComponentRemovedFromEntity(event: ComponentRemoved) {
|
||||
unowned let entity: Entity = event.from
|
||||
update(membership: entity)
|
||||
update(membership: event.from)
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ public protocol FamilyStorage {
|
|||
func clear()
|
||||
}
|
||||
|
||||
class DefaultFamilyStorage: FamilyStorage {
|
||||
class GeneralFamilyStorage: FamilyStorage {
|
||||
|
||||
fileprivate typealias Index = Dictionary<FamilyTraits, Family>.Index
|
||||
fileprivate var families: [FamilyTraits: Family] = [:]
|
||||
|
|
|
|||
|
|
@ -7,20 +7,20 @@
|
|||
|
||||
// trait/predicate/characteristic
|
||||
public struct FamilyTraits {
|
||||
let hasAll: Set<UCT>
|
||||
let hasAny: Set<UCT>
|
||||
let hasNone: Set<UCT>
|
||||
let hasAll: Set<ComponentIdentifier>
|
||||
let hasAny: Set<ComponentIdentifier>
|
||||
let hasNone: Set<ComponentIdentifier>
|
||||
|
||||
public init(hasAll: Set<UCT>, hasAny: Set<UCT>, hasNone: Set<UCT>) {
|
||||
public init(hasAll: Set<ComponentIdentifier>, hasAny: Set<ComponentIdentifier>, hasNone: Set<ComponentIdentifier>) {
|
||||
self.hasAll = hasAll
|
||||
self.hasAny = hasAny
|
||||
self.hasNone = hasNone
|
||||
assert(isValid)
|
||||
}
|
||||
|
||||
fileprivate var iteratorAll: SetIterator<UCT> { return hasAll.makeIterator() }
|
||||
fileprivate var iteratorAny: SetIterator<UCT> { return hasAny.makeIterator() }
|
||||
fileprivate var iteratorNone: SetIterator<UCT> { return hasNone.makeIterator() }
|
||||
fileprivate var iteratorAll: SetIterator<ComponentIdentifier> { return hasAll.makeIterator() }
|
||||
fileprivate var iteratorAny: SetIterator<ComponentIdentifier> { return hasAny.makeIterator() }
|
||||
fileprivate var iteratorNone: SetIterator<ComponentIdentifier> { return hasNone.makeIterator() }
|
||||
}
|
||||
extension FamilyTraits {
|
||||
var isValid: Bool {
|
||||
|
|
@ -35,7 +35,7 @@ extension FamilyTraits {
|
|||
|
||||
fileprivate func matches(all entity: Entity) -> Bool {
|
||||
var all = iteratorAll
|
||||
while let uct: UCT = all.next() {
|
||||
while let uct: ComponentIdentifier = all.next() {
|
||||
guard entity.has(uct) else { return false }
|
||||
}
|
||||
return true
|
||||
|
|
@ -43,7 +43,7 @@ extension FamilyTraits {
|
|||
|
||||
fileprivate func matches(none entity: Entity) -> Bool {
|
||||
var none = iteratorNone
|
||||
while let uct: UCT = none.next() {
|
||||
while let uct: ComponentIdentifier = none.next() {
|
||||
guard !entity.has(uct) else { return false }
|
||||
}
|
||||
return true
|
||||
|
|
@ -52,7 +52,7 @@ extension FamilyTraits {
|
|||
fileprivate func matches(any entity: Entity) -> Bool {
|
||||
guard !hasAny.isEmpty else { return true }
|
||||
var any = iteratorAny
|
||||
while let uct: UCT = any.next() {
|
||||
while let uct: ComponentIdentifier = any.next() {
|
||||
if entity.has(uct) {
|
||||
return true
|
||||
}
|
||||
|
|
@ -92,9 +92,9 @@ extension FamilyTraits: Hashable {
|
|||
extension FamilyTraits: CustomStringConvertible {
|
||||
|
||||
public var description: String {
|
||||
let all: String = hasAll.map { "\($0.type)" }.joined(separator: " AND ")
|
||||
let any: String = hasAny.map { "\($0.type)" }.joined(separator: " OR ")
|
||||
let none: String = hasNone.map { "!\($0.type)"}.joined(separator: " NOT ")
|
||||
let all: String = hasAll.map { "\($0.self)" }.joined(separator: " AND ")
|
||||
let any: String = hasAny.map { "\($0.self)" }.joined(separator: " OR ")
|
||||
let none: String = hasNone.map { "!\($0.self)"}.joined(separator: " NOT ")
|
||||
let out: String = ["\(all)", "\(any)", "\(none)"].joined(separator: " AND ")
|
||||
//TODO: nicer
|
||||
return "FamilyTraits(\(out))"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,107 @@
|
|||
//
|
||||
// Nexus+Component.swift
|
||||
// FirebladeECS
|
||||
//
|
||||
// Created by Christian Treffs on 13.10.17.
|
||||
//
|
||||
|
||||
extension Nexus {
|
||||
|
||||
public func has(component: ComponentIdentifier, entity: EntityIdentifier) -> Bool {
|
||||
let hash: EntityComponentHash = component.hashValue(using: entity.index)
|
||||
return has(hash)
|
||||
}
|
||||
|
||||
fileprivate func has(_ hash: EntityComponentHash) -> Bool {
|
||||
return componentIndexByEntityComponentHash[hash] != nil
|
||||
}
|
||||
|
||||
public func count(components entityId: EntityIdentifier) -> Int {
|
||||
switch componentIdsByEntityIdx[entityId.index] {
|
||||
case .some(let componentIds):
|
||||
return componentIds.count
|
||||
case .none:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
public func add<C>(component: C, to entityId: EntityIdentifier) where C: Component {
|
||||
let componentId = C.identifier
|
||||
let entityIdx = entityId.index
|
||||
let hash: EntityComponentHash = componentId.hashValue(using: entityIdx)
|
||||
assert(!has(hash), "ComponentAdd collision: \(entityId) already has a component \(component)")
|
||||
var newComponentIndex: ComponentIndex = ComponentIndex.invalid
|
||||
if componentsByType[componentId] != nil {
|
||||
newComponentIndex = componentsByType[componentId]!.count // TODO: get next free index
|
||||
componentsByType[componentId]!.insert(component, at: newComponentIndex)
|
||||
} else {
|
||||
newComponentIndex = 0
|
||||
componentsByType[componentId] = UniformComponents(arrayLiteral: component)
|
||||
}
|
||||
|
||||
if componentIdsByEntityIdx[entityIdx] != nil {
|
||||
componentIdsByEntityIdx[entityIdx]!.append(componentId)
|
||||
} else {
|
||||
componentIdsByEntityIdx[entityIdx] = ComponentIdentifiers()
|
||||
componentIdsByEntityIdx[entityIdx]!.append(componentId)
|
||||
}
|
||||
|
||||
|
||||
// assign entity / component to index
|
||||
componentIndexByEntityComponentHash[hash] = newComponentIndex
|
||||
|
||||
notify(ComponentAdded(component: componentId, to: entityId))
|
||||
}
|
||||
|
||||
|
||||
public func get<C>(component componentId: ComponentIdentifier, for entityId: EntityIdentifier) -> C? where C: Component {
|
||||
let hash: EntityComponentHash = componentId.hashValue(using: entityId.index)
|
||||
return get(hash)
|
||||
}
|
||||
|
||||
fileprivate func get<C>(_ hash: EntityComponentHash) -> C? where C: Component {
|
||||
let componentId: ComponentIdentifier = C.identifier
|
||||
guard let componentIdx: ComponentIndex = componentIndexByEntityComponentHash[hash] else { return nil }
|
||||
guard let uniformComponents: UniformComponents = componentsByType[componentId] else { return nil }
|
||||
guard let typedComponent: C = uniformComponents[componentIdx] as? C else { return nil }
|
||||
return typedComponent
|
||||
}
|
||||
|
||||
public func get(components entityId: EntityIdentifier) -> ComponentIdentifiers? {
|
||||
return componentIdsByEntityIdx[entityId.index]
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func remove(component componentId: ComponentIdentifier, from entityId: EntityIdentifier) -> Bool {
|
||||
let hash: EntityComponentHash = componentId.hashValue(using: entityId.index)
|
||||
|
||||
guard let componentIdx: ComponentIndex = componentIndexByEntityComponentHash.removeValue(forKey: hash) else {
|
||||
assert(false, "ComponentRemove failure: entity \(entityId) has no component \(componentId)")
|
||||
return false
|
||||
}
|
||||
|
||||
guard componentsByType[componentId]?.remove(at: componentIdx) != nil else {
|
||||
assert(false, "ComponentRemove failure: no component instance for \(componentId) with the given index \(componentIdx)")
|
||||
return false
|
||||
}
|
||||
|
||||
// FIXME: this is expensive
|
||||
if let removeIndex: Int = get(components: entityId)?.index(where: { $0 == componentId }) {
|
||||
componentIdsByEntityIdx[entityId.index]?.remove(at: removeIndex)
|
||||
}
|
||||
|
||||
|
||||
notify(ComponentRemoved(component: componentId, from: entityId))
|
||||
return true
|
||||
}
|
||||
|
||||
public func clear(componentes entityId: EntityIdentifier) {
|
||||
|
||||
guard let componentIds = get(components: entityId) else { return }
|
||||
|
||||
componentIds.forEach { (componentId: ComponentIdentifier) in
|
||||
remove(component: componentId, from: entityId)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// Nexus+Entity.swift
|
||||
// FirebladeECS
|
||||
//
|
||||
// Created by Christian Treffs on 13.10.17.
|
||||
//
|
||||
|
||||
extension Nexus {
|
||||
|
||||
public func create(entity name: String? = nil) -> Entity {
|
||||
let newEntityIndex: EntityIndex = entities.count // TODO: use free entity index
|
||||
let newEntityIdentifier: EntityIdentifier = newEntityIndex.identifier
|
||||
let newEntity = Entity(nexus: self, id: newEntityIdentifier, name: name)
|
||||
entities.insert(newEntity, at: newEntityIndex)
|
||||
notify(EntityCreated(entityId: newEntityIdentifier))
|
||||
return newEntity
|
||||
}
|
||||
|
||||
public func has(entity entityId: EntityIdentifier) -> Bool {
|
||||
return entities.count > entityId.index // TODO: reuse free index
|
||||
|
||||
}
|
||||
|
||||
public func get(entity entityId: EntityIdentifier) -> Entity? {
|
||||
guard has(entity: entityId) else { return nil }
|
||||
return entities[entityId.index]
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func destroy(entity entityId: EntityIdentifier) -> Bool {
|
||||
guard has(entity: entityId) else {
|
||||
assert(false, "EntityRemove failure: no entity \(entityId) to remove")
|
||||
return false
|
||||
}
|
||||
|
||||
clear(componentes: entityId)
|
||||
|
||||
// FIXME: this is wrong since it removes the entity that should be reused
|
||||
entities.remove(at: entityId.index)
|
||||
|
||||
notify(EntityDestroyed(entityId: entityId))
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
//
|
||||
// Nexus+Family.swift
|
||||
// FirebladeECS
|
||||
//
|
||||
// Created by Christian Treffs on 13.10.17.
|
||||
//
|
||||
|
||||
extension Nexus {
|
||||
|
||||
/*@discardableResult
|
||||
public func family(with traits: FamilyTraits) -> (new: Bool, family: Family) {
|
||||
|
||||
if let existingFamily: Family = families.get(traits) {
|
||||
return (new: false, family: existingFamily)
|
||||
}
|
||||
|
||||
let newFamily = Family(self, traits: traits)
|
||||
// ^ dispatches family creation event here ^
|
||||
let success = families.add(newFamily)
|
||||
assert(success, "Family with the exact traits already exists")
|
||||
|
||||
refreshFamilyCache()
|
||||
|
||||
return (new: true, family: newFamily)
|
||||
}
|
||||
|
||||
func onFamilyCreated(_ newFamily: Family) {
|
||||
newFamily.update(membership: entities.iterator)
|
||||
}
|
||||
|
||||
func refreshFamilyCache() {
|
||||
// TODO:
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
//
|
||||
// Nexus.swift
|
||||
// FirebladeECS
|
||||
//
|
||||
// Created by Christian Treffs on 09.10.17.
|
||||
//
|
||||
public typealias EntityComponentHash = Int
|
||||
public typealias ComponentIndex = Int
|
||||
public extension ComponentIndex {
|
||||
static let invalid: ComponentIndex = Int.min
|
||||
}
|
||||
|
||||
public typealias UniformComponents = ContiguousArray<Component>
|
||||
public typealias ComponentIdentifiers = ContiguousArray<ComponentIdentifier>
|
||||
public typealias Entities = ContiguousArray<Entity>
|
||||
|
||||
public class Nexus {
|
||||
|
||||
/// - Index: index value matching entity identifier shifted to Int
|
||||
/// - Value: each element is a entity instance
|
||||
var entities: Entities
|
||||
|
||||
/// - Key: component type identifier
|
||||
/// - Value: each element is a component instance of the same type (uniform). New component instances are appended.
|
||||
var componentsByType: [ComponentIdentifier: UniformComponents]
|
||||
|
||||
/// - Key: 'entity id' - 'component type' hash that uniquely links both
|
||||
/// - Value: each element is an index pointing to the component instance index in the componentsByType map.
|
||||
var componentIndexByEntityComponentHash: [EntityComponentHash: ComponentIndex]
|
||||
|
||||
var componentIdsByEntityIdx: [EntityIndex: ComponentIdentifiers]
|
||||
|
||||
public init() {
|
||||
entities = Entities()
|
||||
componentsByType = [:]
|
||||
componentIndexByEntityComponentHash = [:]
|
||||
componentIdsByEntityIdx = [:]
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
extension Nexus {
|
||||
|
||||
func notify(_ event: Event) {
|
||||
Log.debug(event)
|
||||
// TODO: implement
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// MARK: - event handler
|
||||
extension Nexus {
|
||||
func handleEntityCreated(_ e: EntityCreated) { print(e) }
|
||||
func handleEntityDestroyed(_ e: EntityDestroyed) { print(e) }
|
||||
|
||||
func handleComponentAdded(_ e: ComponentAdded) { print(e) }
|
||||
func handleComponentUpdated(_ e: ComponentUpdated) { print(e) }
|
||||
func handleComponentRemoved(_ e: ComponentRemoved) { print(e) }
|
||||
|
||||
func handleFamilyCreated(_ e: FamilyCreated) {
|
||||
print(e)
|
||||
let newFamily: Family = e.family
|
||||
onFamilyCreated(newFamily)
|
||||
|
||||
}
|
||||
func handleFamilyMemberAdded(_ e: FamilyMemberAdded) { print(e) }
|
||||
func handleFamilyMemberRemoved(_ e: FamilyMemberRemoved) { print(e) }
|
||||
func handleFamilyDestroyed(_ e: FamilyDestroyed) { print(e) }
|
||||
}
|
||||
*/
|
||||
|
|
@ -14,7 +14,3 @@ public protocol System: class {
|
|||
func startup()
|
||||
func shutdown()
|
||||
}
|
||||
|
||||
public protocol EntitySystem: System {
|
||||
//TODO: var systemFamily: Family { get }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
//
|
||||
// UCT.swift
|
||||
// FirebladeECS
|
||||
//
|
||||
// Created by Christian Treffs on 08.10.17.
|
||||
//
|
||||
|
||||
// MARK: Unique Component Type
|
||||
/// Unique Component Type
|
||||
public struct UCT {
|
||||
let objectIdentifier: ObjectIdentifier
|
||||
let type: Component.Type
|
||||
|
||||
init(_ componentType: Component.Type) {
|
||||
objectIdentifier = ObjectIdentifier(componentType)
|
||||
type = componentType
|
||||
}
|
||||
|
||||
init(_ component: Component) {
|
||||
let componentType: Component.Type = component.uct.type
|
||||
self.init(componentType)
|
||||
}
|
||||
}
|
||||
|
||||
extension UCT: Equatable {
|
||||
public static func ==(lhs: UCT, rhs: UCT) -> Bool {
|
||||
return lhs.objectIdentifier == rhs.objectIdentifier
|
||||
}
|
||||
}
|
||||
|
||||
extension UCT: Hashable {
|
||||
public var hashValue: Int {
|
||||
return objectIdentifier.hashValue
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Unique Component Identifiable
|
||||
public protocol UniqueComponentIdentifiable {
|
||||
static var uct: UCT { get }
|
||||
var uct: UCT { get }
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
//
|
||||
// UEI.swift
|
||||
// FirebladeECS
|
||||
//
|
||||
// Created by Christian Treffs on 08.10.17.
|
||||
//
|
||||
|
||||
// MARK: Unique Entity Index
|
||||
|
||||
public typealias UEI = UInt32 // provides 4294967295 unique identifiers
|
||||
|
||||
public extension UEI {
|
||||
|
||||
private static var currentUEI: UEI = UInt32.min // starts at 0
|
||||
|
||||
/// Provides the next (higher/free) unique entity identifer.
|
||||
/// Minimum: 1, maximum: 4294967295.
|
||||
/// - Returns: next higher unique entity identifer.
|
||||
public static var next: UEI {
|
||||
currentUEI += 1
|
||||
return currentUEI
|
||||
}
|
||||
|
||||
internal static func free(_ uei: UEI) {
|
||||
// TODO: free used index
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Unique Entity Identifiable
|
||||
public protocol UniqueEntityIdentifiable: Hashable {
|
||||
var uei: UEI { get }
|
||||
}
|
||||
|
||||
public extension UniqueEntityIdentifiable {
|
||||
public var hashValue: Int { return uei.hashValue }
|
||||
}
|
||||
|
|
@ -13,6 +13,15 @@ struct Name: Component {
|
|||
let name: String
|
||||
}
|
||||
|
||||
struct Position: Component {
|
||||
let x: Int
|
||||
let y: Int
|
||||
}
|
||||
|
||||
struct Velocity: Component {
|
||||
let a: Float
|
||||
}
|
||||
|
||||
class DebugEventHandler: EventHandler {
|
||||
|
||||
let eventHub = DefaultEventHub()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
//
|
||||
// EntityComponentTests.swift
|
||||
// FirebladeECS
|
||||
//
|
||||
// Created by Christian Treffs on 13.10.17.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
/*@testable */import FirebladeECS
|
||||
|
||||
|
||||
class EntityComponentTests: XCTestCase {
|
||||
|
||||
func testComponentAccess() {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
//
|
||||
// EntityHubTests.swift
|
||||
// FirebladeECS
|
||||
//
|
||||
// Created by Christian Treffs on 09.10.17.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import FirebladeECS
|
||||
|
||||
class EntityHubTests: XCTestCase {
|
||||
|
||||
let entityHub = EntityHub()
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
entityHub.eventHub.sniffer = self
|
||||
}
|
||||
|
||||
func testCreateEntity() {
|
||||
let newEntity: Entity = entityHub.createEntity()
|
||||
|
||||
XCTAssert(newEntity.hasComponents == false)
|
||||
//TODO: XCTAssert(entityHub.entities[newEntity.uei] == newEntity)
|
||||
//TODO: XCTAssert(entityHub.entities[newEntity.uei] === newEntity)
|
||||
}
|
||||
|
||||
func testCreateEntityAndAddComponent() {
|
||||
let newEntity: Entity = entityHub.createEntity()
|
||||
|
||||
let emptyComp = EmptyComponent()
|
||||
|
||||
newEntity.add(component: emptyComp)
|
||||
|
||||
XCTAssert(newEntity.hasComponents)
|
||||
XCTAssert(newEntity.numComponents == 1)
|
||||
|
||||
//TODO: XCTAssert(entityHub.entities[newEntity.uei] == newEntity)
|
||||
//TODO: XCTAssert(entityHub.entities[newEntity.uei] === newEntity)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension EntityHubTests: EventSniffer {
|
||||
public func subscriber(subsribed to: UET) {
|
||||
|
||||
}
|
||||
|
||||
public func subscriber(unsubsribed from: UET) {
|
||||
|
||||
}
|
||||
|
||||
public func dispatched<E>(event: E) where E: Event {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -10,40 +10,5 @@ import XCTest
|
|||
|
||||
class FamilyTests: XCTestCase {
|
||||
|
||||
let entityHub: EntityHub = EntityHub()
|
||||
|
||||
func testFamily() {
|
||||
|
||||
let e1 = entityHub.createEntity()
|
||||
e1 += EmptyComponent()
|
||||
let e2 = entityHub.createEntity()
|
||||
e2 += EmptyComponent()
|
||||
e1 += Name(name: "Sarah")
|
||||
|
||||
let (empty, name) = e1.components(EmptyComponent.self, Name.self)
|
||||
|
||||
print(empty, name)
|
||||
|
||||
e1.components { (empty: EmptyComponent, name: Name) in
|
||||
print(empty, name)
|
||||
}
|
||||
|
||||
let traits = FamilyTraits(hasAll: [EmptyComponent.uct], hasAny: [], hasNone: [])
|
||||
|
||||
let (new, family) = entityHub.family(with: traits)
|
||||
XCTAssert(new == true)
|
||||
|
||||
let (new2, _) = entityHub.family(with: traits)
|
||||
XCTAssert(new2 == false)
|
||||
|
||||
let e3 = entityHub.createEntity()
|
||||
e3 += EmptyComponent()
|
||||
e3 += Name(name: "Michael")
|
||||
|
||||
e2.remove(EmptyComponent.self)
|
||||
|
||||
family.components { (name: Name, empty: EmptyComponent) in
|
||||
print(name, empty)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
//
|
||||
// NexusTests.swift
|
||||
// FirebladeECS
|
||||
//
|
||||
// Created by Christian Treffs on 09.10.17.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import FirebladeECS
|
||||
|
||||
class NexusTests: XCTestCase {
|
||||
|
||||
let nexus = Nexus()
|
||||
|
||||
func testNexus() {
|
||||
|
||||
let e0 = nexus.create(entity: "E0")
|
||||
XCTAssert(e0.identifier == 0)
|
||||
XCTAssert(e0.identifier.index == 0)
|
||||
|
||||
let p0 = Position(x: 1, y: 2)
|
||||
let n0 = Name(name: "FirstName")
|
||||
|
||||
e0.add(p0)
|
||||
e0.add(n0)
|
||||
|
||||
let rE1 = nexus.get(entity: e0.identifier)
|
||||
|
||||
let rN0: Name = rE1!.component(Name.self)
|
||||
let rP0: Position = rE1!.component(Position.self)
|
||||
|
||||
|
||||
XCTAssert(rN0.name == "FirstName")
|
||||
XCTAssert(rP0.x == 1)
|
||||
XCTAssert(rP0.y == 2)
|
||||
|
||||
let count = nexus.count(components: rE1!.identifier)
|
||||
|
||||
XCTAssert(count == 2)
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -10,98 +10,6 @@ import XCTest
|
|||
|
||||
class PerformanceTests: XCTestCase {
|
||||
|
||||
var eventHub: DefaultEventHub = DefaultEventHub()
|
||||
|
||||
var entityArray: [Entity] = [Entity]()
|
||||
var entityContArray: ContiguousArray<Entity> = ContiguousArray<Entity>()
|
||||
|
||||
var entitySet: Set<Entity> = Set<Entity>()
|
||||
var entityMap: [UEI: Entity] = [UEI: Entity]()
|
||||
|
||||
static let maxNum: Int = 65536
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
entitySet.removeAll()
|
||||
entitySet.reserveCapacity(0)
|
||||
|
||||
entityMap.removeAll()
|
||||
entityMap.reserveCapacity(0)
|
||||
|
||||
entityArray.removeAll()
|
||||
entityArray.reserveCapacity(0)
|
||||
|
||||
entityContArray.removeAll()
|
||||
entityContArray.reserveCapacity(0)
|
||||
|
||||
}
|
||||
|
||||
func testEntitySetLinearInsert() {
|
||||
measure {
|
||||
for i in 0..<65536 {
|
||||
let newEntity = Entity(uei: UEI(i), dispatcher: eventHub)
|
||||
entitySet.insert(newEntity)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testEntityMapLinearInsert() {
|
||||
entityMap.removeAll()
|
||||
entityMap.reserveCapacity(0)
|
||||
measure {
|
||||
for i in 0..<65536 {
|
||||
let newEntity = Entity(uei: UEI(i), dispatcher: eventHub)
|
||||
entityMap[newEntity.uei] = newEntity
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testEntityMapUpdateLinarInsert() {
|
||||
entityMap.removeAll()
|
||||
entityMap.reserveCapacity(0)
|
||||
|
||||
measure {
|
||||
for i in 0..<65536 {
|
||||
let newEntity = Entity(uei: UEI(i), dispatcher: eventHub)
|
||||
entityMap.updateValue(newEntity, forKey: newEntity.uei)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testEntityContArrayLinearInsert() {
|
||||
measure {
|
||||
for i in 0..<65536 {
|
||||
let newEntity = Entity(uei: UEI(i), dispatcher: eventHub)
|
||||
entityContArray.append(newEntity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testEntityContArrayFixPosInsert() {
|
||||
|
||||
let count: Int = Int(65536)
|
||||
var entityContArray2 = ContiguousArray<Entity!>(repeating: nil, count: count)
|
||||
entityContArray2.reserveCapacity(count)
|
||||
measure {
|
||||
for i: Int in 0..<count-1 {
|
||||
let uei = UEI(i)
|
||||
let newEntity = Entity(uei: uei, dispatcher: eventHub)
|
||||
|
||||
entityContArray2[i] = newEntity
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testEntityArrayLinearInsert() {
|
||||
measure {
|
||||
for i in 0..<65536 {
|
||||
let newEntity = Entity(uei: UEI(i), dispatcher: eventHub)
|
||||
entityArray.append(newEntity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// SystemTests.swift
|
||||
// FirebladeECS
|
||||
//
|
||||
// Created by Christian Treffs on 11.10.17.
|
||||
//
|
||||
|
||||
|
||||
|
||||
import XCTest
|
||||
import FirebladeECS
|
||||
|
||||
|
||||
class SystemTests: XCTestCase {
|
||||
|
||||
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue