Stabelize API

This commit is contained in:
Christian Treffs 2017-10-22 12:53:21 +02:00
parent 5371107f4f
commit e07ac8e248
8 changed files with 150 additions and 141 deletions

View File

@ -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)
}
}

View File

@ -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 }
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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 {

View File

@ -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)
}
}

View File

@ -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
}
}
}
}

View File

@ -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