Stabelize API
This commit is contained in:
parent
5371107f4f
commit
e07ac8e248
|
|
@ -16,14 +16,14 @@ extension Component {
|
|||
}
|
||||
|
||||
// MARK: - entity component hashable
|
||||
public extension Component {
|
||||
internal 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 {
|
||||
internal static func hashValue(using entityIdx: EntityIndex) -> EntityComponentHash {
|
||||
return Self.identifier.hashValue(using: entityIdx)
|
||||
}
|
||||
|
||||
|
|
@ -32,7 +32,7 @@ public extension Component {
|
|||
///
|
||||
/// - Parameter entityIdx: entity index
|
||||
/// - Returns: combinded entity component hash
|
||||
func hashValue(using entityIdx: EntityIndex) -> EntityComponentHash {
|
||||
internal func hashValue(using entityIdx: EntityIndex) -> EntityComponentHash {
|
||||
return Self.hashValue(using: entityIdx)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,24 +7,24 @@
|
|||
|
||||
public typealias ComponentIdentifier = ObjectIdentifier
|
||||
|
||||
extension ComponentIdentifier {
|
||||
// MARK: Unique Component Identifiable
|
||||
public protocol UniqueComponentIdentifiable {
|
||||
static var identifier: ComponentIdentifier { get }
|
||||
var identifier: ComponentIdentifier { get }
|
||||
}
|
||||
|
||||
internal 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 {
|
||||
internal func hashValue(using entityIdx: EntityIndex) -> EntityComponentHash {
|
||||
return hashValue(using: entityIdx.identifier)
|
||||
}
|
||||
|
||||
func hashValue(using entityId: EntityIdentifier) -> EntityComponentHash {
|
||||
internal func hashValue(using entityId: EntityIdentifier) -> EntityComponentHash {
|
||||
return EntityComponentHash.compose(entityId: entityId, componentTypeHash: hashValue)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Unique Component Identifiable
|
||||
public protocol UniqueComponentIdentifiable {
|
||||
static var identifier: ComponentIdentifier { get }
|
||||
var identifier: ComponentIdentifier { get }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
//
|
||||
// Entity+Component.swift
|
||||
// FirebladeECS
|
||||
//
|
||||
// Created by Christian Treffs on 22.10.17.
|
||||
//
|
||||
|
||||
// MARK: - get components
|
||||
public extension Entity {
|
||||
|
||||
public final func get<C>() -> C? where C: Component {
|
||||
return nexus.get(component: C.identifier, for: identifier)
|
||||
}
|
||||
|
||||
public func get<A>(component compType: A.Type = A.self) -> A? where A: Component {
|
||||
return nexus.get(component: A.identifier, for: identifier)
|
||||
}
|
||||
|
||||
public func getComponent<A>() -> () -> A? where A: Component {
|
||||
func getComponentFunc() -> A? {
|
||||
return get(component: A.self)
|
||||
}
|
||||
return getComponentFunc
|
||||
}
|
||||
|
||||
public func get<A, B>(components _: A.Type, _: B.Type) -> (A, B) where A: Component, B: Component {
|
||||
let a: A = get(component: A.self)!
|
||||
let b: B = get(component: B.self)!
|
||||
return (a, b)
|
||||
}
|
||||
public func get<A, B, C>(components _: A.Type, _: B.Type, _: C.Type) -> (A, B, C) where A: Component, B: Component, C: Component {
|
||||
let a: A = get(component: A.self)!
|
||||
let b: B = get(component: B.self)!
|
||||
let c: C = get(component: C.self)!
|
||||
return (a, b, c)
|
||||
}
|
||||
public func get<A, B, C, D>(components _: A.Type, _: B.Type, _: C.Type, _: D.Type) -> (A, B, C, D) where A: Component, B: Component, C: Component, D: Component {
|
||||
let a: A = get(component: A.self)!
|
||||
let b: B = get(component: B.self)!
|
||||
let c: C = get(component: C.self)!
|
||||
let d: D = get(component: D.self)!
|
||||
return (a, b, c, d)
|
||||
}
|
||||
public func get<A, B, C, D, E>(components _: 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 = get(component: A.self)!
|
||||
let b: B = get(component: B.self)!
|
||||
let c: C = get(component: C.self)!
|
||||
let d: D = get(component: D.self)!
|
||||
let e: E = get(component: E.self)!
|
||||
return (a, b, c, d, e)
|
||||
}
|
||||
public func get<A, B, C, D, E, F>(components _: 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 = get(component: A.self)!
|
||||
let b: B = get(component: B.self)!
|
||||
let c: C = get(component: C.self)!
|
||||
let d: D = get(component: D.self)!
|
||||
let e: E = get(component: E.self)!
|
||||
let f: F = get(component: F.self)!
|
||||
return (a, b, c, d, e, f)
|
||||
}
|
||||
}
|
||||
|
|
@ -10,9 +10,9 @@ public final class Entity: UniqueEntityIdentifiable {
|
|||
internal(set) public var identifier: EntityIdentifier = EntityIdentifier.invalid
|
||||
public var name: String?
|
||||
|
||||
fileprivate let nexus: Nexus
|
||||
internal let nexus: Nexus
|
||||
|
||||
init(nexus: Nexus, id: EntityIdentifier, name: String? = nil) {
|
||||
internal init(nexus: Nexus, id: EntityIdentifier, name: String? = nil) {
|
||||
self.nexus = nexus
|
||||
self.identifier = id
|
||||
self.name = name
|
||||
|
|
@ -27,7 +27,7 @@ extension Entity {
|
|||
return nexus.isValid(entity: self)
|
||||
}
|
||||
|
||||
func invalidate() {
|
||||
internal func invalidate() {
|
||||
assert(nexus.isValid(entity: identifier), "Invalid entity \(self) is being invalidated.")
|
||||
identifier = EntityIdentifier.invalid
|
||||
name = nil
|
||||
|
|
@ -68,9 +68,13 @@ public extension Entity {
|
|||
|
||||
@discardableResult
|
||||
public final func assign(_ components: Component...) -> Entity {
|
||||
components.forEach { (comp: Component) in
|
||||
nexus.assign(component: comp, to: self)
|
||||
}
|
||||
components.forEach { assign($0) }
|
||||
return self
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public final func assign(_ component: Component) -> Entity {
|
||||
nexus.assign(component: component, to: self)
|
||||
return self
|
||||
}
|
||||
|
||||
|
|
@ -91,18 +95,6 @@ public extension Entity {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - get components
|
||||
public extension Entity {
|
||||
|
||||
public final func get<C>() -> C? where C: Component {
|
||||
return nexus.get(component: C.identifier, for: identifier)
|
||||
}
|
||||
|
||||
public final func get<C>(_ componentType: C.Type) -> C? where C: Component {
|
||||
return get()
|
||||
}
|
||||
|
||||
}
|
||||
// MARK: - remove component(s)
|
||||
public extension Entity {
|
||||
|
||||
|
|
@ -139,58 +131,7 @@ public extension Entity {
|
|||
|
||||
// MARK: - destroy/deinit entity
|
||||
extension Entity {
|
||||
final func destroy() {
|
||||
public final func destroy() {
|
||||
nexus.destroy(entity: self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - component tuple access
|
||||
public extension Entity {
|
||||
|
||||
public func component<A>() -> () -> A? where A: Component {
|
||||
func getComponent() -> A? {
|
||||
return component(A.self)
|
||||
}
|
||||
return getComponent
|
||||
}
|
||||
|
||||
public func component<A>(_ compType: A.Type = A.self) -> A? where A: Component {
|
||||
return nexus.get(component: A.identifier, for: identifier)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,11 @@ public final class Family {
|
|||
}
|
||||
|
||||
extension Family {
|
||||
|
||||
public var count: Int {
|
||||
return nexus.members(of: self).count
|
||||
}
|
||||
|
||||
public final func canBecomeMember(_ entity: Entity) -> Bool {
|
||||
return nexus.canBecomeMember(entity, in: self)
|
||||
}
|
||||
|
|
@ -34,7 +39,7 @@ extension Family {
|
|||
return nexus.isMember(entityId, in: self)
|
||||
}
|
||||
|
||||
internal var members: LazyMapCollection<LazyFilterCollection<LazyMapCollection<EntityIdSet, Entity?>>, Entity> {
|
||||
public var members: LazyMapCollection<LazyFilterCollection<LazyMapCollection<EntityIdSet, Entity?>>, Entity> {
|
||||
return nexus.members(of: self)
|
||||
}
|
||||
internal var memberIds: EntityIdSet {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
//
|
||||
// EntityTests.swift
|
||||
// FirebladeECSTests
|
||||
//
|
||||
// Created by Christian Treffs on 22.10.17.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import FirebladeECS
|
||||
|
||||
class EntityTests: XCTestCase {
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
func testEntityIdentifierAndIndex() {
|
||||
|
||||
let min: EntityIndex = EntityIdentifier(EntityIdentifier.min).index
|
||||
XCTAssert(EntityIndex(min).identifier == min)
|
||||
|
||||
let rand: EntityIndex = EntityIdentifier(EntityIdentifier(arc4random())).index
|
||||
XCTAssert(EntityIndex(rand).identifier == rand)
|
||||
|
||||
let max: EntityIndex = EntityIdentifier(EntityIdentifier.max).index
|
||||
XCTAssert(EntityIndex(max).identifier == max)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
//
|
||||
|
||||
import XCTest
|
||||
@testable import FirebladeECS
|
||||
import FirebladeECS
|
||||
|
||||
class FamilyTests: XCTestCase {
|
||||
|
||||
|
|
@ -74,14 +74,17 @@ class FamilyTests: XCTestCase {
|
|||
|
||||
var index: Int = 0
|
||||
|
||||
family.iterate { (e: () -> Entity, p: () -> Position!, v: () -> Velocity!, n: () -> Name?) in
|
||||
family.iterate { (_: () -> Entity, pos: () -> Position!, vel: () -> Velocity!, nm: () -> Name?) in
|
||||
|
||||
let pos: Position = p()
|
||||
pos.x = 10
|
||||
let position: Position = pos()!
|
||||
let name: Name? = nm()
|
||||
|
||||
_ = position
|
||||
_ = name
|
||||
|
||||
print(e(), pos, n())
|
||||
if index == 0 {
|
||||
print(v())
|
||||
let velocity: Velocity = vel()!
|
||||
_ = velocity
|
||||
}
|
||||
// bla
|
||||
index += 1
|
||||
|
|
@ -119,9 +122,8 @@ class FamilyTests: XCTestCase {
|
|||
|
||||
let family = nexus.family(requiresAll: [Position.self, Velocity.self], excludesAll: [Party.self], needsAtLeastOne: [Name.self, EmptyComponent.self])
|
||||
|
||||
XCTAssert(family.members.count == number)
|
||||
XCTAssert(family.memberIds.count == number)
|
||||
XCTAssert(nexus.entities.count == number)
|
||||
XCTAssert(family.count == number)
|
||||
XCTAssert(nexus.count == number)
|
||||
|
||||
measure {
|
||||
family.iterate(components: Position.self, Velocity.self, Name.self) { (_, pos, vel, nm) in
|
||||
|
|
@ -147,9 +149,8 @@ class FamilyTests: XCTestCase {
|
|||
|
||||
let family = nexus.family(requiresAll: [Position.self, Velocity.self], excludesAll: [Party.self], needsAtLeastOne: [Name.self, EmptyComponent.self])
|
||||
|
||||
XCTAssert(family.members.count == number)
|
||||
XCTAssert(family.memberIds.count == number)
|
||||
XCTAssert(nexus.entities.count == number)
|
||||
XCTAssert(family.count == number)
|
||||
XCTAssert(nexus.count == number)
|
||||
|
||||
measure {
|
||||
family.iterate { (_: () -> Entity, pos: () -> Position!, vel: () -> Velocity!, nm: () -> Name?) in
|
||||
|
|
@ -165,21 +166,4 @@ class FamilyTests: XCTestCase {
|
|||
|
||||
}
|
||||
|
||||
func testMeasureEntityIteration() {
|
||||
let nexus = Nexus()
|
||||
let number: Int = 10_000
|
||||
|
||||
for i in 0..<number {
|
||||
nexus.create(entity: "\(i)").assign(Position(x: 1+i, y: 2+i), Name(name: "myName\(i)"), Velocity(a: 3.14), EmptyComponent())
|
||||
}
|
||||
|
||||
let family = nexus.family(requiresAll: [Position.self, Velocity.self], excludesAll: [Party.self], needsAtLeastOne: [Name.self, EmptyComponent.self])
|
||||
|
||||
measure {
|
||||
family.memberIds.forEach { (e) in
|
||||
_ = e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import Darwin.C.stdlib
|
||||
import XCTest
|
||||
@testable import FirebladeECS
|
||||
import FirebladeECS
|
||||
|
||||
class NexusTests: XCTestCase {
|
||||
|
||||
|
|
@ -19,31 +19,18 @@ class NexusTests: XCTestCase {
|
|||
super.tearDown()
|
||||
}
|
||||
|
||||
func testEntityIdentifierAndIndex() {
|
||||
|
||||
let min: EntityIndex = EntityIdentifier(EntityIdentifier.min).index
|
||||
XCTAssert(EntityIndex(min).identifier == min)
|
||||
|
||||
let rand: EntityIndex = EntityIdentifier(EntityIdentifier(arc4random())).index
|
||||
XCTAssert(EntityIndex(rand).identifier == rand)
|
||||
|
||||
let max: EntityIndex = EntityIdentifier(EntityIdentifier.max).index
|
||||
XCTAssert(EntityIndex(max).identifier == max)
|
||||
|
||||
}
|
||||
|
||||
func testCreateEntity() {
|
||||
let nexus: Nexus = Nexus()
|
||||
XCTAssert(nexus.count == 0)
|
||||
|
||||
let e0 = nexus.create()
|
||||
XCTAssert(e0.identifier.index == 0)
|
||||
XCTAssert(nexus.isValid(entity: e0))
|
||||
XCTAssert(e0.isValid)
|
||||
XCTAssert(nexus.count == 1)
|
||||
|
||||
let e1 = nexus.create(entity: "Named e1")
|
||||
XCTAssert(e1.identifier.index == 1)
|
||||
XCTAssert(nexus.isValid(entity: e1))
|
||||
XCTAssert(e1.isValid)
|
||||
XCTAssert(nexus.count == 2)
|
||||
|
||||
XCTAssert(e0.name == nil)
|
||||
|
|
@ -57,22 +44,20 @@ class NexusTests: XCTestCase {
|
|||
func testDestroyAndReuseEntity() {
|
||||
let nexus: Nexus = Nexus()
|
||||
XCTAssert(nexus.count == 0)
|
||||
XCTAssert(nexus.freeEntities.count == 0)
|
||||
|
||||
let e0 = nexus.create(entity: "e0")
|
||||
XCTAssert(nexus.isValid(entity: e0))
|
||||
XCTAssert(e0.isValid)
|
||||
XCTAssert(nexus.count == 1)
|
||||
|
||||
let e1 = nexus.create(entity: "e1")
|
||||
XCTAssert(nexus.isValid(entity: e1))
|
||||
XCTAssert(e1.isValid)
|
||||
XCTAssert(nexus.count == 2)
|
||||
|
||||
e0.destroy()
|
||||
|
||||
XCTAssert(!nexus.isValid(entity: e0))
|
||||
XCTAssert(nexus.isValid(entity: e1))
|
||||
XCTAssert(!e0.isValid)
|
||||
XCTAssert(e1.isValid)
|
||||
XCTAssert(nexus.count == 1)
|
||||
XCTAssert(nexus.freeEntities.count == 1)
|
||||
|
||||
let e2 = nexus.create(entity: "e2")
|
||||
XCTAssert(!e0.isValid)
|
||||
|
|
@ -80,7 +65,6 @@ class NexusTests: XCTestCase {
|
|||
XCTAssert(e2.isValid)
|
||||
|
||||
XCTAssert(nexus.count == 2)
|
||||
XCTAssert(nexus.freeEntities.count == 0)
|
||||
|
||||
XCTAssert(!(e0 == e2))
|
||||
XCTAssert(!(e0 === e2))
|
||||
|
|
@ -89,8 +73,6 @@ class NexusTests: XCTestCase {
|
|||
func testComponentCreation() {
|
||||
let nexus: Nexus = Nexus()
|
||||
XCTAssert(nexus.count == 0)
|
||||
XCTAssert(nexus.freeEntities.isEmpty)
|
||||
XCTAssert(nexus.componentsByType.isEmpty)
|
||||
|
||||
let e0: Entity = nexus.create(entity: "e0")
|
||||
|
||||
|
|
@ -102,7 +84,7 @@ class NexusTests: XCTestCase {
|
|||
XCTAssert(e0.hasComponents)
|
||||
XCTAssert(e0.numComponents == 1)
|
||||
|
||||
let rP0: Position = e0.component(Position.self)!
|
||||
let rP0: Position = e0.get(component: Position.self)!
|
||||
XCTAssert(rP0.x == 1)
|
||||
XCTAssert(rP0.y == 2)
|
||||
}
|
||||
|
|
@ -143,7 +125,7 @@ class NexusTests: XCTestCase {
|
|||
e0.assign(p0)
|
||||
|
||||
XCTAssert(e0.numComponents == 2)
|
||||
let (name, position) = e0.components(Name.self, Position.self)
|
||||
let (name, position) = e0.get(components: Name.self, Position.self)
|
||||
|
||||
XCTAssert(name.name == "myName")
|
||||
XCTAssert(position.x == 99)
|
||||
|
|
@ -168,8 +150,8 @@ class NexusTests: XCTestCase {
|
|||
b.assign(Position(x: 0, y: 0))
|
||||
c.assign(Position(x: 0, y: 0))
|
||||
|
||||
let pA: Position = a.component()!
|
||||
let pB: Position = b.component()!
|
||||
let pA: Position = a.get()!
|
||||
let pB: Position = b.get()!
|
||||
|
||||
pA.x = 23
|
||||
pA.y = 32
|
||||
|
|
|
|||
Loading…
Reference in New Issue