Merge pull request #6 from fireblade-engine/feature/scene-graph
Feature/scene graph
This commit is contained in:
commit
2270191f2e
|
|
@ -0,0 +1,28 @@
|
|||
image: swift:5.0
|
||||
|
||||
#before_script:
|
||||
#- eval "$(curl -sL https://swiftenv.fuller.li/install.sh)"
|
||||
|
||||
stages:
|
||||
- test
|
||||
- build_release
|
||||
|
||||
build_project:
|
||||
stage: build_release
|
||||
script:
|
||||
- swift package reset
|
||||
- swift build -c release
|
||||
tags:
|
||||
- docker
|
||||
only:
|
||||
- master
|
||||
|
||||
test_project:
|
||||
stage: test
|
||||
variables:
|
||||
GIT_DEPTH: "50"
|
||||
script:
|
||||
- swift package reset
|
||||
- swift test
|
||||
tags:
|
||||
- docker
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
included:
|
||||
- Sources
|
||||
excluded:
|
||||
- Tests
|
||||
- docs
|
||||
- build
|
||||
identifier_name:
|
||||
|
|
@ -11,6 +10,17 @@ line_length: 220
|
|||
number_separator:
|
||||
minimum_length: 5
|
||||
opt_in_rules:
|
||||
#- anyobject_protocol
|
||||
#- explicit_acl
|
||||
#- explicit_enum_raw_value
|
||||
#- explicit_type_interface
|
||||
#- extension_access_modifier
|
||||
#- file_header
|
||||
#- file_name
|
||||
#- missing_docs
|
||||
#- multiline_arguments_brackets
|
||||
#- no_grouping_extension
|
||||
#- multiline_literal_brackets
|
||||
- array_init
|
||||
- attributes
|
||||
- closure_body_length
|
||||
|
|
@ -29,7 +39,7 @@ opt_in_rules:
|
|||
- empty_xctest_method
|
||||
- explicit_init
|
||||
- explicit_self
|
||||
- extension_access_modifier
|
||||
- explicit_top_level_acl
|
||||
- fallthrough
|
||||
- fatal_error_message
|
||||
- first_where
|
||||
|
|
@ -47,7 +57,9 @@ opt_in_rules:
|
|||
- multiline_arguments
|
||||
- multiline_function_chains
|
||||
- multiline_parameters
|
||||
- multiline_parameters_brackets
|
||||
- nimble_operator
|
||||
- no_extension_access_modifier
|
||||
- number_separator
|
||||
- object_literal
|
||||
- operator_usage_whitespace
|
||||
|
|
|
|||
11
.travis.yml
11
.travis.yml
|
|
@ -11,3 +11,14 @@ script:
|
|||
- swift package reset
|
||||
- swift build
|
||||
- swift test
|
||||
env:
|
||||
- BADGE=linux
|
||||
- BADGE=osx
|
||||
# hack to get some OS-specific badges
|
||||
# see: https://github.com/travis-ci/travis-ci/issues/9579
|
||||
matrix:
|
||||
exclude:
|
||||
- os: linux
|
||||
env: BADGE=osx
|
||||
- os: osx
|
||||
env: BADGE=linux
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
lint:
|
||||
swiftlint autocorrect --format
|
||||
swiftlint lint --quiet
|
||||
|
||||
genLinuxTests:
|
||||
swift test --generate-linuxmain
|
||||
swiftlint autocorrect --format --path Tests/
|
||||
|
||||
test: genLinuxTests
|
||||
swift test
|
||||
|
||||
clean:
|
||||
swift package reset
|
||||
rm -rdf .swiftpm/xcode
|
||||
rm -rdf .build/
|
||||
rm Package.resolved
|
||||
rm .DS_Store
|
||||
|
||||
cleanArtifacts:
|
||||
swift package clean
|
||||
|
||||
genXcode:
|
||||
swift package generate-xcodeproj --enable-code-coverage --skip-extra-files
|
||||
|
||||
latest:
|
||||
swift package update
|
||||
|
||||
resolve:
|
||||
swift package resolve
|
||||
|
||||
genXcodeOpen: genXcode
|
||||
open *.xcodeproj
|
||||
|
||||
precommit: lint genLinuxTests
|
||||
39
README.md
39
README.md
|
|
@ -1,9 +1,9 @@
|
|||
# Fireblade ECS (Entity-Component-System)
|
||||
[](https://travis-ci.com/fireblade-engine/ecs)
|
||||
[](releases/tag/v0.9.1)
|
||||
[](LICENSE)
|
||||
[](#)
|
||||
[](#)
|
||||
[](https://swift.org/download)
|
||||
[](#)
|
||||
[](#)
|
||||
|
||||
This is a **dependency free**, **lightweight**, **fast** and **easy to use** [Entity-Component-System](https://en.wikipedia.org/wiki/Entity–component–system) implementation in Swift. It is developed and maintained as part of the [Fireblade Game Engine project](https://github.com/fireblade-engine).
|
||||
|
||||
|
|
@ -33,7 +33,7 @@ import PackageDescription
|
|||
let package = Package(
|
||||
name: "YourPackageName",
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/fireblade-engine/ecs.git", from: "0.9.1")
|
||||
.package(url: "https://github.com/fireblade-engine/ecs.git", from: "0.10.0")
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
|
|
@ -184,6 +184,37 @@ class GameLogicSystem {
|
|||
|
||||
```
|
||||
|
||||
### 👫 Relatives
|
||||
|
||||
This ECS implementation provides an integrated way of creating a [directed acyclic graph (DAG)](https://en.wikipedia.org/wiki/Directed_acyclic_graph) hierarchy of entities by forming parent-child relationships. Entities can become children of a parent entity. In family terms they become **relatives**. Families provide iteration over these relationships.
|
||||
The entity hierachy implementation does not use an additional component therefore keeping the hierarchy intact over different component-families.
|
||||
This feature is especially useful for implenting a [scene graph](https://en.wikipedia.org/wiki/Scene_graph).
|
||||
|
||||
```swift
|
||||
// create entities with 0 to n components
|
||||
let parent: Entity = nexus.createEntity(with: Position(x: 1, y: 1), SomeOtherComponent(...))
|
||||
let child: Entity = nexus.createEntity(with: Position(x: 2, y: 2))
|
||||
let child2: Entity = nexus.createEntity(with: Position(x: 3, y: 3), MySpecialComponent(...))
|
||||
|
||||
// create relationships between entities
|
||||
parent.addChild(child)
|
||||
child.addChild(child2)
|
||||
// or remove them
|
||||
// parent.removeChild(child)
|
||||
|
||||
// iterate over component families descending the graph
|
||||
nexus.family(requires: Position.self)
|
||||
.descendRelatives(from: parent) // provide the start entity (aka root "node")
|
||||
.forEach { (parent: Position, child: Position) in
|
||||
// parent: the current parent component
|
||||
// child: the current child component
|
||||
|
||||
// update your components hierarchically
|
||||
child.x += parent.x
|
||||
child.y += parent.y
|
||||
}
|
||||
```
|
||||
|
||||
## 🧪 Demo
|
||||
|
||||
See the [Fireblade ECS Demo App](https://github.com/fireblade-engine/ecs-demo) to get started.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// Component+Access.swift
|
||||
//
|
||||
//
|
||||
//
|
||||
// Created by Christian Treffs on 25.06.19.
|
||||
//
|
||||
|
|
@ -14,7 +14,8 @@ public struct ReadableOnly<Comp> where Comp: Component {
|
|||
self.component = component
|
||||
}
|
||||
|
||||
@inlinable public subscript<C>(dynamicMember keyPath: KeyPath<Comp, C>) -> C {
|
||||
@inlinable
|
||||
public subscript<C>(dynamicMember keyPath: KeyPath<Comp, C>) -> C {
|
||||
return component[keyPath: keyPath]
|
||||
}
|
||||
}
|
||||
|
|
@ -27,7 +28,8 @@ public struct Writable<Comp> where Comp: Component {
|
|||
self.component = component
|
||||
}
|
||||
|
||||
@inlinable public subscript<C>(dynamicMember keyPath: ReferenceWritableKeyPath<Comp, C>) -> C {
|
||||
@inlinable
|
||||
public subscript<C>(dynamicMember keyPath: ReferenceWritableKeyPath<Comp, C>) -> C {
|
||||
nonmutating get { return component[keyPath: keyPath] }
|
||||
nonmutating set { component[keyPath: keyPath] = newValue }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ public protocol Component: class {
|
|||
var identifier: ComponentIdentifier { get }
|
||||
}
|
||||
|
||||
public extension Component {
|
||||
static var identifier: ComponentIdentifier { return ComponentIdentifier(Self.self) }
|
||||
@inlinable var identifier: ComponentIdentifier { return Self.identifier }
|
||||
extension Component {
|
||||
public static var identifier: ComponentIdentifier { return ComponentIdentifier(Self.self) }
|
||||
@inlinable public var identifier: ComponentIdentifier { return Self.identifier }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// ComponentIdentifier.swift
|
||||
//
|
||||
//
|
||||
//
|
||||
// Created by Christian Treffs on 20.08.19.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -5,30 +5,30 @@
|
|||
// Created by Christian Treffs on 22.10.17.
|
||||
//
|
||||
|
||||
public extension Entity {
|
||||
extension Entity {
|
||||
@inlinable
|
||||
func get<C>() -> C? where C: Component {
|
||||
return nexus.get(for: identifier)
|
||||
}
|
||||
public func get<C>() -> C? where C: Component {
|
||||
return nexus.get(for: identifier)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
func get<A>(component compType: A.Type = A.self) -> A? where A: Component {
|
||||
return nexus.get(for: identifier)
|
||||
}
|
||||
public func get<A>(component compType: A.Type = A.self) -> A? where A: Component {
|
||||
return nexus.get(for: identifier)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
func get<A, B>(components _: A.Type, _: B.Type) -> (A?, B?) where A: Component, B: Component {
|
||||
let compA: A? = get(component: A.self)
|
||||
let compB: B? = get(component: B.self)
|
||||
return (compA, compB)
|
||||
}
|
||||
public func get<A, B>(components _: A.Type, _: B.Type) -> (A?, B?) where A: Component, B: Component {
|
||||
let compA: A? = get(component: A.self)
|
||||
let compB: B? = get(component: B.self)
|
||||
return (compA, compB)
|
||||
}
|
||||
|
||||
// swiftlint:disable large_tuple
|
||||
@inlinable
|
||||
func get<A, B, C>(components _: A.Type, _: B.Type, _: C.Type) -> (A?, B?, C?) where A: Component, B: Component, C: Component {
|
||||
let compA: A? = get(component: A.self)
|
||||
let compB: B? = get(component: B.self)
|
||||
let compC: C? = get(component: C.self)
|
||||
return (compA, compB, compC)
|
||||
}
|
||||
@inlinable
|
||||
public func get<A, B, C>(components _: A.Type, _: B.Type, _: C.Type) -> (A?, B?, C?) where A: Component, B: Component, C: Component {
|
||||
let compA: A? = get(component: A.self)
|
||||
let compB: B? = get(component: B.self)
|
||||
let compC: C? = get(component: C.self)
|
||||
return (compA, compB, compC)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -101,12 +101,36 @@ public struct Entity {
|
|||
public func destroy() {
|
||||
nexus.destroy(entity: self)
|
||||
}
|
||||
|
||||
/// Add an entity as child.
|
||||
/// - Parameter entity: The child entity.
|
||||
@discardableResult
|
||||
public func addChild(_ entity: Entity) -> Bool {
|
||||
return nexus.addChild(entity, to: self)
|
||||
}
|
||||
|
||||
/// Remove entity as child.
|
||||
/// - Parameter entity: The child entity.
|
||||
@discardableResult
|
||||
public func removeChild(_ entity: Entity) -> Bool {
|
||||
return nexus.removeChild(entity, from: self)
|
||||
}
|
||||
|
||||
/// Removes all children from this entity.
|
||||
public func removeAllChildren() {
|
||||
return nexus.removeAllChildren(from: self)
|
||||
}
|
||||
|
||||
/// Returns the number of children for this entity.
|
||||
public var numChildren: Int {
|
||||
return nexus.numChildren(for: self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Equatable
|
||||
extension Entity: Equatable {
|
||||
public static func == (lhs: Entity, rhs: Entity) -> Bool {
|
||||
return lhs.nexus == rhs.nexus &&
|
||||
lhs.identifier == rhs.identifier
|
||||
lhs.identifier == rhs.identifier
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,21 +11,17 @@ public struct EntityIdentifier {
|
|||
/// provides 4294967295 unique identifiers since it's constrained to UInt32 - invalid.
|
||||
public let index: Int
|
||||
|
||||
private init() {
|
||||
self = .invalid
|
||||
}
|
||||
|
||||
public init(_ uint32: UInt32) {
|
||||
self.index = Int(uint32)
|
||||
}
|
||||
}
|
||||
|
||||
extension EntityIdentifier: Equatable { }
|
||||
|
||||
extension EntityIdentifier: Hashable { }
|
||||
|
||||
extension EntityIdentifier: Codable { }
|
||||
extension EntityIdentifier: Comparable {
|
||||
@inlinable public static func < (lhs: EntityIdentifier, rhs: EntityIdentifier) -> Bool {
|
||||
@inlinable
|
||||
public static func < (lhs: EntityIdentifier, rhs: EntityIdentifier) -> Bool {
|
||||
return lhs.index < rhs.index
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// Family.swift
|
||||
//
|
||||
//
|
||||
//
|
||||
// Created by Christian Treffs on 21.08.19.
|
||||
//
|
||||
|
|
@ -40,6 +40,14 @@ public struct Family<R> where R: FamilyRequirementsManaging {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - Equatable
|
||||
extension Family: Equatable {
|
||||
public static func == (lhs: Family<R>, rhs: Family<R>) -> Bool {
|
||||
return lhs.nexus == rhs.nexus &&
|
||||
lhs.traits == rhs.traits
|
||||
}
|
||||
}
|
||||
|
||||
extension Family: Sequence {
|
||||
__consuming public func makeIterator() -> ComponentsIterator {
|
||||
return ComponentsIterator(family: self)
|
||||
|
|
@ -123,10 +131,57 @@ extension Family {
|
|||
|
||||
extension Family.EntityComponentIterator: LazySequenceProtocol { }
|
||||
|
||||
// MARK: - Equatable
|
||||
extension Family: Equatable {
|
||||
public static func == (lhs: Family<R>, rhs: Family<R>) -> Bool {
|
||||
return lhs.nexus == rhs.nexus &&
|
||||
lhs.traits == rhs.traits
|
||||
// MARK: - relatives iterator
|
||||
|
||||
extension Family {
|
||||
@inlinable
|
||||
public func descendRelatives(from root: Entity) -> RelativesIterator {
|
||||
return RelativesIterator(family: self, root: root)
|
||||
}
|
||||
|
||||
public struct RelativesIterator: IteratorProtocol {
|
||||
@usableFromInline unowned let nexus: Nexus
|
||||
@usableFromInline let familyTraits: FamilyTraitSet
|
||||
|
||||
@usableFromInline var relatives: ContiguousArray<(EntityIdentifier, EntityIdentifier)>
|
||||
|
||||
public init(family: Family<R>, root: Entity) {
|
||||
self.nexus = family.nexus
|
||||
self.familyTraits = family.traits
|
||||
|
||||
// FIXME: this is not the most efficient way to aggregate all parent child tuples
|
||||
// Problems:
|
||||
// - allocates new memory
|
||||
// - needs to be build on every iteration
|
||||
// - relies on isMember check
|
||||
self.relatives = []
|
||||
self.relatives.reserveCapacity(family.memberIds.count)
|
||||
aggregateRelativesBreathFirst(root.identifier)
|
||||
relatives.reverse()
|
||||
}
|
||||
|
||||
mutating func aggregateRelativesBreathFirst(_ parent: EntityIdentifier) {
|
||||
guard let children = nexus.parentChildrenMap[parent] else {
|
||||
return
|
||||
}
|
||||
children
|
||||
.compactMap { child in
|
||||
guard nexus.isMember(child, in: familyTraits) else {
|
||||
return nil
|
||||
}
|
||||
relatives.append((parent, child))
|
||||
return child
|
||||
}
|
||||
.forEach { aggregateRelativesBreathFirst($0) }
|
||||
}
|
||||
|
||||
public mutating func next() -> R.RelativesDescending? {
|
||||
guard let (parentId, childId) = relatives.popLast() else {
|
||||
return nil
|
||||
}
|
||||
return R.relativesDescending(nexus: nexus, parentId: parentId, childId: childId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Family.RelativesIterator: LazySequenceProtocol { }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// Family1.swift
|
||||
//
|
||||
//
|
||||
//
|
||||
// Created by Christian Treffs on 21.08.19.
|
||||
//
|
||||
|
|
@ -24,6 +24,12 @@ public struct Requires1<A>: FamilyRequirementsManaging where A: Component {
|
|||
let compA: A = nexus.get(unsafeComponentFor: entityId)
|
||||
return (entity, compA)
|
||||
}
|
||||
|
||||
public static func relativesDescending(nexus: Nexus, parentId: EntityIdentifier, childId: EntityIdentifier) -> (parent: A, child: A) {
|
||||
let parentCompA: A = nexus.get(unsafeComponentFor: parentId)
|
||||
let childCompA: A = nexus.get(unsafeComponentFor: childId)
|
||||
return (parent: parentCompA, child: childCompA)
|
||||
}
|
||||
}
|
||||
|
||||
extension Nexus {
|
||||
|
|
@ -32,7 +38,7 @@ extension Nexus {
|
|||
excludesAll excludedComponents: Component.Type...
|
||||
) -> Family1<A> where A: Component {
|
||||
return Family1<A>(nexus: self,
|
||||
requiresAll: componentA,
|
||||
excludesAll: excludedComponents)
|
||||
requiresAll: componentA,
|
||||
excludesAll: excludedComponents)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
//
|
||||
// Family2.swift
|
||||
//
|
||||
//
|
||||
//
|
||||
// Created by Christian Treffs on 21.08.19.
|
||||
//
|
||||
|
||||
// swiftlint:disable large_tuple
|
||||
|
||||
public typealias Family2<A: Component, B: Component> = Family<Requires2<A, B>>
|
||||
|
||||
public struct Requires2<A, B>: FamilyRequirementsManaging where A: Component, B: Component {
|
||||
|
|
@ -25,6 +27,14 @@ public struct Requires2<A, B>: FamilyRequirementsManaging where A: Component, B:
|
|||
let compB: B = nexus.get(unsafeComponentFor: entityId)
|
||||
return (entity, compA, compB)
|
||||
}
|
||||
|
||||
public static func relativesDescending(nexus: Nexus, parentId: EntityIdentifier, childId: EntityIdentifier) -> (parent: (A, B), child: (A, B)) {
|
||||
let pcA: A = nexus.get(unsafeComponentFor: parentId)
|
||||
let pcB: B = nexus.get(unsafeComponentFor: parentId)
|
||||
let ccA: A = nexus.get(unsafeComponentFor: childId)
|
||||
let ccB: B = nexus.get(unsafeComponentFor: childId)
|
||||
return (parent: (pcA, pcB), child: (ccA, ccB))
|
||||
}
|
||||
}
|
||||
|
||||
extension Nexus {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
//
|
||||
// Family3.swift
|
||||
//
|
||||
//
|
||||
//
|
||||
// Created by Christian Treffs on 21.08.19.
|
||||
//
|
||||
|
||||
// swiftlint:disable large_tuple
|
||||
|
||||
public typealias Family3<A: Component, B: Component, C: Component> = Family<Requires3<A, B, C>>
|
||||
|
||||
public struct Requires3<A, B, C>: FamilyRequirementsManaging where A: Component, B: Component, C: Component {
|
||||
public let componentTypes: [Component.Type]
|
||||
|
||||
public init(_ types: (A.Type, B.Type, C.Type)) {
|
||||
componentTypes = [A.self, B.self, C.self]
|
||||
}
|
||||
|
|
@ -27,6 +30,16 @@ public struct Requires3<A, B, C>: FamilyRequirementsManaging where A: Component,
|
|||
let compC: C = nexus.get(unsafeComponentFor: entityId)
|
||||
return (entity, compA, compB, compC)
|
||||
}
|
||||
public static func relativesDescending(nexus: Nexus, parentId: EntityIdentifier, childId: EntityIdentifier) ->
|
||||
(parent: (A, B, C), child: (A, B, C)) {
|
||||
let pcA: A = nexus.get(unsafeComponentFor: parentId)
|
||||
let pcB: B = nexus.get(unsafeComponentFor: parentId)
|
||||
let pcC: C = nexus.get(unsafeComponentFor: parentId)
|
||||
let ccA: A = nexus.get(unsafeComponentFor: childId)
|
||||
let ccB: B = nexus.get(unsafeComponentFor: childId)
|
||||
let ccC: C = nexus.get(unsafeComponentFor: childId)
|
||||
return (parent: (pcA, pcB, pcC), child: (ccA, ccB, ccC))
|
||||
}
|
||||
}
|
||||
|
||||
extension Nexus {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
//
|
||||
// Family4.swift
|
||||
//
|
||||
//
|
||||
//
|
||||
// Created by Christian Treffs on 21.08.19.
|
||||
//
|
||||
|
||||
// swiftlint:disable large_tuple
|
||||
|
||||
public typealias Family4<A: Component, B: Component, C: Component, D: Component> = Family<Requires4<A, B, C, D>>
|
||||
|
||||
public struct Requires4<A, B, C, D>: FamilyRequirementsManaging where A: Component, B: Component, C: Component, D: Component {
|
||||
public let componentTypes: [Component.Type]
|
||||
|
||||
public init(_ types: (A.Type, B.Type, C.Type, D.Type)) {
|
||||
componentTypes = [A.self, B.self, C.self, D.self]
|
||||
}
|
||||
|
|
@ -29,6 +32,19 @@ public struct Requires4<A, B, C, D>: FamilyRequirementsManaging where A: Compone
|
|||
let compD: D = nexus.get(unsafeComponentFor: entityId)
|
||||
return (entity, compA, compB, compC, compD)
|
||||
}
|
||||
|
||||
public static func relativesDescending(nexus: Nexus, parentId: EntityIdentifier, childId: EntityIdentifier) ->
|
||||
(parent: (A, B, C, D), child: (A, B, C, D)) {
|
||||
let pcA: A = nexus.get(unsafeComponentFor: parentId)
|
||||
let pcB: B = nexus.get(unsafeComponentFor: parentId)
|
||||
let pcC: C = nexus.get(unsafeComponentFor: parentId)
|
||||
let pcD: D = nexus.get(unsafeComponentFor: parentId)
|
||||
let ccA: A = nexus.get(unsafeComponentFor: childId)
|
||||
let ccB: B = nexus.get(unsafeComponentFor: childId)
|
||||
let ccC: C = nexus.get(unsafeComponentFor: childId)
|
||||
let ccD: D = nexus.get(unsafeComponentFor: childId)
|
||||
return (parent: (pcA, pcB, pcC, pcD), child: (ccA, ccB, ccC, ccD))
|
||||
}
|
||||
}
|
||||
|
||||
extension Nexus {
|
||||
|
|
@ -38,7 +54,7 @@ extension Nexus {
|
|||
_ componentC: C.Type,
|
||||
_ componentD: D.Type,
|
||||
excludesAll excludedComponents: Component.Type...
|
||||
) -> Family4<A, B, C, D> where A: Component, B: Component, C: Component, D: Component {
|
||||
) -> Family4<A, B, C, D> where A: Component, B: Component, C: Component, D: Component {
|
||||
return Family4(
|
||||
nexus: self,
|
||||
requiresAll: (componentA, componentB, componentC, componentD),
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
//
|
||||
// Family5.swift
|
||||
//
|
||||
//
|
||||
//
|
||||
// Created by Christian Treffs on 21.08.19.
|
||||
//
|
||||
|
||||
// swiftlint:disable large_tuple
|
||||
|
||||
public typealias Family5<A: Component, B: Component, C: Component, D: Component, E: Component> = Family<Requires5<A, B, C, D, E>>
|
||||
|
||||
public struct Requires5<A, B, C, D, E>: FamilyRequirementsManaging where A: Component, B: Component, C: Component, D: Component, E: Component {
|
||||
public let componentTypes: [Component.Type]
|
||||
|
||||
public init(_ types: (A.Type, B.Type, C.Type, D.Type, E.Type)) {
|
||||
componentTypes = [A.self, B.self, C.self, D.self, E.self]
|
||||
}
|
||||
|
|
@ -31,6 +34,22 @@ public struct Requires5<A, B, C, D, E>: FamilyRequirementsManaging where A: Comp
|
|||
let compE: E = nexus.get(unsafeComponentFor: entityId)
|
||||
return (entity, compA, compB, compC, compD, compE)
|
||||
}
|
||||
|
||||
public static func relativesDescending(nexus: Nexus, parentId: EntityIdentifier, childId: EntityIdentifier) ->
|
||||
(parent: (A, B, C, D, E), child: (A, B, C, D, E)) {
|
||||
let pcA: A = nexus.get(unsafeComponentFor: parentId)
|
||||
let pcB: B = nexus.get(unsafeComponentFor: parentId)
|
||||
let pcC: C = nexus.get(unsafeComponentFor: parentId)
|
||||
let pcD: D = nexus.get(unsafeComponentFor: parentId)
|
||||
let pcE: E = nexus.get(unsafeComponentFor: parentId)
|
||||
let ccA: A = nexus.get(unsafeComponentFor: childId)
|
||||
let ccB: B = nexus.get(unsafeComponentFor: childId)
|
||||
let ccC: C = nexus.get(unsafeComponentFor: childId)
|
||||
let ccD: D = nexus.get(unsafeComponentFor: childId)
|
||||
let ccE: E = nexus.get(unsafeComponentFor: childId)
|
||||
return (parent: (pcA, pcB, pcC, pcD, pcE),
|
||||
child: (ccA, ccB, ccC, ccD, ccE))
|
||||
}
|
||||
}
|
||||
|
||||
extension Nexus {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// FamilyRequirementsManaging.swift
|
||||
//
|
||||
//
|
||||
//
|
||||
// Created by Christian Treffs on 21.08.19.
|
||||
//
|
||||
|
|
@ -9,8 +9,13 @@ public protocol FamilyRequirementsManaging {
|
|||
associatedtype Components
|
||||
associatedtype ComponentTypes
|
||||
associatedtype EntityAndComponents
|
||||
associatedtype RelativesDescending
|
||||
|
||||
init(_ types: ComponentTypes)
|
||||
|
||||
var componentTypes: [Component.Type] { get }
|
||||
|
||||
static func components(nexus: Nexus, entityId: EntityIdentifier) -> Components
|
||||
static func entityAndComponents(nexus: Nexus, entityId: EntityIdentifier) -> EntityAndComponents
|
||||
static func relativesDescending(nexus: Nexus, parentId: EntityIdentifier, childId: EntityIdentifier) -> RelativesDescending
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,23 +5,23 @@
|
|||
// Created by Christian Treffs on 13.10.17.
|
||||
//
|
||||
|
||||
public extension Nexus {
|
||||
final var numComponents: Int {
|
||||
extension Nexus {
|
||||
public final var numComponents: Int {
|
||||
return componentsByType.reduce(0) { $0 + $1.value.count }
|
||||
}
|
||||
|
||||
final func has(componentId: ComponentIdentifier, entityId: EntityIdentifier) -> Bool {
|
||||
public final func has(componentId: ComponentIdentifier, entityId: EntityIdentifier) -> Bool {
|
||||
guard let uniforms = componentsByType[componentId] else {
|
||||
return false
|
||||
}
|
||||
return uniforms.contains(entityId.index)
|
||||
}
|
||||
|
||||
final func count(components entityId: EntityIdentifier) -> Int {
|
||||
public final func count(components entityId: EntityIdentifier) -> Int {
|
||||
return componentIdsByEntity[entityId]?.count ?? 0
|
||||
}
|
||||
|
||||
final func assign(component: Component, to entity: Entity) {
|
||||
public final func assign(component: Component, to entity: Entity) {
|
||||
let componentId: ComponentIdentifier = component.identifier
|
||||
let entityId: EntityIdentifier = entity.identifier
|
||||
|
||||
|
|
@ -49,39 +49,44 @@ public extension Nexus {
|
|||
delegate?.nexusEvent(ComponentAdded(component: componentId, toEntity: entity.identifier))
|
||||
}
|
||||
|
||||
final func assign<C>(component: C, to entity: Entity) where C: Component {
|
||||
public final func assign<C>(component: C, to entity: Entity) where C: Component {
|
||||
assign(component: component, to: entity)
|
||||
}
|
||||
|
||||
@inlinable final func get(component componentId: ComponentIdentifier, for entityId: EntityIdentifier) -> Component? {
|
||||
@inlinable
|
||||
public final func get(component componentId: ComponentIdentifier, for entityId: EntityIdentifier) -> Component? {
|
||||
guard let uniformComponents = componentsByType[componentId] else {
|
||||
return nil
|
||||
}
|
||||
return uniformComponents.get(at: entityId.index)
|
||||
}
|
||||
|
||||
@inlinable final func get(unsafeComponent componentId: ComponentIdentifier, for entityId: EntityIdentifier) -> Component {
|
||||
@inlinable
|
||||
public final func get(unsafeComponent componentId: ComponentIdentifier, for entityId: EntityIdentifier) -> Component {
|
||||
let uniformComponents = componentsByType[componentId].unsafelyUnwrapped
|
||||
return uniformComponents.get(unsafeAt: entityId.index)
|
||||
}
|
||||
|
||||
@inlinable final func get<C>(for entityId: EntityIdentifier) -> C? where C: Component {
|
||||
@inlinable
|
||||
public final func get<C>(for entityId: EntityIdentifier) -> C? where C: Component {
|
||||
let componentId: ComponentIdentifier = C.identifier
|
||||
return get(componentId: componentId, entityId: entityId)
|
||||
}
|
||||
|
||||
@inlinable final func get<C>(unsafeComponentFor entityId: EntityIdentifier) -> C where C: Component {
|
||||
@inlinable
|
||||
public final func get<C>(unsafeComponentFor entityId: EntityIdentifier) -> C where C: Component {
|
||||
let component: Component = get(unsafeComponent: C.identifier, for: entityId)
|
||||
/// components are guaranteed to be reference tyes so unsafeDowncast is applicable here
|
||||
return unsafeDowncast(component, to: C.self)
|
||||
}
|
||||
|
||||
@inlinable final func get(components entityId: EntityIdentifier) -> Set<ComponentIdentifier>? {
|
||||
@inlinable
|
||||
public final func get(components entityId: EntityIdentifier) -> Set<ComponentIdentifier>? {
|
||||
return componentIdsByEntity[entityId]
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
final func remove(component componentId: ComponentIdentifier, from entityId: EntityIdentifier) -> Bool {
|
||||
public final func remove(component componentId: ComponentIdentifier, from entityId: EntityIdentifier) -> Bool {
|
||||
// delete component instance
|
||||
componentsByType[componentId]?.remove(at: entityId.index)
|
||||
// unasign component from entity
|
||||
|
|
@ -94,7 +99,7 @@ public extension Nexus {
|
|||
}
|
||||
|
||||
@discardableResult
|
||||
final func removeAll(componentes entityId: EntityIdentifier) -> Bool {
|
||||
public final func removeAll(componentes entityId: EntityIdentifier) -> Bool {
|
||||
guard let allComponents = get(components: entityId) else {
|
||||
delegate?.nexusNonFatalError("clearing components form entity \(entityId) with no components")
|
||||
return false
|
||||
|
|
@ -106,10 +111,9 @@ public extension Nexus {
|
|||
}
|
||||
return removedAll
|
||||
}
|
||||
}
|
||||
|
||||
extension Nexus {
|
||||
@inlinable final func get<C>(componentId: ComponentIdentifier, entityId: EntityIdentifier) -> C? where C: Component {
|
||||
@inlinable
|
||||
public final func get<C>(componentId: ComponentIdentifier, entityId: EntityIdentifier) -> C? where C: Component {
|
||||
guard let uniformComponents = componentsByType[componentId] else {
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@
|
|||
//
|
||||
|
||||
extension Nexus {
|
||||
@inlinable internal func nextEntityId() -> EntityIdentifier {
|
||||
@inlinable
|
||||
internal func nextEntityId() -> EntityIdentifier {
|
||||
guard let nextReused: EntityIdentifier = freeEntities.popLast() else {
|
||||
return EntityIdentifier(UInt32(entityStorage.count))
|
||||
}
|
||||
|
|
@ -55,6 +56,8 @@ extension Nexus {
|
|||
return false
|
||||
}
|
||||
|
||||
removeAllChildren(from: entity)
|
||||
|
||||
if removeAll(componentes: entityId) {
|
||||
update(familyMembership: entityId)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,32 +5,32 @@
|
|||
// Created by Christian Treffs on 13.10.17.
|
||||
//
|
||||
|
||||
public extension Nexus {
|
||||
final var numFamilies: Int {
|
||||
extension Nexus {
|
||||
public final var numFamilies: Int {
|
||||
return familyMembersByTraits.keys.count
|
||||
}
|
||||
|
||||
func canBecomeMember(_ entity: Entity, in traits: FamilyTraitSet) -> Bool {
|
||||
guard let componentIds = componentIdsByEntity[entity.identifier] else {
|
||||
assertionFailure("no component set defined for entity: \(entity)")
|
||||
return false
|
||||
}
|
||||
return traits.isMatch(components: componentIds)
|
||||
}
|
||||
public func canBecomeMember(_ entity: Entity, in traits: FamilyTraitSet) -> Bool {
|
||||
guard let componentIds = componentIdsByEntity[entity.identifier] else {
|
||||
assertionFailure("no component set defined for entity: \(entity)")
|
||||
return false
|
||||
}
|
||||
return traits.isMatch(components: componentIds)
|
||||
}
|
||||
|
||||
func members(withFamilyTraits traits: FamilyTraitSet) -> UnorderedSparseSet<EntityIdentifier> {
|
||||
return familyMembersByTraits[traits] ?? UnorderedSparseSet<EntityIdentifier>()
|
||||
}
|
||||
public func members(withFamilyTraits traits: FamilyTraitSet) -> UnorderedSparseSet<EntityIdentifier> {
|
||||
return familyMembersByTraits[traits] ?? UnorderedSparseSet<EntityIdentifier>()
|
||||
}
|
||||
|
||||
func isMember(_ entity: Entity, in family: FamilyTraitSet) -> Bool {
|
||||
return isMember(entity.identifier, in: family)
|
||||
}
|
||||
public func isMember(_ entity: Entity, in family: FamilyTraitSet) -> Bool {
|
||||
return isMember(entity.identifier, in: family)
|
||||
}
|
||||
|
||||
func isMember(_ entityId: EntityIdentifier, in family: FamilyTraitSet) -> Bool {
|
||||
public func isMember(_ entityId: EntityIdentifier, in family: FamilyTraitSet) -> Bool {
|
||||
return isMember(entity: entityId, inFamilyWithTraits: family)
|
||||
}
|
||||
|
||||
func isMember(entity entityId: EntityIdentifier, inFamilyWithTraits traits: FamilyTraitSet) -> Bool {
|
||||
return members(withFamilyTraits: traits).contains(entityId.index)
|
||||
}
|
||||
public func isMember(entity entityId: EntityIdentifier, inFamilyWithTraits traits: FamilyTraitSet) -> Bool {
|
||||
return members(withFamilyTraits: traits).contains(entityId.index)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// Nexus+SceneGraph.swift
|
||||
//
|
||||
//
|
||||
// Created by Christian Treffs on 30.09.19.
|
||||
//
|
||||
|
||||
extension Nexus {
|
||||
public final func addChild(_ child: Entity, to parent: Entity) -> Bool {
|
||||
let inserted: Bool
|
||||
if parentChildrenMap[parent.identifier] == nil {
|
||||
parentChildrenMap[parent.identifier] = [child.identifier]
|
||||
inserted = true
|
||||
} else {
|
||||
let (isNewMember, _) = parentChildrenMap[parent.identifier]!.insert(child.identifier)
|
||||
inserted = isNewMember
|
||||
}
|
||||
if inserted {
|
||||
delegate?.nexusEvent(ChildAdded(parent: parent.identifier, child: child.identifier))
|
||||
}
|
||||
return inserted
|
||||
}
|
||||
|
||||
public final func removeChild(_ child: Entity, from parent: Entity) -> Bool {
|
||||
return removeChild(child.identifier, from: parent.identifier)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public final func removeChild(_ child: EntityIdentifier, from parent: EntityIdentifier) -> Bool {
|
||||
let removed: Bool = parentChildrenMap[parent]?.remove(child) != nil
|
||||
if removed {
|
||||
delegate?.nexusEvent(ChildRemoved(parent: parent, child: child))
|
||||
}
|
||||
return removed
|
||||
}
|
||||
|
||||
public final func removeAllChildren(from parent: Entity) {
|
||||
parentChildrenMap[parent.identifier]?.forEach { removeChild($0, from: parent.identifier) }
|
||||
return parentChildrenMap[parent.identifier] = nil
|
||||
}
|
||||
|
||||
public final func numChildren(for entity: Entity) -> Int {
|
||||
return parentChildrenMap[entity.identifier]?.count ?? 0
|
||||
}
|
||||
}
|
||||
|
|
@ -29,12 +29,17 @@ public final class Nexus {
|
|||
/// - Value: Tightly packed EntityIdentifiers that represent the association of an entity to the family.
|
||||
@usableFromInline final var familyMembersByTraits: [FamilyTraitSet: UnorderedSparseSet<EntityIdentifier>]
|
||||
|
||||
/// - Key: A parent entity id.
|
||||
/// - Value: Adjacency Set of all associated children.
|
||||
@usableFromInline final var parentChildrenMap: [EntityIdentifier: Set<EntityIdentifier>]
|
||||
|
||||
public init() {
|
||||
entityStorage = UnorderedSparseSet<Entity>()
|
||||
componentsByType = [:]
|
||||
componentIdsByEntity = [:]
|
||||
freeEntities = ContiguousArray<EntityIdentifier>()
|
||||
familyMembersByTraits = [:]
|
||||
parentChildrenMap = [:]
|
||||
}
|
||||
|
||||
public final func clear() {
|
||||
|
|
@ -55,6 +60,7 @@ public final class Nexus {
|
|||
componentsByType.removeAll()
|
||||
componentIdsByEntity.removeAll()
|
||||
familyMembersByTraits.removeAll()
|
||||
parentChildrenMap.removeAll()
|
||||
}
|
||||
|
||||
deinit {
|
||||
|
|
@ -64,12 +70,14 @@ public final class Nexus {
|
|||
|
||||
// MARK: - Equatable
|
||||
extension Nexus: Equatable {
|
||||
@inlinable public static func == (lhs: Nexus, rhs: Nexus) -> Bool {
|
||||
@inlinable
|
||||
public static func == (lhs: Nexus, rhs: Nexus) -> Bool {
|
||||
return lhs.entityStorage == rhs.entityStorage &&
|
||||
lhs.componentIdsByEntity == rhs.componentIdsByEntity &&
|
||||
lhs.freeEntities == rhs.freeEntities &&
|
||||
lhs.familyMembersByTraits == rhs.familyMembersByTraits &&
|
||||
lhs.componentsByType.keys == rhs.componentsByType.keys
|
||||
lhs.componentsByType.keys == rhs.componentsByType.keys &&
|
||||
lhs.parentChildrenMap == rhs.parentChildrenMap
|
||||
// NOTE: components are not equatable (yet)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// NexusEventDelegate.swift
|
||||
//
|
||||
//
|
||||
//
|
||||
// Created by Christian Treffs on 20.08.19.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -8,41 +8,51 @@
|
|||
public protocol NexusEvent {}
|
||||
|
||||
public struct EntityCreated: NexusEvent {
|
||||
public let entityId: EntityIdentifier
|
||||
public let entityId: EntityIdentifier
|
||||
}
|
||||
|
||||
public struct EntityDestroyed: NexusEvent {
|
||||
public let entityId: EntityIdentifier
|
||||
public let entityId: EntityIdentifier
|
||||
}
|
||||
|
||||
public struct ComponentAdded: NexusEvent {
|
||||
public let component: ComponentIdentifier
|
||||
public let toEntity: EntityIdentifier
|
||||
public let component: ComponentIdentifier
|
||||
public let toEntity: EntityIdentifier
|
||||
}
|
||||
|
||||
public struct ComponentUpdated: NexusEvent {
|
||||
public let atEnity: EntityIdentifier
|
||||
public let atEnity: EntityIdentifier
|
||||
}
|
||||
|
||||
public struct ComponentRemoved: NexusEvent {
|
||||
public let component: ComponentIdentifier
|
||||
public let from: EntityIdentifier
|
||||
public let component: ComponentIdentifier
|
||||
public let from: EntityIdentifier
|
||||
}
|
||||
|
||||
public struct FamilyMemberAdded: NexusEvent {
|
||||
public let member: EntityIdentifier
|
||||
public let toFamily: FamilyTraitSet
|
||||
public let member: EntityIdentifier
|
||||
public let toFamily: FamilyTraitSet
|
||||
}
|
||||
|
||||
public struct FamilyMemberRemoved: NexusEvent {
|
||||
public let member: EntityIdentifier
|
||||
public let from: FamilyTraitSet
|
||||
public let member: EntityIdentifier
|
||||
public let from: FamilyTraitSet
|
||||
}
|
||||
|
||||
public struct FamilyCreated: NexusEvent {
|
||||
public let family: FamilyTraitSet
|
||||
public let family: FamilyTraitSet
|
||||
}
|
||||
|
||||
public struct FamilyDestroyed: NexusEvent {
|
||||
public let family: FamilyTraitSet
|
||||
public let family: FamilyTraitSet
|
||||
}
|
||||
|
||||
public struct ChildAdded: NexusEvent {
|
||||
public let parent: EntityIdentifier
|
||||
public let child: EntityIdentifier
|
||||
}
|
||||
|
||||
public struct ChildRemoved: NexusEvent {
|
||||
public let parent: EntityIdentifier
|
||||
public let child: EntityIdentifier
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ public protocol SingleComponent: Component {
|
|||
init()
|
||||
}
|
||||
|
||||
public extension Nexus {
|
||||
func single<S>(_ component: S.Type) -> Single<S> where S: SingleComponent {
|
||||
extension Nexus {
|
||||
public func single<S>(_ component: S.Type) -> Single<S> where S: SingleComponent {
|
||||
let family = self.family(requires: S.self)
|
||||
precondition(family.count <= 1, "Singleton count of \(S.self) must be 0 or 1: \(family.count)")
|
||||
let entityId: EntityIdentifier
|
||||
|
|
@ -23,21 +23,23 @@ public extension Nexus {
|
|||
}
|
||||
}
|
||||
|
||||
public struct Single<A>: Equatable where A: SingleComponent {
|
||||
public struct Single<A> where A: SingleComponent {
|
||||
public let nexus: Nexus
|
||||
public let traits: FamilyTraitSet
|
||||
public let entityId: EntityIdentifier
|
||||
}
|
||||
|
||||
public extension Single where A: SingleComponent {
|
||||
@inlinable var component: A {
|
||||
extension Single: Equatable { }
|
||||
|
||||
extension Single where A: SingleComponent {
|
||||
@inlinable public var component: A {
|
||||
/// Since we guarantee that the component will always be present by managing the complete lifecycle of the entity
|
||||
/// and component assignment we may unsafelyUnwrap here.
|
||||
/// Since components will allways be of reference type (class) we may use unsafeDowncast here for performance reasons.
|
||||
return nexus.get(unsafeComponentFor: entityId)
|
||||
}
|
||||
|
||||
var entity: Entity {
|
||||
public var entity: Entity {
|
||||
return nexus.get(entity: entityId).unsafelyUnwrapped
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,33 +10,33 @@ import FirebladeECS
|
|||
class EmptyComponent: Component { }
|
||||
|
||||
class Name: Component {
|
||||
var name: String
|
||||
init(name: String) {
|
||||
self.name = name
|
||||
}
|
||||
var name: String
|
||||
init(name: String) {
|
||||
self.name = name
|
||||
}
|
||||
}
|
||||
|
||||
class Position: Component {
|
||||
var x: Int
|
||||
var y: Int
|
||||
init(x: Int, y: Int) {
|
||||
self.x = x
|
||||
self.y = y
|
||||
}
|
||||
var x: Int
|
||||
var y: Int
|
||||
init(x: Int, y: Int) {
|
||||
self.x = x
|
||||
self.y = y
|
||||
}
|
||||
}
|
||||
|
||||
class Velocity: Component {
|
||||
var a: Float
|
||||
init(a: Float) {
|
||||
self.a = a
|
||||
}
|
||||
var a: Float
|
||||
init(a: Float) {
|
||||
self.a = a
|
||||
}
|
||||
}
|
||||
|
||||
class Party: Component {
|
||||
var partying: Bool
|
||||
init(partying: Bool) {
|
||||
self.partying = partying
|
||||
}
|
||||
var partying: Bool
|
||||
init(partying: Bool) {
|
||||
self.partying = partying
|
||||
}
|
||||
}
|
||||
|
||||
class Color: Component {
|
||||
|
|
@ -46,26 +46,21 @@ class Color: Component {
|
|||
}
|
||||
|
||||
class ExampleSystem {
|
||||
private let family: Family2<Position, Velocity>
|
||||
private let family: Family2<Position, Velocity>
|
||||
|
||||
init(nexus: Nexus) {
|
||||
family = nexus.family(requiresAll: Position.self, Velocity.self, excludesAll: EmptyComponent.self)
|
||||
}
|
||||
init(nexus: Nexus) {
|
||||
family = nexus.family(requiresAll: Position.self, Velocity.self, excludesAll: EmptyComponent.self)
|
||||
}
|
||||
|
||||
func update(deltaT: Double) {
|
||||
func update(deltaT: Double) {
|
||||
family.forEach { (position: Position, velocity: Velocity) in
|
||||
position.x *= 2
|
||||
velocity.a *= 2
|
||||
|
||||
position.x *= 2
|
||||
velocity.a *= 2
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final class SingleGameState: SingleComponent {
|
||||
var shouldQuit: Bool = false
|
||||
var playerHealth: Int = 67
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import FirebladeECS
|
|||
import XCTest
|
||||
|
||||
class ComponentTests: XCTestCase {
|
||||
|
||||
func testMeasureStaticComponentIdentifier() {
|
||||
let number: Int = 10_000
|
||||
measure {
|
||||
|
|
@ -19,7 +18,7 @@ class ComponentTests: XCTestCase {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func testMeasureComponentIdentifier() {
|
||||
let number: Int = 10_000
|
||||
let pos = Position(x: 1, y: 2)
|
||||
|
|
|
|||
|
|
@ -5,16 +5,15 @@
|
|||
// Created by Christian Treffs on 14.02.19.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import FirebladeECS
|
||||
import XCTest
|
||||
|
||||
class HashingPerformanceTests: XCTestCase {
|
||||
|
||||
func testMeasureCombineHash() {
|
||||
let a: Set<Int> = Set<Int>([14_561_291, 26_451_562, 34_562_182, 488_972_556, 5_128_426_962, 68_211_812])
|
||||
let b: Set<Int> = Set<Int>([1_083_838, 912_312, 83_333, 71_234_555, 4_343_234])
|
||||
let c: Set<Int> = Set<Int>([3_410_346_899_765, 90_000_002, 12_212_321, 71, 6_123_345_676_543])
|
||||
|
||||
|
||||
let input: ContiguousArray<Int> = ContiguousArray<Int>(arrayLiteral: a.hashValue, b.hashValue, c.hashValue)
|
||||
measure {
|
||||
for _ in 0..<1_000_000 {
|
||||
|
|
@ -23,12 +22,12 @@ class HashingPerformanceTests: XCTestCase {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func testMeasureSetOfSetHash() {
|
||||
let a: Set<Int> = Set<Int>([14_561_291, 26_451_562, 34_562_182, 488_972_556, 5_128_426_962, 68_211_812])
|
||||
let b: Set<Int> = Set<Int>([1_083_838, 912_312, 83_333, 71_234_555, 4_343_234])
|
||||
let c: Set<Int> = Set<Int>([3_410_346_899_765, 90_000_002, 12_212_321, 71, 6_123_345_676_543])
|
||||
|
||||
|
||||
let input = Set<Set<Int>>(arrayLiteral: a, b, c)
|
||||
measure {
|
||||
for _ in 0..<1_000_000 {
|
||||
|
|
@ -37,6 +36,4 @@ class HashingPerformanceTests: XCTestCase {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,39 +9,37 @@ import FirebladeECS
|
|||
import XCTest
|
||||
|
||||
class TypedFamilyPerformanceTests: XCTestCase {
|
||||
|
||||
let numEntities: Int = 10_000
|
||||
var nexus: Nexus!
|
||||
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
nexus = Nexus()
|
||||
|
||||
|
||||
for i in 0..<numEntities {
|
||||
nexus.createEntity().assign(Position(x: 1 + i, y: 2 + i),
|
||||
Name(name: "myName\(i)"),
|
||||
Velocity(a: 3.14),
|
||||
EmptyComponent(),
|
||||
Color())
|
||||
Name(name: "myName\(i)"),
|
||||
Velocity(a: 3.14),
|
||||
EmptyComponent(),
|
||||
Color())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override func tearDown() {
|
||||
nexus = nil
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
|
||||
func testMeasureTraitMatching() {
|
||||
|
||||
let a = nexus.createEntity()
|
||||
a.assign(Position(x: 1, y: 2))
|
||||
a.assign(Name(name: "myName"))
|
||||
a.assign(Velocity(a: 3.14))
|
||||
a.assign(EmptyComponent())
|
||||
|
||||
|
||||
let isMatch = nexus.family(requiresAll: Position.self, Velocity.self,
|
||||
excludesAll: Party.self)
|
||||
|
||||
|
||||
measure {
|
||||
for _ in 0..<10_000 {
|
||||
let success = isMatch.canBecomeMember(a)
|
||||
|
|
@ -49,35 +47,35 @@ class TypedFamilyPerformanceTests: XCTestCase {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func testPerformanceTypedFamilyEntities() {
|
||||
let family = nexus.family(requires: Position.self, excludesAll: Party.self)
|
||||
|
||||
|
||||
XCTAssertEqual(family.count, numEntities)
|
||||
XCTAssertEqual(nexus.numEntities, numEntities)
|
||||
XCTAssertEqual(nexus.numFamilies, 1)
|
||||
XCTAssertEqual(nexus.numComponents, numEntities * 5)
|
||||
|
||||
|
||||
var loopCount: Int = 0
|
||||
|
||||
|
||||
measure {
|
||||
family
|
||||
.entities
|
||||
.forEach { (entity: Entity) in
|
||||
_ = entity
|
||||
|
||||
|
||||
loopCount += 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
XCTAssertEqual(loopCount, family.count * 10)
|
||||
}
|
||||
|
||||
|
||||
func testPerformanceArray() {
|
||||
let positions = [Position](repeating: Position(x: Int.random(in: 0...10), y: Int.random(in: 0...10)), count: numEntities)
|
||||
|
||||
|
||||
var loopCount: Int = 0
|
||||
|
||||
|
||||
measure {
|
||||
positions
|
||||
.forEach { (position: Position) in
|
||||
|
|
@ -85,89 +83,89 @@ class TypedFamilyPerformanceTests: XCTestCase {
|
|||
loopCount += 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
XCTAssertEqual(loopCount, numEntities * 10)
|
||||
}
|
||||
|
||||
|
||||
func testPerformanceTypedFamilyOneComponent() {
|
||||
let family = nexus.family(requires: Position.self, excludesAll: Party.self)
|
||||
|
||||
|
||||
XCTAssertEqual(family.count, numEntities)
|
||||
XCTAssertEqual(nexus.numEntities, numEntities)
|
||||
XCTAssertEqual(nexus.numFamilies, 1)
|
||||
XCTAssertEqual(nexus.numComponents, numEntities * 5)
|
||||
|
||||
|
||||
var loopCount: Int = 0
|
||||
|
||||
|
||||
measure {
|
||||
family
|
||||
.forEach { (position: Position) in
|
||||
_ = position
|
||||
|
||||
|
||||
loopCount += 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
XCTAssertEqual(loopCount, family.count * 10)
|
||||
}
|
||||
|
||||
|
||||
func testPerformanceTypedFamilyEntityOneComponent() {
|
||||
let family = nexus.family(requires: Position.self, excludesAll: Party.self)
|
||||
|
||||
|
||||
XCTAssertEqual(family.count, numEntities)
|
||||
XCTAssertEqual(nexus.numEntities, numEntities)
|
||||
XCTAssertEqual(nexus.numFamilies, 1)
|
||||
XCTAssertEqual(nexus.numComponents, numEntities * 5)
|
||||
|
||||
|
||||
var loopCount: Int = 0
|
||||
|
||||
|
||||
measure {
|
||||
family
|
||||
.entityAndComponents
|
||||
.forEach { (entity: Entity, position: Position) in
|
||||
_ = entity
|
||||
_ = position
|
||||
|
||||
|
||||
loopCount += 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
XCTAssertEqual(loopCount, family.count * 10)
|
||||
}
|
||||
|
||||
|
||||
func testPerformanceTypedFamilyTwoComponents() {
|
||||
let family = nexus.family(requiresAll: Position.self, Velocity.self, excludesAll: Party.self)
|
||||
|
||||
|
||||
XCTAssertEqual(family.count, numEntities)
|
||||
XCTAssertEqual(nexus.numEntities, numEntities)
|
||||
XCTAssertEqual(nexus.numFamilies, 1)
|
||||
XCTAssertEqual(nexus.numComponents, numEntities * 5)
|
||||
|
||||
|
||||
var loopCount: Int = 0
|
||||
|
||||
|
||||
measure {
|
||||
family
|
||||
.forEach { (position: Position, velocity: Velocity) in
|
||||
_ = position
|
||||
_ = velocity
|
||||
|
||||
|
||||
loopCount += 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
XCTAssertEqual(loopCount, family.count * 10)
|
||||
}
|
||||
|
||||
|
||||
func testPerformanceTypedFamilyEntityTwoComponents() {
|
||||
let family = nexus.family(requiresAll: Position.self, Velocity.self, excludesAll: Party.self)
|
||||
|
||||
|
||||
XCTAssertEqual(family.count, numEntities)
|
||||
XCTAssertEqual(nexus.numEntities, numEntities)
|
||||
XCTAssertEqual(nexus.numFamilies, 1)
|
||||
XCTAssertEqual(nexus.numComponents, numEntities * 5)
|
||||
|
||||
|
||||
var loopCount: Int = 0
|
||||
|
||||
|
||||
measure {
|
||||
family
|
||||
.entityAndComponents
|
||||
|
|
@ -175,48 +173,48 @@ class TypedFamilyPerformanceTests: XCTestCase {
|
|||
_ = entity
|
||||
_ = position
|
||||
_ = velocity
|
||||
|
||||
|
||||
loopCount += 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
XCTAssertEqual(loopCount, family.count * 10)
|
||||
}
|
||||
|
||||
|
||||
func testPerformanceTypedFamilyThreeComponents() {
|
||||
let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, excludesAll: Party.self)
|
||||
|
||||
|
||||
XCTAssertEqual(family.count, numEntities)
|
||||
XCTAssertEqual(nexus.numEntities, numEntities)
|
||||
XCTAssertEqual(nexus.numFamilies, 1)
|
||||
XCTAssertEqual(nexus.numComponents, numEntities * 5)
|
||||
|
||||
|
||||
var loopCount: Int = 0
|
||||
|
||||
|
||||
measure {
|
||||
family
|
||||
.forEach { (position: Position, velocity: Velocity, name: Name) in
|
||||
_ = position
|
||||
_ = velocity
|
||||
_ = name
|
||||
|
||||
|
||||
loopCount += 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
XCTAssertEqual(loopCount, family.count * 10)
|
||||
}
|
||||
|
||||
|
||||
func testPerformanceTypedFamilyEntityThreeComponents() {
|
||||
let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, excludesAll: Party.self)
|
||||
|
||||
|
||||
XCTAssertEqual(family.count, numEntities)
|
||||
XCTAssertEqual(nexus.numEntities, numEntities)
|
||||
XCTAssertEqual(nexus.numFamilies, 1)
|
||||
XCTAssertEqual(nexus.numComponents, numEntities * 5)
|
||||
|
||||
|
||||
var loopCount: Int = 0
|
||||
|
||||
|
||||
measure {
|
||||
family
|
||||
.entityAndComponents
|
||||
|
|
@ -225,24 +223,24 @@ class TypedFamilyPerformanceTests: XCTestCase {
|
|||
_ = position
|
||||
_ = velocity
|
||||
_ = name
|
||||
|
||||
|
||||
loopCount += 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
XCTAssertEqual(loopCount, family.count * 10)
|
||||
}
|
||||
|
||||
|
||||
func testPerformanceTypedFamilyFourComponents() {
|
||||
let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, Color.self, excludesAll: Party.self)
|
||||
|
||||
|
||||
XCTAssertEqual(family.count, numEntities)
|
||||
XCTAssertEqual(nexus.numEntities, numEntities)
|
||||
XCTAssertEqual(nexus.numFamilies, 1)
|
||||
XCTAssertEqual(nexus.numComponents, numEntities * 5)
|
||||
|
||||
|
||||
var loopCount: Int = 0
|
||||
|
||||
|
||||
measure {
|
||||
family
|
||||
.forEach { (position: Position, velocity: Velocity, name: Name, color: Color) in
|
||||
|
|
@ -250,24 +248,24 @@ class TypedFamilyPerformanceTests: XCTestCase {
|
|||
_ = velocity
|
||||
_ = name
|
||||
_ = color
|
||||
|
||||
|
||||
loopCount += 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
XCTAssertEqual(loopCount, family.count * 10)
|
||||
}
|
||||
|
||||
|
||||
func testPerformanceTypedFamilyEntityFourComponents() {
|
||||
let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, Color.self, excludesAll: Party.self)
|
||||
|
||||
|
||||
XCTAssertEqual(family.count, numEntities)
|
||||
XCTAssertEqual(nexus.numEntities, numEntities)
|
||||
XCTAssertEqual(nexus.numFamilies, 1)
|
||||
XCTAssertEqual(nexus.numComponents, numEntities * 5)
|
||||
|
||||
|
||||
var loopCount: Int = 0
|
||||
|
||||
|
||||
measure {
|
||||
family
|
||||
.entityAndComponents
|
||||
|
|
@ -277,24 +275,24 @@ class TypedFamilyPerformanceTests: XCTestCase {
|
|||
_ = velocity
|
||||
_ = name
|
||||
_ = color
|
||||
|
||||
|
||||
loopCount += 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
XCTAssertEqual(loopCount, family.count * 10)
|
||||
}
|
||||
|
||||
|
||||
func testPerformanceTypedFamilyFiveComponents() {
|
||||
let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, Color.self, EmptyComponent.self, excludesAll: Party.self)
|
||||
|
||||
|
||||
XCTAssertEqual(family.count, numEntities)
|
||||
XCTAssertEqual(nexus.numEntities, numEntities)
|
||||
XCTAssertEqual(nexus.numFamilies, 1)
|
||||
XCTAssertEqual(nexus.numComponents, numEntities * 5)
|
||||
|
||||
|
||||
var loopCount: Int = 0
|
||||
|
||||
|
||||
measure {
|
||||
family.forEach { (position: Position, velocity: Velocity, name: Name, color: Color, empty: EmptyComponent) in
|
||||
_ = position
|
||||
|
|
@ -302,24 +300,24 @@ class TypedFamilyPerformanceTests: XCTestCase {
|
|||
_ = name
|
||||
_ = color
|
||||
_ = empty
|
||||
|
||||
|
||||
loopCount += 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
XCTAssertEqual(loopCount, family.count * 10)
|
||||
}
|
||||
|
||||
|
||||
func testPerformanceTypedFamilyEntityFiveComponents() {
|
||||
let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, Color.self, EmptyComponent.self, excludesAll: Party.self)
|
||||
|
||||
|
||||
XCTAssertEqual(family.count, numEntities)
|
||||
XCTAssertEqual(nexus.numEntities, numEntities)
|
||||
XCTAssertEqual(nexus.numFamilies, 1)
|
||||
XCTAssertEqual(nexus.numComponents, numEntities * 5)
|
||||
|
||||
|
||||
var loopCount: Int = 0
|
||||
|
||||
|
||||
measure {
|
||||
family
|
||||
.entityAndComponents
|
||||
|
|
@ -330,11 +328,11 @@ class TypedFamilyPerformanceTests: XCTestCase {
|
|||
_ = name
|
||||
_ = color
|
||||
_ = empty
|
||||
|
||||
|
||||
loopCount += 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
XCTAssertEqual(loopCount, family.count * 10)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ extension ComponentTests {
|
|||
// to regenerate.
|
||||
static let __allTests__ComponentTests = [
|
||||
("testMeasureComponentIdentifier", testMeasureComponentIdentifier),
|
||||
("testMeasureStaticComponentIdentifier", testMeasureStaticComponentIdentifier),
|
||||
("testMeasureStaticComponentIdentifier", testMeasureStaticComponentIdentifier)
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -17,7 +17,7 @@ extension HashingPerformanceTests {
|
|||
// to regenerate.
|
||||
static let __allTests__HashingPerformanceTests = [
|
||||
("testMeasureCombineHash", testMeasureCombineHash),
|
||||
("testMeasureSetOfSetHash", testMeasureSetOfSetHash),
|
||||
("testMeasureSetOfSetHash", testMeasureSetOfSetHash)
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -38,7 +38,7 @@ extension TypedFamilyPerformanceTests {
|
|||
("testPerformanceTypedFamilyFourComponents", testPerformanceTypedFamilyFourComponents),
|
||||
("testPerformanceTypedFamilyOneComponent", testPerformanceTypedFamilyOneComponent),
|
||||
("testPerformanceTypedFamilyThreeComponents", testPerformanceTypedFamilyThreeComponents),
|
||||
("testPerformanceTypedFamilyTwoComponents", testPerformanceTypedFamilyTwoComponents),
|
||||
("testPerformanceTypedFamilyTwoComponents", testPerformanceTypedFamilyTwoComponents)
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -46,7 +46,7 @@ public func __allTests() -> [XCTestCaseEntry] {
|
|||
return [
|
||||
testCase(ComponentTests.__allTests__ComponentTests),
|
||||
testCase(HashingPerformanceTests.__allTests__HashingPerformanceTests),
|
||||
testCase(TypedFamilyPerformanceTests.__allTests__TypedFamilyPerformanceTests),
|
||||
testCase(TypedFamilyPerformanceTests.__allTests__TypedFamilyPerformanceTests)
|
||||
]
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,45 +1,41 @@
|
|||
//
|
||||
// AccessTests.swift
|
||||
//
|
||||
//
|
||||
//
|
||||
// Created by Christian Treffs on 25.06.19.
|
||||
//
|
||||
|
||||
|
||||
|
||||
import FirebladeECS
|
||||
import XCTest
|
||||
|
||||
#if swift(>=5.1)
|
||||
class AccessTests: XCTestCase {
|
||||
|
||||
func testReadOnly() {
|
||||
let pos = Position(x: 1, y: 2)
|
||||
|
||||
|
||||
let readable = ReadableOnly<Position>(pos)
|
||||
|
||||
|
||||
XCTAssertEqual(readable.x, 1)
|
||||
XCTAssertEqual(readable.y, 2)
|
||||
|
||||
|
||||
// readable.x = 3 // does not work and that's correct!
|
||||
}
|
||||
|
||||
|
||||
func testWrite() {
|
||||
let pos = Position(x: 1, y: 2)
|
||||
|
||||
|
||||
let writable = Writable<Position>(pos)
|
||||
|
||||
|
||||
XCTAssertEqual(writable.x, 1)
|
||||
XCTAssertEqual(writable.y, 2)
|
||||
|
||||
|
||||
writable.x = 3
|
||||
|
||||
|
||||
XCTAssertEqual(writable.x, 3)
|
||||
XCTAssertEqual(pos.x, 3)
|
||||
|
||||
XCTAssertEqual(writable.y, 2)
|
||||
XCTAssertEqual(pos.y, 2)
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -45,42 +45,42 @@ class Color: Component {
|
|||
var b: UInt8 = 0
|
||||
}
|
||||
|
||||
class Index: Component {
|
||||
var index: Int
|
||||
|
||||
init(index: Int) {
|
||||
self.index = index
|
||||
}
|
||||
}
|
||||
|
||||
final class SingleGameState: SingleComponent {
|
||||
var shouldQuit: Bool = false
|
||||
var playerHealth: Int = 67
|
||||
|
||||
}
|
||||
|
||||
|
||||
class ExampleSystem {
|
||||
private let family: Family2<Position, Velocity>
|
||||
|
||||
|
||||
init(nexus: Nexus) {
|
||||
family = nexus.family(requiresAll: Position.self, Velocity.self, excludesAll: EmptyComponent.self)
|
||||
}
|
||||
|
||||
|
||||
func update(deltaT: Double) {
|
||||
family.forEach { (position: Position, velocity: Velocity) in
|
||||
position.x *= 2
|
||||
velocity.a *= 2
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class ColorSystem {
|
||||
|
||||
let nexus: Nexus
|
||||
lazy var colors = nexus.family(requires: Color.self)
|
||||
|
||||
|
||||
init(nexus: Nexus) {
|
||||
self.nexus = nexus
|
||||
}
|
||||
|
||||
|
||||
func update() {
|
||||
colors
|
||||
.forEach { (color: Color) in
|
||||
|
|
@ -93,30 +93,27 @@ class ColorSystem {
|
|||
|
||||
class PositionSystem {
|
||||
let positions: Family1<Position>
|
||||
|
||||
|
||||
var velocity: Double = 4.0
|
||||
|
||||
|
||||
init(nexus: Nexus) {
|
||||
positions = nexus.family(requires: Position.self)
|
||||
}
|
||||
|
||||
|
||||
func randNorm() -> Double {
|
||||
return 4.0
|
||||
}
|
||||
|
||||
|
||||
func update() {
|
||||
positions
|
||||
.forEach { [unowned self](pos: Position) in
|
||||
|
||||
let deltaX: Double = self.velocity * ((self.randNorm() * 2) - 1)
|
||||
let deltaY: Double = self.velocity * ((self.randNorm() * 2) - 1)
|
||||
let x = pos.x + Int(deltaX)
|
||||
let y = pos.y + Int(deltaY)
|
||||
|
||||
|
||||
pos.x = x
|
||||
pos.y = y
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,19 +9,15 @@ import FirebladeECS
|
|||
import XCTest
|
||||
|
||||
class ComponentTests: XCTestCase {
|
||||
|
||||
func testComponentIdentifier() {
|
||||
XCTAssertEqual(Position.identifier, Position.identifier)
|
||||
XCTAssertEqual(Velocity.identifier, Velocity.identifier)
|
||||
func testComponentIdentifier() {
|
||||
XCTAssertEqual(Position.identifier, Position.identifier)
|
||||
XCTAssertEqual(Velocity.identifier, Velocity.identifier)
|
||||
XCTAssertNotEqual(Velocity.identifier, Position.identifier)
|
||||
|
||||
|
||||
let p1 = Position(x: 1, y: 2)
|
||||
let v1 = Velocity(a: 3.14)
|
||||
XCTAssertEqual(p1.identifier, Position.identifier)
|
||||
XCTAssertEqual(v1.identifier, Velocity.identifier)
|
||||
XCTAssertNotEqual(v1.identifier, p1.identifier)
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,20 +9,21 @@ import FirebladeECS
|
|||
import XCTest
|
||||
|
||||
class EntityTests: XCTestCase {
|
||||
|
||||
func testEntityIdentifierAndIndex() {
|
||||
|
||||
let min = EntityIdentifier(.min)
|
||||
XCTAssertEqual(min.index, Int(UInt32.min))
|
||||
func testEntityIdentifierAndIndex() {
|
||||
let min = EntityIdentifier(.min)
|
||||
XCTAssertEqual(min.index, Int(UInt32.min))
|
||||
|
||||
let uRand = UInt32.random(in: UInt32.min...UInt32.max)
|
||||
let rand = EntityIdentifier(uRand)
|
||||
XCTAssertEqual(rand.index, Int(uRand))
|
||||
let rand = EntityIdentifier(uRand)
|
||||
XCTAssertEqual(rand.index, Int(uRand))
|
||||
|
||||
let max = EntityIdentifier(.max)
|
||||
XCTAssertEqual(max, EntityIdentifier.invalid)
|
||||
let max = EntityIdentifier(.max)
|
||||
XCTAssertEqual(max, EntityIdentifier.invalid)
|
||||
XCTAssertEqual(max.index, Int(UInt32.max))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func testEntityIdentifierComparison() {
|
||||
XCTAssertTrue(EntityIdentifier(1) < EntityIdentifier(2))
|
||||
XCTAssertTrue(EntityIdentifier(23) > EntityIdentifier(4))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,54 +9,51 @@
|
|||
import XCTest
|
||||
|
||||
class FamilyTests: XCTestCase {
|
||||
|
||||
var nexus: Nexus!
|
||||
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
nexus = Nexus()
|
||||
}
|
||||
|
||||
|
||||
override func tearDown() {
|
||||
nexus = nil
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
|
||||
func createDefaultEntity() {
|
||||
let e = nexus.createEntity()
|
||||
e.assign(Position(x: 1, y: 2))
|
||||
e.assign(Color())
|
||||
}
|
||||
|
||||
|
||||
func testFamilyCreation() {
|
||||
|
||||
let family = nexus.family(requires: Position.self,
|
||||
excludesAll: Name.self)
|
||||
|
||||
|
||||
XCTAssertEqual(family.nexus, self.nexus)
|
||||
XCTAssertTrue(family.nexus === self.nexus)
|
||||
XCTAssertEqual(nexus.numFamilies, 1)
|
||||
XCTAssertEqual(nexus.numComponents, 0)
|
||||
XCTAssertEqual(nexus.numEntities, 0)
|
||||
|
||||
|
||||
let traits = FamilyTraitSet(requiresAll: [Position.self], excludesAll: [Name.self])
|
||||
XCTAssertEqual(family.traits, traits)
|
||||
}
|
||||
|
||||
|
||||
func testFamilyReuse() {
|
||||
|
||||
let familyA = nexus.family(requires: Position.self,
|
||||
excludesAll: Name.self)
|
||||
|
||||
|
||||
let familyB = nexus.family(requires: Position.self,
|
||||
excludesAll: Name.self)
|
||||
|
||||
|
||||
XCTAssertEqual(nexus.numFamilies, 1)
|
||||
XCTAssertEqual(nexus.numComponents, 0)
|
||||
|
||||
|
||||
XCTAssertEqual(familyA, familyB)
|
||||
}
|
||||
|
||||
|
||||
func testFamilyAbandoned() {
|
||||
XCTAssertEqual(nexus.numFamilies, 0)
|
||||
XCTAssertEqual(nexus.numComponents, 0)
|
||||
|
|
@ -83,9 +80,8 @@ class FamilyTests: XCTestCase {
|
|||
XCTAssertEqual(nexus.numFamilies, 1)
|
||||
XCTAssertEqual(nexus.numComponents, 0)
|
||||
XCTAssertEqual(nexus.numEntities, 0)
|
||||
|
||||
}
|
||||
|
||||
|
||||
func testFamilyLateMember() {
|
||||
let eEarly = nexus.createEntity(with: Position(x: 1, y: 2))
|
||||
XCTAssertEqual(nexus.numFamilies, 0)
|
||||
|
|
@ -102,95 +98,91 @@ class FamilyTests: XCTestCase {
|
|||
XCTAssertTrue(family.isMember(eEarly))
|
||||
XCTAssertTrue(family.isMember(eLate))
|
||||
}
|
||||
|
||||
|
||||
func testFamilyExchange() {
|
||||
let number: Int = 10
|
||||
|
||||
|
||||
for i in 0..<number {
|
||||
nexus.createEntity(with: Position(x: i + 1, y: i + 2))
|
||||
}
|
||||
|
||||
|
||||
let familyA = nexus.family(requires: Position.self,
|
||||
excludesAll: Velocity.self)
|
||||
|
||||
|
||||
let familyB = nexus.family(requires: Velocity.self,
|
||||
excludesAll: Position.self)
|
||||
|
||||
|
||||
XCTAssertEqual(familyA.count, 10)
|
||||
XCTAssertEqual(familyB.count, 0)
|
||||
|
||||
|
||||
familyA
|
||||
.entityAndComponents
|
||||
.forEach { (entity: Entity, position: Position) in
|
||||
.forEach { (entity: Entity, _: Position) in
|
||||
entity.assign(Velocity(a: 3.14))
|
||||
entity.remove(Position.self)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
XCTAssertEqual(familyA.count, 0)
|
||||
XCTAssertEqual(familyB.count, 10)
|
||||
|
||||
|
||||
familyB
|
||||
.entityAndComponents
|
||||
.forEach { (entity: Entity, velocity: Velocity) in
|
||||
entity.assign(Position(x: 1, y: 2))
|
||||
entity.remove(velocity)
|
||||
entity.assign(Position(x: 1, y: 2))
|
||||
entity.remove(velocity)
|
||||
}
|
||||
|
||||
|
||||
XCTAssertEqual(familyA.count, 10)
|
||||
XCTAssertEqual(familyB.count, 0)
|
||||
|
||||
}
|
||||
|
||||
|
||||
func testFamilyMemberBasicIteration() {
|
||||
|
||||
for i in 0..<1000 {
|
||||
nexus.createEntity(with: Position(x: i + 1, y: i + 2))
|
||||
nexus.createEntity(with: Velocity(a: Float(i)))
|
||||
}
|
||||
|
||||
|
||||
let familyA = nexus.family(requires: Position.self,
|
||||
excludesAll: Velocity.self)
|
||||
|
||||
|
||||
let familyB = nexus.family(requires: Velocity.self,
|
||||
excludesAll: Position.self)
|
||||
|
||||
|
||||
familyA.forEach { (pos: Position?) in
|
||||
XCTAssertNotNil(pos)
|
||||
}
|
||||
|
||||
|
||||
familyB.forEach { (vel: Velocity?) in
|
||||
XCTAssertNotNil(vel)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func testFamilyBulkDestroy() {
|
||||
let count = 10_000
|
||||
|
||||
|
||||
for _ in 0..<count {
|
||||
createDefaultEntity()
|
||||
}
|
||||
|
||||
|
||||
let family = nexus.family(requires: Position.self)
|
||||
|
||||
|
||||
XCTAssertEqual(family.memberIds.count, count)
|
||||
|
||||
|
||||
let currentCount: Int = (count / 2)
|
||||
|
||||
|
||||
family
|
||||
.entities
|
||||
.prefix(currentCount)
|
||||
.forEach { (entity: Entity) in
|
||||
entity.destroy()
|
||||
|
||||
}
|
||||
|
||||
|
||||
XCTAssertEqual(family.memberIds.count, (count / 2))
|
||||
|
||||
|
||||
for _ in 0..<count {
|
||||
createDefaultEntity()
|
||||
}
|
||||
|
||||
|
||||
XCTAssertEqual(family.memberIds.count, count + (count / 2))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import FirebladeECS
|
|||
import XCTest
|
||||
|
||||
class FamilyTraitsTests: XCTestCase {
|
||||
|
||||
var nexus: Nexus!
|
||||
|
||||
override func setUp() {
|
||||
|
|
@ -23,7 +22,6 @@ class FamilyTraitsTests: XCTestCase {
|
|||
}
|
||||
|
||||
func testTraitCommutativity() {
|
||||
|
||||
let t1 = FamilyTraitSet(requiresAll: [Position.self, Velocity.self],
|
||||
excludesAll: [Name.self])
|
||||
let t2 = FamilyTraitSet(requiresAll: [Velocity.self, Position.self],
|
||||
|
|
@ -31,11 +29,9 @@ class FamilyTraitsTests: XCTestCase {
|
|||
|
||||
XCTAssertEqual(t1, t2)
|
||||
XCTAssertEqual(t1.hashValue, t2.hashValue)
|
||||
|
||||
}
|
||||
|
||||
func testTraitMatching() {
|
||||
|
||||
let a = nexus.createEntity()
|
||||
a.assign(Position(x: 1, y: 2))
|
||||
a.assign(Name(name: "myName"))
|
||||
|
|
@ -49,7 +45,5 @@ class FamilyTraitsTests: XCTestCase {
|
|||
|
||||
XCTAssertFalse(noMatch.canBecomeMember(a))
|
||||
XCTAssertTrue(isMatch.canBecomeMember(a))
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
import XCTest
|
||||
|
||||
class HashingTests: XCTestCase {
|
||||
|
||||
func makeComponent() -> Int {
|
||||
let upperBound: Int = 44
|
||||
let range = UInt32.min...UInt32.max
|
||||
|
|
@ -23,35 +22,32 @@ class HashingTests: XCTestCase {
|
|||
let cH = Int(bitPattern: rand)
|
||||
return cH
|
||||
}
|
||||
|
||||
|
||||
func testCollisionsInCritialRange() {
|
||||
|
||||
var hashSet: Set<Int> = Set<Int>()
|
||||
|
||||
|
||||
var range: CountableRange<UInt32> = 0 ..< 1_000_000
|
||||
|
||||
|
||||
let maxComponents: Int = 1000
|
||||
let components: [Int] = (0..<maxComponents).map { _ in makeComponent() }
|
||||
|
||||
|
||||
var index: Int = 0
|
||||
while let idx: UInt32 = range.popLast() {
|
||||
let eId = EntityIdentifier(idx)
|
||||
|
||||
|
||||
let entityId: EntityIdentifier = eId
|
||||
let c = (index % maxComponents)
|
||||
index += 1
|
||||
|
||||
|
||||
let cH: ComponentTypeHash = components[c]
|
||||
|
||||
|
||||
let h: Int = EntityComponentHash.compose(entityId: entityId, componentTypeHash: cH)
|
||||
|
||||
|
||||
let (collisionFree, _) = hashSet.insert(h)
|
||||
XCTAssert(collisionFree)
|
||||
|
||||
|
||||
XCTAssert(EntityComponentHash.decompose(h, with: cH) == entityId)
|
||||
XCTAssert(EntityComponentHash.decompose(h, with: entityId) == cH)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,25 +9,24 @@ import FirebladeECS
|
|||
import XCTest
|
||||
|
||||
class NexusTests: XCTestCase {
|
||||
|
||||
var nexus: Nexus!
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
nexus = Nexus()
|
||||
}
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
override func tearDown() {
|
||||
nexus = nil
|
||||
super.tearDown()
|
||||
}
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
func testEntityCreate() {
|
||||
func testEntityCreate() {
|
||||
XCTAssertEqual(nexus.numEntities, 0)
|
||||
|
||||
let e0 = nexus.createEntity()
|
||||
let e0 = nexus.createEntity()
|
||||
|
||||
XCTAssertEqual(e0.identifier.index, 0)
|
||||
XCTAssertEqual(e0.identifier.index, 0)
|
||||
XCTAssertEqual(nexus.numEntities, 1)
|
||||
|
||||
let e1 = nexus.createEntity(with: Name(name: "Entity 1"))
|
||||
|
|
@ -37,7 +36,7 @@ class NexusTests: XCTestCase {
|
|||
|
||||
//FIXME: XCTAssertNil(e0.name)
|
||||
//FIXME: XCTAssertEqual(e1.name, "Entity 1")
|
||||
}
|
||||
}
|
||||
|
||||
func testEntityDestroy() {
|
||||
testEntityCreate()
|
||||
|
|
@ -61,108 +60,101 @@ class NexusTests: XCTestCase {
|
|||
XCTAssertEqual(nexus.numEntities, 0)
|
||||
}
|
||||
|
||||
func testComponentCreation() {
|
||||
func testComponentCreation() {
|
||||
XCTAssert(nexus.numEntities == 0)
|
||||
|
||||
XCTAssert(nexus.numEntities == 0)
|
||||
let e0: Entity = nexus.createEntity()
|
||||
|
||||
let e0: Entity = nexus.createEntity()
|
||||
let p0 = Position(x: 1, y: 2)
|
||||
|
||||
let p0 = Position(x: 1, y: 2)
|
||||
|
||||
e0.assign(p0)
|
||||
e0.assign(p0)
|
||||
// component collision: e0.assign(p0)
|
||||
|
||||
XCTAssert(e0.hasComponents)
|
||||
XCTAssert(e0.numComponents == 1)
|
||||
XCTAssert(e0.hasComponents)
|
||||
XCTAssert(e0.numComponents == 1)
|
||||
|
||||
let rP0: Position = e0.get(component: Position.self)!
|
||||
XCTAssert(rP0.x == 1)
|
||||
XCTAssert(rP0.y == 2)
|
||||
}
|
||||
let rP0: Position = e0.get(component: Position.self)!
|
||||
XCTAssert(rP0.x == 1)
|
||||
XCTAssert(rP0.y == 2)
|
||||
}
|
||||
|
||||
func testComponentDeletion() {
|
||||
func testComponentDeletion() {
|
||||
let identifier: EntityIdentifier = nexus.createEntity().identifier
|
||||
|
||||
let identifier: EntityIdentifier = nexus.createEntity().identifier
|
||||
let e0 = nexus.get(entity: identifier)!
|
||||
|
||||
let e0 = nexus.get(entity: identifier)!
|
||||
XCTAssert(e0.numComponents == 0)
|
||||
e0.remove(Position.self)
|
||||
XCTAssert(e0.numComponents == 0)
|
||||
|
||||
XCTAssert(e0.numComponents == 0)
|
||||
e0.remove(Position.self)
|
||||
XCTAssert(e0.numComponents == 0)
|
||||
let n0 = Name(name: "myName")
|
||||
let p0 = Position(x: 99, y: 111)
|
||||
|
||||
let n0 = Name(name: "myName")
|
||||
let p0 = Position(x: 99, y: 111)
|
||||
e0.assign(n0)
|
||||
XCTAssert(e0.numComponents == 1)
|
||||
XCTAssert(e0.hasComponents)
|
||||
|
||||
e0.assign(n0)
|
||||
XCTAssert(e0.numComponents == 1)
|
||||
XCTAssert(e0.hasComponents)
|
||||
e0.remove(Name.self)
|
||||
|
||||
e0.remove(Name.self)
|
||||
XCTAssert(e0.numComponents == 0)
|
||||
XCTAssert(!e0.hasComponents)
|
||||
|
||||
XCTAssert(e0.numComponents == 0)
|
||||
XCTAssert(!e0.hasComponents)
|
||||
e0.assign(p0)
|
||||
|
||||
e0.assign(p0)
|
||||
XCTAssert(e0.numComponents == 1)
|
||||
XCTAssert(e0.hasComponents)
|
||||
|
||||
XCTAssert(e0.numComponents == 1)
|
||||
XCTAssert(e0.hasComponents)
|
||||
e0.remove(p0)
|
||||
|
||||
e0.remove(p0)
|
||||
XCTAssert(e0.numComponents == 0)
|
||||
XCTAssert(!e0.hasComponents)
|
||||
|
||||
XCTAssert(e0.numComponents == 0)
|
||||
XCTAssert(!e0.hasComponents)
|
||||
e0.assign(n0)
|
||||
e0.assign(p0)
|
||||
|
||||
e0.assign(n0)
|
||||
e0.assign(p0)
|
||||
XCTAssert(e0.numComponents == 2)
|
||||
let (name, position) = e0.get(components: Name.self, Position.self)
|
||||
|
||||
XCTAssert(e0.numComponents == 2)
|
||||
let (name, position) = e0.get(components: Name.self, Position.self)
|
||||
XCTAssert(name?.name == "myName")
|
||||
XCTAssert(position?.x == 99)
|
||||
XCTAssert(position?.y == 111)
|
||||
|
||||
XCTAssert(name?.name == "myName")
|
||||
XCTAssert(position?.x == 99)
|
||||
XCTAssert(position?.y == 111)
|
||||
e0.destroy()
|
||||
|
||||
e0.destroy()
|
||||
XCTAssert(e0.numComponents == 0)
|
||||
}
|
||||
|
||||
XCTAssert(e0.numComponents == 0)
|
||||
|
||||
}
|
||||
|
||||
func testComponentRetrieval() {
|
||||
let pos = Position(x: 1, y: 2)
|
||||
let name = Name(name: "myName")
|
||||
let vel = Velocity(a: 3)
|
||||
let entity = nexus.createEntity(with: pos, name, vel)
|
||||
|
||||
|
||||
let (rPos, rName, rVel) = entity.get(components: Position.self, Name.self, Velocity.self)
|
||||
|
||||
|
||||
|
||||
XCTAssertTrue(rPos === pos)
|
||||
XCTAssertTrue(rName === name)
|
||||
XCTAssertTrue(rVel === vel)
|
||||
|
||||
}
|
||||
|
||||
func testComponentUniqueness() {
|
||||
let a = nexus.createEntity()
|
||||
let b = nexus.createEntity()
|
||||
let c = nexus.createEntity()
|
||||
func testComponentUniqueness() {
|
||||
let a = nexus.createEntity()
|
||||
let b = nexus.createEntity()
|
||||
let c = nexus.createEntity()
|
||||
|
||||
XCTAssert(nexus.numEntities == 3)
|
||||
XCTAssert(nexus.numEntities == 3)
|
||||
|
||||
a.assign(Position(x: 0, y: 0))
|
||||
b.assign(Position(x: 0, y: 0))
|
||||
c.assign(Position(x: 0, y: 0))
|
||||
a.assign(Position(x: 0, y: 0))
|
||||
b.assign(Position(x: 0, y: 0))
|
||||
c.assign(Position(x: 0, y: 0))
|
||||
|
||||
let pA: Position = a.get()!
|
||||
let pB: Position = b.get()!
|
||||
let pA: Position = a.get()!
|
||||
let pB: Position = b.get()!
|
||||
|
||||
pA.x = 23
|
||||
pA.y = 32
|
||||
|
||||
XCTAssert(pB.x != pA.x)
|
||||
XCTAssert(pB.y != pA.y)
|
||||
|
||||
}
|
||||
pA.x = 23
|
||||
pA.y = 32
|
||||
|
||||
XCTAssert(pB.x != pA.x)
|
||||
XCTAssert(pB.y != pA.y)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,133 @@
|
|||
//
|
||||
// SceneGraphTests.swift
|
||||
// FirebladeECSTests
|
||||
//
|
||||
// Created by Christian Treffs on 30.09.19.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import FirebladeECS
|
||||
|
||||
class SceneGraphTests: XCTestCase {
|
||||
|
||||
var nexus: Nexus!
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
nexus = Nexus()
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
super.tearDown()
|
||||
nexus = nil
|
||||
}
|
||||
|
||||
func testAddChild() {
|
||||
let nttParrent = nexus.createEntity()
|
||||
let nttChild1 = nexus.createEntity()
|
||||
XCTAssertEqual(nttParrent.numChildren, 0)
|
||||
XCTAssertTrue(nttParrent.addChild(nttChild1))
|
||||
XCTAssertEqual(nttParrent.numChildren, 1)
|
||||
XCTAssertFalse(nttParrent.addChild(nttChild1))
|
||||
XCTAssertEqual(nttParrent.numChildren, 1)
|
||||
}
|
||||
|
||||
func testRemoveChild() {
|
||||
let nttParrent = nexus.createEntity()
|
||||
let nttChild1 = nexus.createEntity()
|
||||
|
||||
XCTAssertEqual(nttParrent.numChildren, 0)
|
||||
XCTAssertTrue(nttParrent.addChild(nttChild1))
|
||||
XCTAssertEqual(nttParrent.numChildren, 1)
|
||||
XCTAssertTrue(nttParrent.removeChild(nttChild1))
|
||||
XCTAssertEqual(nttParrent.numChildren, 0)
|
||||
XCTAssertFalse(nttParrent.removeChild(nttChild1))
|
||||
XCTAssertEqual(nttParrent.numChildren, 0)
|
||||
XCTAssertTrue(nttParrent.addChild(nttChild1))
|
||||
XCTAssertEqual(nttParrent.numChildren, 1)
|
||||
}
|
||||
|
||||
func testRemoveAllChildren() {
|
||||
let nttParrent = nexus.createEntity()
|
||||
let nttChild1 = nexus.createEntity()
|
||||
let nttChild2 = nexus.createEntity()
|
||||
|
||||
XCTAssertEqual(nttParrent.numChildren, 0)
|
||||
nttParrent.addChild(nttChild1)
|
||||
nttParrent.addChild(nttChild2)
|
||||
XCTAssertEqual(nttParrent.numChildren, 2)
|
||||
|
||||
nttParrent.removeAllChildren()
|
||||
XCTAssertEqual(nttParrent.numChildren, 0)
|
||||
}
|
||||
|
||||
func testDescendRelativesSimple() {
|
||||
let nttParrent = nexus.createEntity(with: Position(x: 1, y: 1))
|
||||
let child1Pos = Position(x: 2, y: 2)
|
||||
let nttChild1 = nexus.createEntity(with: child1Pos)
|
||||
|
||||
nttParrent.addChild(nttChild1)
|
||||
|
||||
let family = nexus.family(requires: Position.self)
|
||||
|
||||
var counter: Int = 0
|
||||
|
||||
XCTAssertEqual(child1Pos.x, 2)
|
||||
XCTAssertEqual(child1Pos.y, 2)
|
||||
|
||||
family
|
||||
.descendRelatives(from: nttParrent)
|
||||
.forEach { (parent: Position, child: Position) in
|
||||
defer { counter += 1 }
|
||||
|
||||
child.x += parent.x
|
||||
child.y += parent.y
|
||||
}
|
||||
|
||||
XCTAssertEqual(counter, 1)
|
||||
XCTAssertEqual(child1Pos.x, 3)
|
||||
XCTAssertEqual(child1Pos.y, 3)
|
||||
}
|
||||
|
||||
func testDescendRelativesOnlyFamilyMembers() {
|
||||
let otherComponents: [Component] = [Position(x: 0, y: 0), Velocity(a: 0), Party(partying: true), Color(), Name(name: "")]
|
||||
|
||||
func addChild(to parent: Entity, index: Int) -> Entity {
|
||||
let randComp = otherComponents.randomElement()!
|
||||
let badChild = nexus.createEntity(with: randComp)
|
||||
let child = nexus.createEntity(with: Index(index: index))
|
||||
parent.addChild(child)
|
||||
parent.addChild(badChild)
|
||||
return child
|
||||
}
|
||||
|
||||
let root = nexus.createEntity(with: Index(index: 0))
|
||||
|
||||
var parent: Entity = root
|
||||
for i in 1..<10 {
|
||||
parent = addChild(to: parent, index: i)
|
||||
}
|
||||
|
||||
XCTAssertEqual(nexus.numEntities, 19)
|
||||
|
||||
var parentSum: Int = 0
|
||||
var childSum: Int = 0
|
||||
|
||||
var lastIndex: Int = -1
|
||||
|
||||
nexus
|
||||
.family(requires: Index.self)
|
||||
.descendRelatives(from: root)
|
||||
.forEach { (parent: Index, child: Index) in
|
||||
XCTAssertEqual(parent.index + 1, child.index)
|
||||
XCTAssertGreaterThan(parent.index, lastIndex)
|
||||
lastIndex = parent.index
|
||||
parentSum += parent.index
|
||||
childSum += child.index
|
||||
}
|
||||
|
||||
XCTAssertEqual(parentSum, 36)
|
||||
XCTAssertEqual(childSum, 45)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -10,43 +10,42 @@ import XCTest
|
|||
|
||||
class SingleTests: XCTestCase {
|
||||
var nexus: Nexus!
|
||||
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
nexus = Nexus()
|
||||
}
|
||||
|
||||
|
||||
override func tearDown() {
|
||||
nexus = nil
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
|
||||
func testSingleCreation() {
|
||||
let single = nexus.single(SingleGameState.self)
|
||||
XCTAssertEqual(single.nexus, self.nexus)
|
||||
XCTAssertTrue(single.nexus === self.nexus)
|
||||
XCTAssertEqual(single.traits.requiresAll.count, 1)
|
||||
XCTAssertEqual(single.traits.excludesAll.count, 0)
|
||||
|
||||
|
||||
XCTAssertEqual(nexus.familyMembersByTraits.keys.count, 1)
|
||||
XCTAssertEqual(nexus.familyMembersByTraits.values.count, 1)
|
||||
|
||||
|
||||
let traits = FamilyTraitSet(requiresAll: [SingleGameState.self], excludesAll: [])
|
||||
XCTAssertEqual(single.traits, traits)
|
||||
}
|
||||
|
||||
|
||||
func testSingleReuse() {
|
||||
let singleA = nexus.single(SingleGameState.self)
|
||||
|
||||
|
||||
let singleB = nexus.single(SingleGameState.self)
|
||||
|
||||
|
||||
XCTAssertEqual(nexus.familyMembersByTraits.keys.count, 1)
|
||||
XCTAssertEqual(nexus.familyMembersByTraits.values.count, 1)
|
||||
|
||||
|
||||
XCTAssertEqual(singleA, singleB)
|
||||
}
|
||||
|
||||
|
||||
|
||||
func testSingleEntityAndComponentCreation() {
|
||||
let single = nexus.single(SingleGameState.self)
|
||||
let gameState = SingleGameState()
|
||||
|
|
@ -54,9 +53,8 @@ class SingleTests: XCTestCase {
|
|||
XCTAssertNotNil(single.component)
|
||||
XCTAssertEqual(single.component.shouldQuit, gameState.shouldQuit)
|
||||
XCTAssertEqual(single.component.playerHealth, gameState.playerHealth)
|
||||
|
||||
}
|
||||
|
||||
|
||||
func testSingleCreationOnExistingFamilyMember() {
|
||||
_ = nexus.createEntity(with: Position(x: 1, y: 2))
|
||||
let singleGame = SingleGameState()
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
import XCTest
|
||||
|
||||
class SparseSetTests: XCTestCase {
|
||||
|
||||
var set: UnorderedSparseSet<Position>!
|
||||
|
||||
override func setUp() {
|
||||
|
|
@ -23,7 +22,6 @@ class SparseSetTests: XCTestCase {
|
|||
}
|
||||
|
||||
func testSparseSetAdd() {
|
||||
|
||||
let num: Int = 100
|
||||
|
||||
for idx in 0..<num {
|
||||
|
|
@ -47,7 +45,6 @@ class SparseSetTests: XCTestCase {
|
|||
}
|
||||
|
||||
func testSparseSetAddAndReplace() {
|
||||
|
||||
let p1 = Position(x: 1, y: 1)
|
||||
let p2 = Position(x: 2, y: 2)
|
||||
|
||||
|
|
@ -60,11 +57,9 @@ class SparseSetTests: XCTestCase {
|
|||
|
||||
XCTAssertEqual(set.get(at: 10)?.x, p2.x)
|
||||
XCTAssertEqual(set.count, 1)
|
||||
|
||||
}
|
||||
|
||||
func testSparseSetGet() {
|
||||
|
||||
let p1 = Position(x: 1, y: 1)
|
||||
|
||||
set.insert(p1, at: 10)
|
||||
|
|
@ -79,7 +74,6 @@ class SparseSetTests: XCTestCase {
|
|||
}
|
||||
|
||||
func testSparseSetRemove() {
|
||||
|
||||
let num: Int = 7
|
||||
|
||||
for idx in 0..<num {
|
||||
|
|
@ -358,7 +352,6 @@ class SparseSetTests: XCTestCase {
|
|||
XCTAssertEqual(set.sparse[5], nil)
|
||||
XCTAssertEqual(set.sparse[6], nil)
|
||||
XCTAssertEqual(set.sparse[7], nil)
|
||||
|
||||
}
|
||||
|
||||
func testSparseSetRemoveAndAdd() {
|
||||
|
|
@ -384,11 +377,9 @@ class SparseSetTests: XCTestCase {
|
|||
XCTAssertEqual(set.get(at: 56)?.x, 56)
|
||||
XCTAssertEqual(set.get(at: 99)?.x, 99)
|
||||
XCTAssertEqual(set.get(at: 3)?.x, 3)
|
||||
|
||||
}
|
||||
|
||||
func testSparseSetRemoveNonPresent() {
|
||||
|
||||
XCTAssertTrue(set.isEmpty)
|
||||
XCTAssertNil(set.remove(at: 100))
|
||||
XCTAssertTrue(set.isEmpty)
|
||||
|
|
@ -427,11 +418,9 @@ class SparseSetTests: XCTestCase {
|
|||
XCTAssertTrue(set.get(at: 0) === a)
|
||||
|
||||
XCTAssertEqual(set.count, 1)
|
||||
|
||||
}
|
||||
|
||||
func testSparseSetNonCongiuousData() {
|
||||
|
||||
var indices: Set<Int> = [0, 30, 1, 21, 78, 56, 99, 3]
|
||||
|
||||
for idx in indices {
|
||||
|
|
@ -462,7 +451,6 @@ class SparseSetTests: XCTestCase {
|
|||
}
|
||||
|
||||
func testSparseSetClear() {
|
||||
|
||||
let num: Int = 100
|
||||
|
||||
XCTAssertEqual(set.count, 0)
|
||||
|
|
@ -506,27 +494,26 @@ class SparseSetTests: XCTestCase {
|
|||
|
||||
// NOTE: this tests only dense insertion order, this is no guarantee for the real ordering.
|
||||
XCTAssertEqual(string, "Hello World")
|
||||
|
||||
}
|
||||
|
||||
|
||||
func testSubscript() {
|
||||
let characters = UnorderedSparseSet<Character>()
|
||||
|
||||
|
||||
characters[4] = "H"
|
||||
characters[13] = "e"
|
||||
characters[44] = "l"
|
||||
characters[123] = "l"
|
||||
characters[89] = "o"
|
||||
|
||||
|
||||
characters[66] = " "
|
||||
characters[77] = "W"
|
||||
characters[55] = "o"
|
||||
characters[90] = "r"
|
||||
characters[34] = "l"
|
||||
characters[140] = "d"
|
||||
|
||||
|
||||
XCTAssertEqual(characters.count, 11)
|
||||
|
||||
|
||||
XCTAssertEqual(characters[4], "H")
|
||||
XCTAssertEqual(characters[13], "e")
|
||||
XCTAssertEqual(characters[44], "l")
|
||||
|
|
@ -539,17 +526,16 @@ class SparseSetTests: XCTestCase {
|
|||
XCTAssertEqual(characters[34], "l")
|
||||
XCTAssertEqual(characters[140], "d")
|
||||
}
|
||||
|
||||
|
||||
func testStartEndIndex() {
|
||||
|
||||
let set = UnorderedSparseSet<Character>()
|
||||
|
||||
|
||||
set.insert("C", at: 33)
|
||||
set.insert("A", at: 11)
|
||||
set.insert("B", at: 22)
|
||||
|
||||
|
||||
let mapped = set.dense.map { $0.element }
|
||||
|
||||
|
||||
XCTAssertEqual(mapped, ["C", "A", "B"])
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,116 +9,113 @@
|
|||
import XCTest
|
||||
|
||||
class SystemsTests: XCTestCase {
|
||||
|
||||
var nexus: Nexus!
|
||||
var colorSystem: ColorSystem!
|
||||
var positionSystem: PositionSystem!
|
||||
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
nexus = Nexus()
|
||||
colorSystem = ColorSystem(nexus: nexus)
|
||||
positionSystem = PositionSystem(nexus: nexus)
|
||||
}
|
||||
|
||||
|
||||
override func tearDown() {
|
||||
colorSystem = nil
|
||||
positionSystem = nil
|
||||
nexus = nil
|
||||
super.tearDown()
|
||||
|
||||
}
|
||||
|
||||
|
||||
func testSystemsUpdate() {
|
||||
|
||||
let num: Int = 10_000
|
||||
|
||||
|
||||
colorSystem.update()
|
||||
positionSystem.update()
|
||||
|
||||
|
||||
let posTraits = positionSystem.positions.traits
|
||||
|
||||
|
||||
XCTAssertEqual(nexus.numEntities, 0)
|
||||
XCTAssertEqual(colorSystem.colors.memberIds.count, 0)
|
||||
XCTAssertEqual(positionSystem.positions.memberIds.count, 0)
|
||||
XCTAssertEqual(nexus.freeEntities.count, 0)
|
||||
XCTAssertEqual(nexus.familyMembersByTraits[posTraits]?.count, 0)
|
||||
|
||||
|
||||
batchCreateEntities(count: num)
|
||||
|
||||
|
||||
XCTAssertEqual(nexus.numEntities, num)
|
||||
XCTAssertEqual(nexus.familyMembersByTraits[posTraits]?.count, num)
|
||||
XCTAssertEqual(colorSystem.colors.memberIds.count, num)
|
||||
XCTAssertEqual(positionSystem.positions.memberIds.count, num)
|
||||
XCTAssertEqual(nexus.freeEntities.count, 0)
|
||||
|
||||
|
||||
colorSystem.update()
|
||||
positionSystem.update()
|
||||
|
||||
|
||||
XCTAssertEqual(nexus.numEntities, num)
|
||||
XCTAssertEqual(nexus.familyMembersByTraits[posTraits]?.count, num)
|
||||
XCTAssertEqual(colorSystem.colors.memberIds.count, num)
|
||||
XCTAssertEqual(positionSystem.positions.memberIds.count, num)
|
||||
XCTAssertEqual(nexus.freeEntities.count, 0)
|
||||
|
||||
|
||||
batchCreateEntities(count: num)
|
||||
|
||||
|
||||
XCTAssertEqual(nexus.numEntities, num * 2)
|
||||
XCTAssertEqual(nexus.familyMembersByTraits[posTraits]?.count, num * 2)
|
||||
XCTAssertEqual(colorSystem.colors.memberIds.count, num * 2)
|
||||
XCTAssertEqual(positionSystem.positions.memberIds.count, num * 2)
|
||||
XCTAssertEqual(nexus.freeEntities.count, 0)
|
||||
|
||||
|
||||
colorSystem.update()
|
||||
positionSystem.update()
|
||||
|
||||
|
||||
XCTAssertEqual(nexus.numEntities, num * 2)
|
||||
XCTAssertEqual(nexus.familyMembersByTraits[posTraits]?.count, num * 2)
|
||||
XCTAssertEqual(colorSystem.colors.memberIds.count, num * 2)
|
||||
XCTAssertEqual(positionSystem.positions.memberIds.count, num * 2)
|
||||
XCTAssertEqual(nexus.freeEntities.count, 0)
|
||||
|
||||
|
||||
batchDestroyEntities(count: num)
|
||||
|
||||
|
||||
XCTAssertEqual(nexus.familyMembersByTraits[posTraits]?.count, num)
|
||||
XCTAssertEqual(nexus.freeEntities.count, num)
|
||||
XCTAssertEqual(nexus.numEntities, num)
|
||||
XCTAssertEqual(colorSystem.colors.memberIds.count, num)
|
||||
XCTAssertEqual(positionSystem.positions.memberIds.count, num)
|
||||
|
||||
|
||||
colorSystem.update()
|
||||
positionSystem.update()
|
||||
|
||||
|
||||
XCTAssertEqual(nexus.familyMembersByTraits[posTraits]?.count, num)
|
||||
XCTAssertEqual(nexus.numEntities, num)
|
||||
XCTAssertEqual(colorSystem.colors.memberIds.count, num)
|
||||
XCTAssertEqual(positionSystem.positions.memberIds.count, num)
|
||||
XCTAssertEqual(nexus.freeEntities.count, num)
|
||||
|
||||
|
||||
batchCreateEntities(count: num)
|
||||
|
||||
|
||||
XCTAssertEqual(nexus.familyMembersByTraits[posTraits]?.count, num * 2)
|
||||
XCTAssertEqual(nexus.numEntities, num * 2)
|
||||
XCTAssertEqual(colorSystem.colors.memberIds.count, num * 2)
|
||||
XCTAssertEqual(positionSystem.positions.memberIds.count, num * 2)
|
||||
XCTAssertEqual(nexus.freeEntities.count, 0)
|
||||
}
|
||||
|
||||
|
||||
func createDefaultEntity() {
|
||||
let e = nexus.createEntity()
|
||||
e.assign(Position(x: 1, y: 2))
|
||||
e.assign(Color())
|
||||
}
|
||||
|
||||
|
||||
func batchCreateEntities(count: Int) {
|
||||
for _ in 0..<count {
|
||||
createDefaultEntity()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func batchDestroyEntities(count: Int) {
|
||||
let family = nexus.family(requires: Position.self)
|
||||
|
||||
|
||||
family
|
||||
.entities
|
||||
.prefix(count)
|
||||
|
|
@ -126,5 +123,4 @@ class SystemsTests: XCTestCase {
|
|||
entity.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ extension ComponentTests {
|
|||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__ComponentTests = [
|
||||
("testComponentIdentifier", testComponentIdentifier),
|
||||
("testComponentIdentifier", testComponentIdentifier)
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -16,6 +16,7 @@ extension EntityTests {
|
|||
// to regenerate.
|
||||
static let __allTests__EntityTests = [
|
||||
("testEntityIdentifierAndIndex", testEntityIdentifierAndIndex),
|
||||
("testEntityIdentifierComparison", testEntityIdentifierComparison)
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -30,7 +31,7 @@ extension FamilyTests {
|
|||
("testFamilyExchange", testFamilyExchange),
|
||||
("testFamilyLateMember", testFamilyLateMember),
|
||||
("testFamilyMemberBasicIteration", testFamilyMemberBasicIteration),
|
||||
("testFamilyReuse", testFamilyReuse),
|
||||
("testFamilyReuse", testFamilyReuse)
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -40,7 +41,7 @@ extension FamilyTraitsTests {
|
|||
// to regenerate.
|
||||
static let __allTests__FamilyTraitsTests = [
|
||||
("testTraitCommutativity", testTraitCommutativity),
|
||||
("testTraitMatching", testTraitMatching),
|
||||
("testTraitMatching", testTraitMatching)
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -49,7 +50,7 @@ extension HashingTests {
|
|||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__HashingTests = [
|
||||
("testCollisionsInCritialRange", testCollisionsInCritialRange),
|
||||
("testCollisionsInCritialRange", testCollisionsInCritialRange)
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -63,7 +64,20 @@ extension NexusTests {
|
|||
("testComponentRetrieval", testComponentRetrieval),
|
||||
("testComponentUniqueness", testComponentUniqueness),
|
||||
("testEntityCreate", testEntityCreate),
|
||||
("testEntityDestroy", testEntityDestroy),
|
||||
("testEntityDestroy", testEntityDestroy)
|
||||
]
|
||||
}
|
||||
|
||||
extension SceneGraphTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__SceneGraphTests = [
|
||||
("testAddChild", testAddChild),
|
||||
("testDescendRelativesOnlyFamilyMembers", testDescendRelativesOnlyFamilyMembers),
|
||||
("testDescendRelativesSimple", testDescendRelativesSimple),
|
||||
("testRemoveAllChildren", testRemoveAllChildren),
|
||||
("testRemoveChild", testRemoveChild)
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -75,7 +89,7 @@ extension SingleTests {
|
|||
("testSingleCreation", testSingleCreation),
|
||||
("testSingleCreationOnExistingFamilyMember", testSingleCreationOnExistingFamilyMember),
|
||||
("testSingleEntityAndComponentCreation", testSingleEntityAndComponentCreation),
|
||||
("testSingleReuse", testSingleReuse),
|
||||
("testSingleReuse", testSingleReuse)
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -95,7 +109,7 @@ extension SparseSetTests {
|
|||
("testSparseSetRemoveAndAdd", testSparseSetRemoveAndAdd),
|
||||
("testSparseSetRemoveNonPresent", testSparseSetRemoveNonPresent),
|
||||
("testStartEndIndex", testStartEndIndex),
|
||||
("testSubscript", testSubscript),
|
||||
("testSubscript", testSubscript)
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -104,7 +118,7 @@ extension SystemsTests {
|
|||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__SystemsTests = [
|
||||
("testSystemsUpdate", testSystemsUpdate),
|
||||
("testSystemsUpdate", testSystemsUpdate)
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -116,9 +130,10 @@ public func __allTests() -> [XCTestCaseEntry] {
|
|||
testCase(FamilyTraitsTests.__allTests__FamilyTraitsTests),
|
||||
testCase(HashingTests.__allTests__HashingTests),
|
||||
testCase(NexusTests.__allTests__NexusTests),
|
||||
testCase(SceneGraphTests.__allTests__SceneGraphTests),
|
||||
testCase(SingleTests.__allTests__SingleTests),
|
||||
testCase(SparseSetTests.__allTests__SparseSetTests),
|
||||
testCase(SystemsTests.__allTests__SystemsTests),
|
||||
testCase(SystemsTests.__allTests__SystemsTests)
|
||||
]
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in New Issue