From 79b87086ef3477e4c0a0c896a2f28c5032be34ac Mon Sep 17 00:00:00 2001 From: Christian Treffs Date: Thu, 22 Aug 2019 17:12:59 +0200 Subject: [PATCH 01/26] Add gitlab runner config --- .gitlab-ci.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..c1faf4d --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,24 @@ +image: swift:latest + +before_script: + - eval "$(curl -sL https://swiftenv.fuller.li/install.sh)" + +stages: + - build + - test + +build_project: + stage: build + script: + - swift package reset + - swift build + tags: + - swift + +test_project: + stage: test + script: + - swift package reset + - swift test + tags: + - swift \ No newline at end of file From d850fd3872cddcc0a39febfd58e90a1941a92952 Mon Sep 17 00:00:00 2001 From: Christian Treffs Date: Thu, 22 Aug 2019 17:14:51 +0200 Subject: [PATCH 02/26] Add docker as runner tag --- .gitlab-ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c1faf4d..4a3f2d6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -14,6 +14,7 @@ build_project: - swift build tags: - swift + - docker test_project: stage: test @@ -21,4 +22,5 @@ test_project: - swift package reset - swift test tags: - - swift \ No newline at end of file + - swift + - docker \ No newline at end of file From e26b03ec5b6c6873d8bf57f21b694ac7dff4cb29 Mon Sep 17 00:00:00 2001 From: Christian Treffs Date: Thu, 22 Aug 2019 17:15:31 +0200 Subject: [PATCH 03/26] remove swift tag --- .gitlab-ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4a3f2d6..12dbc42 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -13,7 +13,6 @@ build_project: - swift package reset - swift build tags: - - swift - docker test_project: @@ -22,5 +21,4 @@ test_project: - swift package reset - swift test tags: - - swift - docker \ No newline at end of file From e5af20b65a86ba5f5f1ac4050f26bdceac584bc1 Mon Sep 17 00:00:00 2001 From: Christian Treffs Date: Thu, 22 Aug 2019 17:24:18 +0200 Subject: [PATCH 04/26] Refine build config --- .gitlab-ci.yml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 12dbc42..3b14853 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,19 +1,21 @@ -image: swift:latest +image: swift:5.0 -before_script: - - eval "$(curl -sL https://swiftenv.fuller.li/install.sh)" +#before_script: + #- eval "$(curl -sL https://swiftenv.fuller.li/install.sh)" stages: - - build - test - + - build_release + build_project: - stage: build + stage: build_release script: - swift package reset - - swift build + - swift build -c release tags: - docker + only: + - master test_project: stage: test From bbf2e0156ab3e475b00c94b5518f07a273847a9a Mon Sep 17 00:00:00 2001 From: Christian Treffs Date: Thu, 22 Aug 2019 17:29:26 +0200 Subject: [PATCH 05/26] Add git depth --- .gitlab-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3b14853..5770440 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -19,6 +19,8 @@ build_project: test_project: stage: test + variables: + GIT_DEPTH: "50" script: - swift package reset - swift test From 51a08a0c5139b893fc4b9507cc9293a9c97a01de Mon Sep 17 00:00:00 2001 From: Christian Treffs Date: Mon, 30 Sep 2019 20:36:51 +0200 Subject: [PATCH 06/26] Add Makefile --- Makefile | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b868cea --- /dev/null +++ b/Makefile @@ -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 From 1ed0e2458577a90a3eed25e6dcc9b3f8ea338c7b Mon Sep 17 00:00:00 2001 From: Christian Treffs Date: Mon, 30 Sep 2019 20:37:03 +0200 Subject: [PATCH 07/26] Swiftlint --- Sources/FirebladeECS/Component+Access.swift | 2 +- Sources/FirebladeECS/Component.swift | 2 +- .../FirebladeECS/ComponentIdentifier.swift | 2 +- Sources/FirebladeECS/Entity+Component.swift | 34 +++++++++---------- Sources/FirebladeECS/Entity.swift | 2 +- Sources/FirebladeECS/Family.swift | 4 +-- Sources/FirebladeECS/Family1.swift | 6 ++-- Sources/FirebladeECS/Family2.swift | 2 +- Sources/FirebladeECS/Family3.swift | 2 +- Sources/FirebladeECS/Family4.swift | 4 +-- Sources/FirebladeECS/Family5.swift | 2 +- .../FamilyRequirementsManaging.swift | 2 +- Sources/FirebladeECS/Nexus+Family.swift | 32 ++++++++--------- Sources/FirebladeECS/NexusEventDelegate.swift | 2 +- Sources/FirebladeECS/NexusEvents.swift | 26 +++++++------- 15 files changed, 62 insertions(+), 62 deletions(-) diff --git a/Sources/FirebladeECS/Component+Access.swift b/Sources/FirebladeECS/Component+Access.swift index acd650e..1fd521f 100644 --- a/Sources/FirebladeECS/Component+Access.swift +++ b/Sources/FirebladeECS/Component+Access.swift @@ -1,6 +1,6 @@ // // Component+Access.swift -// +// // // Created by Christian Treffs on 25.06.19. // diff --git a/Sources/FirebladeECS/Component.swift b/Sources/FirebladeECS/Component.swift index b56345d..73dd567 100644 --- a/Sources/FirebladeECS/Component.swift +++ b/Sources/FirebladeECS/Component.swift @@ -15,6 +15,6 @@ public protocol Component: class { } public extension Component { - static var identifier: ComponentIdentifier { return ComponentIdentifier(Self.self) } + static var identifier: ComponentIdentifier { return ComponentIdentifier(Self.self) } @inlinable var identifier: ComponentIdentifier { return Self.identifier } } diff --git a/Sources/FirebladeECS/ComponentIdentifier.swift b/Sources/FirebladeECS/ComponentIdentifier.swift index 2c3b394..2ed9d57 100644 --- a/Sources/FirebladeECS/ComponentIdentifier.swift +++ b/Sources/FirebladeECS/ComponentIdentifier.swift @@ -1,6 +1,6 @@ // // ComponentIdentifier.swift -// +// // // Created by Christian Treffs on 20.08.19. // diff --git a/Sources/FirebladeECS/Entity+Component.swift b/Sources/FirebladeECS/Entity+Component.swift index fa8a70f..8eb0dbf 100644 --- a/Sources/FirebladeECS/Entity+Component.swift +++ b/Sources/FirebladeECS/Entity+Component.swift @@ -7,28 +7,28 @@ public extension Entity { @inlinable - func get() -> C? where C: Component { - return nexus.get(for: identifier) - } + func get() -> C? where C: Component { + return nexus.get(for: identifier) + } @inlinable - func get(component compType: A.Type = A.self) -> A? where A: Component { - return nexus.get(for: identifier) - } + func get(component compType: A.Type = A.self) -> A? where A: Component { + return nexus.get(for: identifier) + } @inlinable - func get(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) - } + func get(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 + @inlinable func get(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) - } + 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) + } } diff --git a/Sources/FirebladeECS/Entity.swift b/Sources/FirebladeECS/Entity.swift index 0b45535..7d68fa4 100644 --- a/Sources/FirebladeECS/Entity.swift +++ b/Sources/FirebladeECS/Entity.swift @@ -107,6 +107,6 @@ public struct Entity { extension Entity: Equatable { public static func == (lhs: Entity, rhs: Entity) -> Bool { return lhs.nexus == rhs.nexus && - lhs.identifier == rhs.identifier + lhs.identifier == rhs.identifier } } diff --git a/Sources/FirebladeECS/Family.swift b/Sources/FirebladeECS/Family.swift index cbfe10a..8d19398 100644 --- a/Sources/FirebladeECS/Family.swift +++ b/Sources/FirebladeECS/Family.swift @@ -1,6 +1,6 @@ // // Family.swift -// +// // // Created by Christian Treffs on 21.08.19. // @@ -127,6 +127,6 @@ extension Family.EntityComponentIterator: LazySequenceProtocol { } extension Family: Equatable { public static func == (lhs: Family, rhs: Family) -> Bool { return lhs.nexus == rhs.nexus && - lhs.traits == rhs.traits + lhs.traits == rhs.traits } } diff --git a/Sources/FirebladeECS/Family1.swift b/Sources/FirebladeECS/Family1.swift index d4bd37f..7af446a 100644 --- a/Sources/FirebladeECS/Family1.swift +++ b/Sources/FirebladeECS/Family1.swift @@ -1,6 +1,6 @@ // // Family1.swift -// +// // // Created by Christian Treffs on 21.08.19. // @@ -32,7 +32,7 @@ extension Nexus { excludesAll excludedComponents: Component.Type... ) -> Family1 where A: Component { return Family1(nexus: self, - requiresAll: componentA, - excludesAll: excludedComponents) + requiresAll: componentA, + excludesAll: excludedComponents) } } diff --git a/Sources/FirebladeECS/Family2.swift b/Sources/FirebladeECS/Family2.swift index afb470e..9e16251 100644 --- a/Sources/FirebladeECS/Family2.swift +++ b/Sources/FirebladeECS/Family2.swift @@ -1,6 +1,6 @@ // // Family2.swift -// +// // // Created by Christian Treffs on 21.08.19. // diff --git a/Sources/FirebladeECS/Family3.swift b/Sources/FirebladeECS/Family3.swift index 272b936..3db3d46 100644 --- a/Sources/FirebladeECS/Family3.swift +++ b/Sources/FirebladeECS/Family3.swift @@ -1,6 +1,6 @@ // // Family3.swift -// +// // // Created by Christian Treffs on 21.08.19. // diff --git a/Sources/FirebladeECS/Family4.swift b/Sources/FirebladeECS/Family4.swift index 68b1e5b..55177a2 100644 --- a/Sources/FirebladeECS/Family4.swift +++ b/Sources/FirebladeECS/Family4.swift @@ -1,6 +1,6 @@ // // Family4.swift -// +// // // Created by Christian Treffs on 21.08.19. // @@ -38,7 +38,7 @@ extension Nexus { _ componentC: C.Type, _ componentD: D.Type, excludesAll excludedComponents: Component.Type... - ) -> Family4 where A: Component, B: Component, C: Component, D: Component { + ) -> Family4 where A: Component, B: Component, C: Component, D: Component { return Family4( nexus: self, requiresAll: (componentA, componentB, componentC, componentD), diff --git a/Sources/FirebladeECS/Family5.swift b/Sources/FirebladeECS/Family5.swift index 20186f8..fad8865 100644 --- a/Sources/FirebladeECS/Family5.swift +++ b/Sources/FirebladeECS/Family5.swift @@ -1,6 +1,6 @@ // // Family5.swift -// +// // // Created by Christian Treffs on 21.08.19. // diff --git a/Sources/FirebladeECS/FamilyRequirementsManaging.swift b/Sources/FirebladeECS/FamilyRequirementsManaging.swift index 02970d2..157b77c 100644 --- a/Sources/FirebladeECS/FamilyRequirementsManaging.swift +++ b/Sources/FirebladeECS/FamilyRequirementsManaging.swift @@ -1,6 +1,6 @@ // // FamilyRequirementsManaging.swift -// +// // // Created by Christian Treffs on 21.08.19. // diff --git a/Sources/FirebladeECS/Nexus+Family.swift b/Sources/FirebladeECS/Nexus+Family.swift index 6d01a0c..1e19ceb 100644 --- a/Sources/FirebladeECS/Nexus+Family.swift +++ b/Sources/FirebladeECS/Nexus+Family.swift @@ -10,27 +10,27 @@ public extension Nexus { 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) - } + 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 { - return familyMembersByTraits[traits] ?? UnorderedSparseSet() - } + func members(withFamilyTraits traits: FamilyTraitSet) -> UnorderedSparseSet { + return familyMembersByTraits[traits] ?? UnorderedSparseSet() + } - func isMember(_ entity: Entity, in family: FamilyTraitSet) -> Bool { - return isMember(entity.identifier, in: family) - } + func isMember(_ entity: Entity, in family: FamilyTraitSet) -> Bool { + return isMember(entity.identifier, in: family) + } 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) - } + func isMember(entity entityId: EntityIdentifier, inFamilyWithTraits traits: FamilyTraitSet) -> Bool { + return members(withFamilyTraits: traits).contains(entityId.index) + } } diff --git a/Sources/FirebladeECS/NexusEventDelegate.swift b/Sources/FirebladeECS/NexusEventDelegate.swift index c9ca461..72bf4c7 100644 --- a/Sources/FirebladeECS/NexusEventDelegate.swift +++ b/Sources/FirebladeECS/NexusEventDelegate.swift @@ -1,6 +1,6 @@ // // NexusEventDelegate.swift -// +// // // Created by Christian Treffs on 20.08.19. // diff --git a/Sources/FirebladeECS/NexusEvents.swift b/Sources/FirebladeECS/NexusEvents.swift index 08a136c..ff7070f 100644 --- a/Sources/FirebladeECS/NexusEvents.swift +++ b/Sources/FirebladeECS/NexusEvents.swift @@ -8,41 +8,41 @@ 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 } From 28bad38ea9ab1c2c7427f6502e42493f1585d9bd Mon Sep 17 00:00:00 2001 From: Christian Treffs Date: Mon, 30 Sep 2019 20:49:52 +0200 Subject: [PATCH 08/26] Update swiftlint rules --- .swiftlint.yml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/.swiftlint.yml b/.swiftlint.yml index 223ef8f..f89f518 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -11,6 +11,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 +40,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 +58,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 From 29cd66b947725cfcb77af37ca90dae1ee5101476 Mon Sep 17 00:00:00 2001 From: Christian Treffs Date: Mon, 30 Sep 2019 20:50:12 +0200 Subject: [PATCH 09/26] Fix swiftlint warnings --- Sources/FirebladeECS/Component+Access.swift | 6 ++-- Sources/FirebladeECS/Component.swift | 6 ++-- Sources/FirebladeECS/Entity+Component.swift | 10 +++--- Sources/FirebladeECS/EntityIdentifier.swift | 3 +- Sources/FirebladeECS/Family2.swift | 1 + Sources/FirebladeECS/Family3.swift | 3 ++ Sources/FirebladeECS/Family4.swift | 3 ++ Sources/FirebladeECS/Family5.swift | 3 ++ .../FamilyRequirementsManaging.swift | 3 ++ Sources/FirebladeECS/Nexus+Component.swift | 36 ++++++++++--------- Sources/FirebladeECS/Nexus+Entity.swift | 3 +- Sources/FirebladeECS/Nexus+Family.swift | 14 ++++---- Sources/FirebladeECS/Nexus.swift | 3 +- Sources/FirebladeECS/Single.swift | 14 ++++---- 14 files changed, 66 insertions(+), 42 deletions(-) diff --git a/Sources/FirebladeECS/Component+Access.swift b/Sources/FirebladeECS/Component+Access.swift index 1fd521f..ebced3f 100644 --- a/Sources/FirebladeECS/Component+Access.swift +++ b/Sources/FirebladeECS/Component+Access.swift @@ -14,7 +14,8 @@ public struct ReadableOnly where Comp: Component { self.component = component } - @inlinable public subscript(dynamicMember keyPath: KeyPath) -> C { + @inlinable + public subscript(dynamicMember keyPath: KeyPath) -> C { return component[keyPath: keyPath] } } @@ -27,7 +28,8 @@ public struct Writable where Comp: Component { self.component = component } - @inlinable public subscript(dynamicMember keyPath: ReferenceWritableKeyPath) -> C { + @inlinable + public subscript(dynamicMember keyPath: ReferenceWritableKeyPath) -> C { nonmutating get { return component[keyPath: keyPath] } nonmutating set { component[keyPath: keyPath] = newValue } } diff --git a/Sources/FirebladeECS/Component.swift b/Sources/FirebladeECS/Component.swift index 73dd567..6764e89 100644 --- a/Sources/FirebladeECS/Component.swift +++ b/Sources/FirebladeECS/Component.swift @@ -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 } } diff --git a/Sources/FirebladeECS/Entity+Component.swift b/Sources/FirebladeECS/Entity+Component.swift index 8eb0dbf..991a491 100644 --- a/Sources/FirebladeECS/Entity+Component.swift +++ b/Sources/FirebladeECS/Entity+Component.swift @@ -5,19 +5,19 @@ // Created by Christian Treffs on 22.10.17. // -public extension Entity { +extension Entity { @inlinable - func get() -> C? where C: Component { + public func get() -> C? where C: Component { return nexus.get(for: identifier) } @inlinable - func get(component compType: A.Type = A.self) -> A? where A: Component { + public func get(component compType: A.Type = A.self) -> A? where A: Component { return nexus.get(for: identifier) } @inlinable - func get(components _: A.Type, _: B.Type) -> (A?, B?) where A: Component, B: Component { + public func get(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) @@ -25,7 +25,7 @@ public extension Entity { // swiftlint:disable large_tuple @inlinable - func get(components _: A.Type, _: B.Type, _: C.Type) -> (A?, B?, C?) where A: Component, B: Component, C: Component { + public func get(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) diff --git a/Sources/FirebladeECS/EntityIdentifier.swift b/Sources/FirebladeECS/EntityIdentifier.swift index a12841d..69ab623 100644 --- a/Sources/FirebladeECS/EntityIdentifier.swift +++ b/Sources/FirebladeECS/EntityIdentifier.swift @@ -25,7 +25,8 @@ extension EntityIdentifier: Equatable { } extension EntityIdentifier: Hashable { } 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 } } diff --git a/Sources/FirebladeECS/Family2.swift b/Sources/FirebladeECS/Family2.swift index 9e16251..3c69c3d 100644 --- a/Sources/FirebladeECS/Family2.swift +++ b/Sources/FirebladeECS/Family2.swift @@ -19,6 +19,7 @@ public struct Requires2: FamilyRequirementsManaging where A: Component, B: return (compA, compB) } + // swiftlint:disable:next large_tuple public static func entityAndComponents(nexus: Nexus, entityId: EntityIdentifier) -> (Entity, A, B) { let entity: Entity = nexus.get(unsafeEntity: entityId) let compA: A = nexus.get(unsafeComponentFor: entityId) diff --git a/Sources/FirebladeECS/Family3.swift b/Sources/FirebladeECS/Family3.swift index 3db3d46..55618bb 100644 --- a/Sources/FirebladeECS/Family3.swift +++ b/Sources/FirebladeECS/Family3.swift @@ -5,10 +5,13 @@ // Created by Christian Treffs on 21.08.19. // +// swiftlint:disable large_tuple + public typealias Family3 = Family> public struct Requires3: 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] } diff --git a/Sources/FirebladeECS/Family4.swift b/Sources/FirebladeECS/Family4.swift index 55177a2..adc620c 100644 --- a/Sources/FirebladeECS/Family4.swift +++ b/Sources/FirebladeECS/Family4.swift @@ -5,10 +5,13 @@ // Created by Christian Treffs on 21.08.19. // +// swiftlint:disable large_tuple + public typealias Family4 = Family> public struct Requires4: 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] } diff --git a/Sources/FirebladeECS/Family5.swift b/Sources/FirebladeECS/Family5.swift index fad8865..17955ae 100644 --- a/Sources/FirebladeECS/Family5.swift +++ b/Sources/FirebladeECS/Family5.swift @@ -5,10 +5,13 @@ // Created by Christian Treffs on 21.08.19. // +// swiftlint:disable large_tuple + public typealias Family5 = Family> public struct Requires5: 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] } diff --git a/Sources/FirebladeECS/FamilyRequirementsManaging.swift b/Sources/FirebladeECS/FamilyRequirementsManaging.swift index 157b77c..d248a4f 100644 --- a/Sources/FirebladeECS/FamilyRequirementsManaging.swift +++ b/Sources/FirebladeECS/FamilyRequirementsManaging.swift @@ -9,8 +9,11 @@ public protocol FamilyRequirementsManaging { associatedtype Components associatedtype ComponentTypes associatedtype EntityAndComponents + init(_ types: ComponentTypes) + var componentTypes: [Component.Type] { get } + static func components(nexus: Nexus, entityId: EntityIdentifier) -> Components static func entityAndComponents(nexus: Nexus, entityId: EntityIdentifier) -> EntityAndComponents } diff --git a/Sources/FirebladeECS/Nexus+Component.swift b/Sources/FirebladeECS/Nexus+Component.swift index 55b3c7e..6195df4 100644 --- a/Sources/FirebladeECS/Nexus+Component.swift +++ b/Sources/FirebladeECS/Nexus+Component.swift @@ -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(component: C, to entity: Entity) where C: Component { + public final func assign(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(for entityId: EntityIdentifier) -> C? where C: Component { + @inlinable + public final func get(for entityId: EntityIdentifier) -> C? where C: Component { let componentId: ComponentIdentifier = C.identifier return get(componentId: componentId, entityId: entityId) } - @inlinable final func get(unsafeComponentFor entityId: EntityIdentifier) -> C where C: Component { + @inlinable + public final func get(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? { + @inlinable + public final func get(components entityId: EntityIdentifier) -> Set? { 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(componentId: ComponentIdentifier, entityId: EntityIdentifier) -> C? where C: Component { + @inlinable + public final func get(componentId: ComponentIdentifier, entityId: EntityIdentifier) -> C? where C: Component { guard let uniformComponents = componentsByType[componentId] else { return nil } diff --git a/Sources/FirebladeECS/Nexus+Entity.swift b/Sources/FirebladeECS/Nexus+Entity.swift index fff53f7..56d0208 100644 --- a/Sources/FirebladeECS/Nexus+Entity.swift +++ b/Sources/FirebladeECS/Nexus+Entity.swift @@ -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)) } diff --git a/Sources/FirebladeECS/Nexus+Family.swift b/Sources/FirebladeECS/Nexus+Family.swift index 1e19ceb..f7622a3 100644 --- a/Sources/FirebladeECS/Nexus+Family.swift +++ b/Sources/FirebladeECS/Nexus+Family.swift @@ -5,12 +5,12 @@ // 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 { + 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 @@ -18,19 +18,19 @@ public extension Nexus { return traits.isMatch(components: componentIds) } - func members(withFamilyTraits traits: FamilyTraitSet) -> UnorderedSparseSet { + public func members(withFamilyTraits traits: FamilyTraitSet) -> UnorderedSparseSet { return familyMembersByTraits[traits] ?? UnorderedSparseSet() } - func isMember(_ entity: Entity, in family: FamilyTraitSet) -> Bool { + 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 { + public func isMember(entity entityId: EntityIdentifier, inFamilyWithTraits traits: FamilyTraitSet) -> Bool { return members(withFamilyTraits: traits).contains(entityId.index) } } diff --git a/Sources/FirebladeECS/Nexus.swift b/Sources/FirebladeECS/Nexus.swift index fdb232e..c0989d4 100644 --- a/Sources/FirebladeECS/Nexus.swift +++ b/Sources/FirebladeECS/Nexus.swift @@ -64,7 +64,8 @@ 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 && diff --git a/Sources/FirebladeECS/Single.swift b/Sources/FirebladeECS/Single.swift index fbf42b8..bc0a50e 100644 --- a/Sources/FirebladeECS/Single.swift +++ b/Sources/FirebladeECS/Single.swift @@ -9,8 +9,8 @@ public protocol SingleComponent: Component { init() } -public extension Nexus { - func single(_ component: S.Type) -> Single where S: SingleComponent { +extension Nexus { + public func single(_ component: S.Type) -> Single 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: Equatable where A: SingleComponent { +public struct Single 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 } } From 5117127ee38c18dfc110074dd5d2a18563b2b83a Mon Sep 17 00:00:00 2001 From: Christian Treffs Date: Mon, 30 Sep 2019 20:52:24 +0200 Subject: [PATCH 10/26] Lint tests --- .swiftlint.yml | 1 - Tests/FirebladeECSPerformanceTests/Base.swift | 57 +++--- .../ComponentPerformanceTests.swift | 3 +- .../HashingPerformanceTests.swift | 11 +- .../TypedFamilyPerformanceTests.swift | 184 +++++++++--------- .../XCTestManifests.swift | 8 +- Tests/FirebladeECSTests/AccessTests.swift | 22 +-- Tests/FirebladeECSTests/Base.swift | 33 ++-- Tests/FirebladeECSTests/ComponentTests.swift | 14 +- Tests/FirebladeECSTests/EntityTests.swift | 20 +- Tests/FirebladeECSTests/FamilyTests.swift | 88 ++++----- .../FirebladeECSTests/FamilyTraitsTests.swift | 6 - Tests/FirebladeECSTests/HashingTests.swift | 22 +-- Tests/FirebladeECSTests/NexusTests.swift | 144 +++++++------- Tests/FirebladeECSTests/SingleTests.swift | 24 ++- Tests/FirebladeECSTests/SparseSetTests.swift | 32 +-- Tests/FirebladeECSTests/SystemsTests.swift | 54 +++-- Tests/FirebladeECSTests/XCTestManifests.swift | 20 +- 18 files changed, 331 insertions(+), 412 deletions(-) diff --git a/.swiftlint.yml b/.swiftlint.yml index f89f518..5aa8fe1 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -1,7 +1,6 @@ included: - Sources excluded: - - Tests - docs - build identifier_name: diff --git a/Tests/FirebladeECSPerformanceTests/Base.swift b/Tests/FirebladeECSPerformanceTests/Base.swift index 5f9d964..3b5834d 100644 --- a/Tests/FirebladeECSPerformanceTests/Base.swift +++ b/Tests/FirebladeECSPerformanceTests/Base.swift @@ -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 + private let family: Family2 - 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 - } diff --git a/Tests/FirebladeECSPerformanceTests/ComponentPerformanceTests.swift b/Tests/FirebladeECSPerformanceTests/ComponentPerformanceTests.swift index 6272077..14721d5 100644 --- a/Tests/FirebladeECSPerformanceTests/ComponentPerformanceTests.swift +++ b/Tests/FirebladeECSPerformanceTests/ComponentPerformanceTests.swift @@ -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) diff --git a/Tests/FirebladeECSPerformanceTests/HashingPerformanceTests.swift b/Tests/FirebladeECSPerformanceTests/HashingPerformanceTests.swift index af535da..694b4e6 100644 --- a/Tests/FirebladeECSPerformanceTests/HashingPerformanceTests.swift +++ b/Tests/FirebladeECSPerformanceTests/HashingPerformanceTests.swift @@ -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 = Set([14_561_291, 26_451_562, 34_562_182, 488_972_556, 5_128_426_962, 68_211_812]) let b: Set = Set([1_083_838, 912_312, 83_333, 71_234_555, 4_343_234]) let c: Set = Set([3_410_346_899_765, 90_000_002, 12_212_321, 71, 6_123_345_676_543]) - + let input: ContiguousArray = ContiguousArray(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 = Set([14_561_291, 26_451_562, 34_562_182, 488_972_556, 5_128_426_962, 68_211_812]) let b: Set = Set([1_083_838, 912_312, 83_333, 71_234_555, 4_343_234]) let c: Set = Set([3_410_346_899_765, 90_000_002, 12_212_321, 71, 6_123_345_676_543]) - + let input = Set>(arrayLiteral: a, b, c) measure { for _ in 0..<1_000_000 { @@ -37,6 +36,4 @@ class HashingPerformanceTests: XCTestCase { } } } - - } diff --git a/Tests/FirebladeECSPerformanceTests/TypedFamilyPerformanceTests.swift b/Tests/FirebladeECSPerformanceTests/TypedFamilyPerformanceTests.swift index d575a1d..7f6a0ee 100644 --- a/Tests/FirebladeECSPerformanceTests/TypedFamilyPerformanceTests.swift +++ b/Tests/FirebladeECSPerformanceTests/TypedFamilyPerformanceTests.swift @@ -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.. [XCTestCaseEntry] { return [ testCase(ComponentTests.__allTests__ComponentTests), testCase(HashingPerformanceTests.__allTests__HashingPerformanceTests), - testCase(TypedFamilyPerformanceTests.__allTests__TypedFamilyPerformanceTests), + testCase(TypedFamilyPerformanceTests.__allTests__TypedFamilyPerformanceTests) ] } #endif diff --git a/Tests/FirebladeECSTests/AccessTests.swift b/Tests/FirebladeECSTests/AccessTests.swift index d5fbcd3..0c51137 100644 --- a/Tests/FirebladeECSTests/AccessTests.swift +++ b/Tests/FirebladeECSTests/AccessTests.swift @@ -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(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(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 diff --git a/Tests/FirebladeECSTests/Base.swift b/Tests/FirebladeECSTests/Base.swift index ada553d..48de411 100644 --- a/Tests/FirebladeECSTests/Base.swift +++ b/Tests/FirebladeECSTests/Base.swift @@ -45,78 +45,67 @@ class Color: Component { var b: UInt8 = 0 } - final class SingleGameState: SingleComponent { var shouldQuit: Bool = false var playerHealth: Int = 67 - } - class ExampleSystem { private let family: Family2 - + 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 color.r = 1 color.g = 2 color.b = 3 - } + } } } class PositionSystem { let positions: Family1 - + 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 - } + } } - } - diff --git a/Tests/FirebladeECSTests/ComponentTests.swift b/Tests/FirebladeECSTests/ComponentTests.swift index 9ee80ee..0372b0c 100644 --- a/Tests/FirebladeECSTests/ComponentTests.swift +++ b/Tests/FirebladeECSTests/ComponentTests.swift @@ -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) - - } - - + } } diff --git a/Tests/FirebladeECSTests/EntityTests.swift b/Tests/FirebladeECSTests/EntityTests.swift index da6095d..73f46c1 100644 --- a/Tests/FirebladeECSTests/EntityTests.swift +++ b/Tests/FirebladeECSTests/EntityTests.swift @@ -9,20 +9,16 @@ 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)) - - } - + } } diff --git a/Tests/FirebladeECSTests/FamilyTests.swift b/Tests/FirebladeECSTests/FamilyTests.swift index acdb36f..5838973 100644 --- a/Tests/FirebladeECSTests/FamilyTests.swift +++ b/Tests/FirebladeECSTests/FamilyTests.swift @@ -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.. 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 = Set() - + var range: CountableRange = 0 ..< 1_000_000 - + let maxComponents: Int = 1000 let components: [Int] = (0..! override func setUp() { @@ -23,7 +22,6 @@ class SparseSetTests: XCTestCase { } func testSparseSetAdd() { - let num: Int = 100 for idx in 0.. = [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() - + 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() - + 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"]) } } diff --git a/Tests/FirebladeECSTests/SystemsTests.swift b/Tests/FirebladeECSTests/SystemsTests.swift index 500f52e..a40ed60 100644 --- a/Tests/FirebladeECSTests/SystemsTests.swift +++ b/Tests/FirebladeECSTests/SystemsTests.swift @@ -9,122 +9,118 @@ 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.. [XCTestCaseEntry] { testCase(NexusTests.__allTests__NexusTests), testCase(SingleTests.__allTests__SingleTests), testCase(SparseSetTests.__allTests__SparseSetTests), - testCase(SystemsTests.__allTests__SystemsTests), + testCase(SystemsTests.__allTests__SystemsTests) ] } #endif From 2657faff34afecfb68fd625530b9bf034e7370d3 Mon Sep 17 00:00:00 2001 From: Christian Treffs Date: Mon, 30 Sep 2019 21:01:19 +0200 Subject: [PATCH 11/26] Cleanups --- Sources/FirebladeECS/EntityIdentifier.swift | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Sources/FirebladeECS/EntityIdentifier.swift b/Sources/FirebladeECS/EntityIdentifier.swift index 69ab623..6a1dc64 100644 --- a/Sources/FirebladeECS/EntityIdentifier.swift +++ b/Sources/FirebladeECS/EntityIdentifier.swift @@ -11,19 +11,14 @@ 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 { From d4b8ffbf0c526650063e86015feff264eb19a48b Mon Sep 17 00:00:00 2001 From: Christian Treffs Date: Mon, 30 Sep 2019 21:08:11 +0200 Subject: [PATCH 12/26] Add entity identifier comparison test --- Tests/FirebladeECSTests/EntityTests.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Tests/FirebladeECSTests/EntityTests.swift b/Tests/FirebladeECSTests/EntityTests.swift index 73f46c1..7721375 100644 --- a/Tests/FirebladeECSTests/EntityTests.swift +++ b/Tests/FirebladeECSTests/EntityTests.swift @@ -21,4 +21,9 @@ class EntityTests: XCTestCase { XCTAssertEqual(max, EntityIdentifier.invalid) XCTAssertEqual(max.index, Int(UInt32.max)) } + + func testEntityIdentifierComparison() { + XCTAssertTrue(EntityIdentifier(1) < EntityIdentifier(2)) + XCTAssertTrue(EntityIdentifier(23) > EntityIdentifier(4)) + } } From dbf82389e04be0d222235e8fe8ab84f99df89189 Mon Sep 17 00:00:00 2001 From: Christian Treffs Date: Mon, 30 Sep 2019 21:54:19 +0200 Subject: [PATCH 13/26] Add basic scene graph API --- Sources/FirebladeECS/Entity.swift | 6 ++ Sources/FirebladeECS/Family.swift | 38 +++++++++++-- Sources/FirebladeECS/Family1.swift | 6 ++ Sources/FirebladeECS/Family2.swift | 11 +++- Sources/FirebladeECS/Family3.swift | 10 ++++ Sources/FirebladeECS/Family4.swift | 13 +++++ Sources/FirebladeECS/Family5.swift | 16 ++++++ .../FamilyRequirementsManaging.swift | 2 + Tests/FirebladeECSTests/SceneGraphTests.swift | 56 +++++++++++++++++++ 9 files changed, 152 insertions(+), 6 deletions(-) create mode 100644 Tests/FirebladeECSTests/SceneGraphTests.swift diff --git a/Sources/FirebladeECS/Entity.swift b/Sources/FirebladeECS/Entity.swift index 7d68fa4..02fbb6e 100644 --- a/Sources/FirebladeECS/Entity.swift +++ b/Sources/FirebladeECS/Entity.swift @@ -101,6 +101,12 @@ public struct Entity { public func destroy() { nexus.destroy(entity: self) } + + public func addChild(_ entity: Entity) { + } + + public func removeChild(_ entity: Entity) { + } } // MARK: - Equatable diff --git a/Sources/FirebladeECS/Family.swift b/Sources/FirebladeECS/Family.swift index 8d19398..e162492 100644 --- a/Sources/FirebladeECS/Family.swift +++ b/Sources/FirebladeECS/Family.swift @@ -40,6 +40,14 @@ public struct Family where R: FamilyRequirementsManaging { } } +// MARK: - Equatable +extension Family: Equatable { + public static func == (lhs: Family, rhs: Family) -> 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,30 @@ extension Family { extension Family.EntityComponentIterator: LazySequenceProtocol { } -// MARK: - Equatable -extension Family: Equatable { - public static func == (lhs: Family, rhs: Family) -> Bool { - return lhs.nexus == rhs.nexus && - lhs.traits == rhs.traits +// MARK: - relatives iterator + +extension Family { + @inlinable public var descendRelatives: RelativesIterator { + return RelativesIterator(family: self) + } + + public struct RelativesIterator: IteratorProtocol { + @usableFromInline var memberIdsIterator: UnorderedSparseSetIterator + @usableFromInline unowned let nexus: Nexus + + public init(family: Family) { + self.nexus = family.nexus + memberIdsIterator = family.memberIds.makeIterator() + } + + public mutating func next() -> R.RelativesDescending? { + guard let parentId = memberIdsIterator.next() else { + return nil + } + + fatalError("implement") + } } } + +extension Family.RelativesIterator: LazySequenceProtocol { } diff --git a/Sources/FirebladeECS/Family1.swift b/Sources/FirebladeECS/Family1.swift index 7af446a..8e0f42c 100644 --- a/Sources/FirebladeECS/Family1.swift +++ b/Sources/FirebladeECS/Family1.swift @@ -24,6 +24,12 @@ public struct Requires1: 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 { diff --git a/Sources/FirebladeECS/Family2.swift b/Sources/FirebladeECS/Family2.swift index 3c69c3d..8d33200 100644 --- a/Sources/FirebladeECS/Family2.swift +++ b/Sources/FirebladeECS/Family2.swift @@ -5,6 +5,8 @@ // Created by Christian Treffs on 21.08.19. // +// swiftlint:disable large_tuple + public typealias Family2 = Family> public struct Requires2: FamilyRequirementsManaging where A: Component, B: Component { @@ -19,13 +21,20 @@ public struct Requires2: FamilyRequirementsManaging where A: Component, B: return (compA, compB) } - // swiftlint:disable:next large_tuple public static func entityAndComponents(nexus: Nexus, entityId: EntityIdentifier) -> (Entity, A, B) { let entity: Entity = nexus.get(unsafeEntity: entityId) let compA: A = nexus.get(unsafeComponentFor: entityId) 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 { diff --git a/Sources/FirebladeECS/Family3.swift b/Sources/FirebladeECS/Family3.swift index 55618bb..74446e2 100644 --- a/Sources/FirebladeECS/Family3.swift +++ b/Sources/FirebladeECS/Family3.swift @@ -30,6 +30,16 @@ public struct Requires3: 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 { diff --git a/Sources/FirebladeECS/Family4.swift b/Sources/FirebladeECS/Family4.swift index adc620c..88050f7 100644 --- a/Sources/FirebladeECS/Family4.swift +++ b/Sources/FirebladeECS/Family4.swift @@ -32,6 +32,19 @@ public struct Requires4: 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 { diff --git a/Sources/FirebladeECS/Family5.swift b/Sources/FirebladeECS/Family5.swift index 17955ae..0d76248 100644 --- a/Sources/FirebladeECS/Family5.swift +++ b/Sources/FirebladeECS/Family5.swift @@ -34,6 +34,22 @@ public struct Requires5: 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 { diff --git a/Sources/FirebladeECS/FamilyRequirementsManaging.swift b/Sources/FirebladeECS/FamilyRequirementsManaging.swift index d248a4f..9774563 100644 --- a/Sources/FirebladeECS/FamilyRequirementsManaging.swift +++ b/Sources/FirebladeECS/FamilyRequirementsManaging.swift @@ -9,6 +9,7 @@ public protocol FamilyRequirementsManaging { associatedtype Components associatedtype ComponentTypes associatedtype EntityAndComponents + associatedtype RelativesDescending init(_ types: ComponentTypes) @@ -16,4 +17,5 @@ public protocol FamilyRequirementsManaging { 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 } diff --git a/Tests/FirebladeECSTests/SceneGraphTests.swift b/Tests/FirebladeECSTests/SceneGraphTests.swift new file mode 100644 index 0000000..5591744 --- /dev/null +++ b/Tests/FirebladeECSTests/SceneGraphTests.swift @@ -0,0 +1,56 @@ +// +// 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 testParent() { + let nttParrent = nexus.createEntity(with: Position(x: 1, y: 1)) + + let nttChild1 = nexus.createEntity(with: Position(x: 2, y: 2)) + let nttChild2 = nexus.createEntity(with: Position(x: 3, y: 3)) + + nttParrent.addChild(nttChild1) + nttParrent.removeChild(nttChild1) + nttParrent.addChild(nttChild1) + + let family = nexus.family(requires: Position.self) + + + family + .descendRelatives.forEach { (parent: Position, child: Position) in + + } + + let family2 = nexus.family(requiresAll: Position.self, Name.self) + + family2 + .descendRelatives + .forEach { (parent, child) in + let (pPos, pName) = parent + let (cPos, cName) = child + + + } + + } + +} From f312f9335e226598c0f0a501a3acdd31e6206653 Mon Sep 17 00:00:00 2001 From: Christian Treffs Date: Mon, 30 Sep 2019 22:05:56 +0200 Subject: [PATCH 14/26] Extend child API --- Sources/FirebladeECS/Entity.swift | 21 +++++++++++++++++++-- Sources/FirebladeECS/Family.swift | 2 +- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/Sources/FirebladeECS/Entity.swift b/Sources/FirebladeECS/Entity.swift index 02fbb6e..6d7864d 100644 --- a/Sources/FirebladeECS/Entity.swift +++ b/Sources/FirebladeECS/Entity.swift @@ -102,10 +102,27 @@ public struct Entity { nexus.destroy(entity: self) } - public func addChild(_ entity: Entity) { + /// Add an entity as child. + /// - Parameter entity: The child entity. + @discardableResult + public func addChild(_ entity: Entity) -> Bool { + return false } - public func removeChild(_ entity: Entity) { + /// Remove entity as child. + /// - Parameter entity: The child entity. + @discardableResult + public func removeChild(_ entity: Entity) -> Bool { + return false + } + + /// Removes all children from this entity. + public func removeAllChildren() { + } + + /// Returns the number of children for this entity. + public var numChildren: Int { + return 0 } } diff --git a/Sources/FirebladeECS/Family.swift b/Sources/FirebladeECS/Family.swift index e162492..131bb20 100644 --- a/Sources/FirebladeECS/Family.swift +++ b/Sources/FirebladeECS/Family.swift @@ -152,7 +152,7 @@ extension Family { return nil } - fatalError("implement") + return nil } } } From f99f171a154b1c7b22972dc45585d94ccb77c84a Mon Sep 17 00:00:00 2001 From: Christian Treffs Date: Mon, 30 Sep 2019 22:19:41 +0200 Subject: [PATCH 15/26] Add tests for scene graph implementation --- .../TypedFamilyPerformanceTests.swift | 22 +++--- Tests/FirebladeECSTests/Base.swift | 4 +- Tests/FirebladeECSTests/EntityTests.swift | 2 +- Tests/FirebladeECSTests/FamilyTests.swift | 6 +- Tests/FirebladeECSTests/SceneGraphTests.swift | 70 ++++++++++++------- Tests/FirebladeECSTests/SystemsTests.swift | 2 +- Tests/FirebladeECSTests/XCTestManifests.swift | 16 ++++- 7 files changed, 79 insertions(+), 43 deletions(-) diff --git a/Tests/FirebladeECSPerformanceTests/TypedFamilyPerformanceTests.swift b/Tests/FirebladeECSPerformanceTests/TypedFamilyPerformanceTests.swift index 7f6a0ee..ce3a897 100644 --- a/Tests/FirebladeECSPerformanceTests/TypedFamilyPerformanceTests.swift +++ b/Tests/FirebladeECSPerformanceTests/TypedFamilyPerformanceTests.swift @@ -65,7 +65,7 @@ class TypedFamilyPerformanceTests: XCTestCase { _ = entity loopCount += 1 - } + } } XCTAssertEqual(loopCount, family.count * 10) @@ -81,7 +81,7 @@ class TypedFamilyPerformanceTests: XCTestCase { .forEach { (position: Position) in _ = position loopCount += 1 - } + } } XCTAssertEqual(loopCount, numEntities * 10) @@ -103,7 +103,7 @@ class TypedFamilyPerformanceTests: XCTestCase { _ = position loopCount += 1 - } + } } XCTAssertEqual(loopCount, family.count * 10) @@ -127,7 +127,7 @@ class TypedFamilyPerformanceTests: XCTestCase { _ = position loopCount += 1 - } + } } XCTAssertEqual(loopCount, family.count * 10) @@ -150,7 +150,7 @@ class TypedFamilyPerformanceTests: XCTestCase { _ = velocity loopCount += 1 - } + } } XCTAssertEqual(loopCount, family.count * 10) @@ -175,7 +175,7 @@ class TypedFamilyPerformanceTests: XCTestCase { _ = velocity loopCount += 1 - } + } } XCTAssertEqual(loopCount, family.count * 10) @@ -199,7 +199,7 @@ class TypedFamilyPerformanceTests: XCTestCase { _ = name loopCount += 1 - } + } } XCTAssertEqual(loopCount, family.count * 10) @@ -225,7 +225,7 @@ class TypedFamilyPerformanceTests: XCTestCase { _ = name loopCount += 1 - } + } } XCTAssertEqual(loopCount, family.count * 10) @@ -250,7 +250,7 @@ class TypedFamilyPerformanceTests: XCTestCase { _ = color loopCount += 1 - } + } } XCTAssertEqual(loopCount, family.count * 10) @@ -277,7 +277,7 @@ class TypedFamilyPerformanceTests: XCTestCase { _ = color loopCount += 1 - } + } } XCTAssertEqual(loopCount, family.count * 10) @@ -330,7 +330,7 @@ class TypedFamilyPerformanceTests: XCTestCase { _ = empty loopCount += 1 - } + } } XCTAssertEqual(loopCount, family.count * 10) diff --git a/Tests/FirebladeECSTests/Base.swift b/Tests/FirebladeECSTests/Base.swift index 48de411..6705191 100644 --- a/Tests/FirebladeECSTests/Base.swift +++ b/Tests/FirebladeECSTests/Base.swift @@ -79,7 +79,7 @@ class ColorSystem { color.r = 1 color.g = 2 color.b = 3 - } + } } } @@ -106,6 +106,6 @@ class PositionSystem { pos.x = x pos.y = y - } + } } } diff --git a/Tests/FirebladeECSTests/EntityTests.swift b/Tests/FirebladeECSTests/EntityTests.swift index 7721375..d15452d 100644 --- a/Tests/FirebladeECSTests/EntityTests.swift +++ b/Tests/FirebladeECSTests/EntityTests.swift @@ -21,7 +21,7 @@ class EntityTests: XCTestCase { XCTAssertEqual(max, EntityIdentifier.invalid) XCTAssertEqual(max.index, Int(UInt32.max)) } - + func testEntityIdentifierComparison() { XCTAssertTrue(EntityIdentifier(1) < EntityIdentifier(2)) XCTAssertTrue(EntityIdentifier(23) > EntityIdentifier(4)) diff --git a/Tests/FirebladeECSTests/FamilyTests.swift b/Tests/FirebladeECSTests/FamilyTests.swift index 5838973..fb7fb50 100644 --- a/Tests/FirebladeECSTests/FamilyTests.swift +++ b/Tests/FirebladeECSTests/FamilyTests.swift @@ -120,7 +120,7 @@ class FamilyTests: XCTestCase { .forEach { (entity: Entity, _: Position) in entity.assign(Velocity(a: 3.14)) entity.remove(Position.self) - } + } XCTAssertEqual(familyA.count, 0) XCTAssertEqual(familyB.count, 10) @@ -130,7 +130,7 @@ class FamilyTests: XCTestCase { .forEach { (entity: Entity, velocity: Velocity) in entity.assign(Position(x: 1, y: 2)) entity.remove(velocity) - } + } XCTAssertEqual(familyA.count, 10) XCTAssertEqual(familyB.count, 0) @@ -175,7 +175,7 @@ class FamilyTests: XCTestCase { .prefix(currentCount) .forEach { (entity: Entity) in entity.destroy() - } + } XCTAssertEqual(family.memberIds.count, (count / 2)) diff --git a/Tests/FirebladeECSTests/SceneGraphTests.swift b/Tests/FirebladeECSTests/SceneGraphTests.swift index 5591744..4b46369 100644 --- a/Tests/FirebladeECSTests/SceneGraphTests.swift +++ b/Tests/FirebladeECSTests/SceneGraphTests.swift @@ -11,7 +11,7 @@ import FirebladeECS class SceneGraphTests: XCTestCase { var nexus: Nexus! - + override func setUp() { super.setUp() nexus = Nexus() @@ -22,35 +22,57 @@ class SceneGraphTests: XCTestCase { nexus = nil } - func testParent() { + 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 testDescendRelatives() { let nttParrent = nexus.createEntity(with: Position(x: 1, y: 1)) - let nttChild1 = nexus.createEntity(with: Position(x: 2, y: 2)) - let nttChild2 = nexus.createEntity(with: Position(x: 3, y: 3)) - + nttParrent.addChild(nttChild1) - nttParrent.removeChild(nttChild1) - nttParrent.addChild(nttChild1) - + let family = nexus.family(requires: Position.self) - - + family - .descendRelatives.forEach { (parent: Position, child: Position) in - + .descendRelatives.forEach { (_: Position, _: Position) in + // TODO: } - - let family2 = nexus.family(requiresAll: Position.self, Name.self) - - family2 - .descendRelatives - .forEach { (parent, child) in - let (pPos, pName) = parent - let (cPos, cName) = child - - - } - } } diff --git a/Tests/FirebladeECSTests/SystemsTests.swift b/Tests/FirebladeECSTests/SystemsTests.swift index a40ed60..ac815bd 100644 --- a/Tests/FirebladeECSTests/SystemsTests.swift +++ b/Tests/FirebladeECSTests/SystemsTests.swift @@ -121,6 +121,6 @@ class SystemsTests: XCTestCase { .prefix(count) .forEach { (entity: Entity) in entity.destroy() - } + } } } diff --git a/Tests/FirebladeECSTests/XCTestManifests.swift b/Tests/FirebladeECSTests/XCTestManifests.swift index 6f3020b..1f17d34 100644 --- a/Tests/FirebladeECSTests/XCTestManifests.swift +++ b/Tests/FirebladeECSTests/XCTestManifests.swift @@ -15,7 +15,8 @@ extension EntityTests { // `swift test --generate-linuxmain` // to regenerate. static let __allTests__EntityTests = [ - ("testEntityIdentifierAndIndex", testEntityIdentifierAndIndex) + ("testEntityIdentifierAndIndex", testEntityIdentifierAndIndex), + ("testEntityIdentifierComparison", testEntityIdentifierComparison) ] } @@ -67,6 +68,18 @@ extension NexusTests { ] } +extension SceneGraphTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__SceneGraphTests = [ + ("testAddChild", testAddChild), + ("testDescendRelatives", testDescendRelatives), + ("testRemoveAllChildren", testRemoveAllChildren), + ("testRemoveChild", testRemoveChild) + ] +} + extension SingleTests { // DO NOT MODIFY: This is autogenerated, use: // `swift test --generate-linuxmain` @@ -116,6 +129,7 @@ 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) From 8958d9668732cea8c848830d651854a23c5b1781 Mon Sep 17 00:00:00 2001 From: Christian Treffs Date: Mon, 30 Sep 2019 23:17:59 +0200 Subject: [PATCH 16/26] Implement first draft of parent child relation storage --- Sources/FirebladeECS/Entity.swift | 7 +++-- Sources/FirebladeECS/Nexus+Entity.swift | 2 ++ Sources/FirebladeECS/Nexus+SceneGraph.swift | 32 +++++++++++++++++++++ Sources/FirebladeECS/Nexus.swift | 4 +++ 4 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 Sources/FirebladeECS/Nexus+SceneGraph.swift diff --git a/Sources/FirebladeECS/Entity.swift b/Sources/FirebladeECS/Entity.swift index 6d7864d..d7796ea 100644 --- a/Sources/FirebladeECS/Entity.swift +++ b/Sources/FirebladeECS/Entity.swift @@ -106,23 +106,24 @@ public struct Entity { /// - Parameter entity: The child entity. @discardableResult public func addChild(_ entity: Entity) -> Bool { - return false + return nexus.addChild(entity, to: self) } /// Remove entity as child. /// - Parameter entity: The child entity. @discardableResult public func removeChild(_ entity: Entity) -> Bool { - return false + 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 0 + return nexus.numChildren(for: self) } } diff --git a/Sources/FirebladeECS/Nexus+Entity.swift b/Sources/FirebladeECS/Nexus+Entity.swift index 56d0208..d8ce86a 100644 --- a/Sources/FirebladeECS/Nexus+Entity.swift +++ b/Sources/FirebladeECS/Nexus+Entity.swift @@ -56,6 +56,8 @@ extension Nexus { return false } + removeAllChildren(from: entity) + if removeAll(componentes: entityId) { update(familyMembership: entityId) } diff --git a/Sources/FirebladeECS/Nexus+SceneGraph.swift b/Sources/FirebladeECS/Nexus+SceneGraph.swift new file mode 100644 index 0000000..0a3d5ac --- /dev/null +++ b/Sources/FirebladeECS/Nexus+SceneGraph.swift @@ -0,0 +1,32 @@ +// +// 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 + } + return inserted + } + + public final func removeChild(_ child: Entity, from parent: Entity) -> Bool { + return parentChildrenMap[parent.identifier]?.remove(child.identifier) != nil + } + + public final func removeAllChildren(from parent: Entity) { + return parentChildrenMap[parent.identifier] = nil + } + + public final func numChildren(for entity: Entity) -> Int { + return parentChildrenMap[entity.identifier]?.count ?? 0 + } +} diff --git a/Sources/FirebladeECS/Nexus.swift b/Sources/FirebladeECS/Nexus.swift index c0989d4..e05237e 100644 --- a/Sources/FirebladeECS/Nexus.swift +++ b/Sources/FirebladeECS/Nexus.swift @@ -29,12 +29,15 @@ public final class Nexus { /// - Value: Tightly packed EntityIdentifiers that represent the association of an entity to the family. @usableFromInline final var familyMembersByTraits: [FamilyTraitSet: UnorderedSparseSet] + @usableFromInline final var parentChildrenMap: [EntityIdentifier: Set] + public init() { entityStorage = UnorderedSparseSet() componentsByType = [:] componentIdsByEntity = [:] freeEntities = ContiguousArray() familyMembersByTraits = [:] + parentChildrenMap = [:] } public final func clear() { @@ -55,6 +58,7 @@ public final class Nexus { componentsByType.removeAll() componentIdsByEntity.removeAll() familyMembersByTraits.removeAll() + parentChildrenMap.removeAll() } deinit { From 71a319fe270d2bc4041c4845d9cc10a296e9d6b7 Mon Sep 17 00:00:00 2001 From: Christian Treffs Date: Tue, 1 Oct 2019 07:06:00 +0200 Subject: [PATCH 17/26] Finish basic scene graph implementation --- Sources/FirebladeECS/Family.swift | 43 +++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/Sources/FirebladeECS/Family.swift b/Sources/FirebladeECS/Family.swift index 131bb20..7c8b199 100644 --- a/Sources/FirebladeECS/Family.swift +++ b/Sources/FirebladeECS/Family.swift @@ -134,25 +134,52 @@ extension Family.EntityComponentIterator: LazySequenceProtocol { } // MARK: - relatives iterator extension Family { - @inlinable public var descendRelatives: RelativesIterator { - return RelativesIterator(family: self) + @inlinable + public func descendRelatives(from root: Entity) -> RelativesIterator { + return RelativesIterator(family: self, root: root) } public struct RelativesIterator: IteratorProtocol { - @usableFromInline var memberIdsIterator: UnorderedSparseSetIterator @usableFromInline unowned let nexus: Nexus + @usableFromInline let familyTraits: FamilyTraitSet - public init(family: Family) { + @usableFromInline var relatives: [(EntityIdentifier, EntityIdentifier)] + + public init(family: Family, root: Entity) { self.nexus = family.nexus - memberIdsIterator = family.memberIds.makeIterator() + 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 = memberIdsIterator.next() else { + guard let (parentId, childId) = relatives.popLast() else { return nil } - - return nil + return R.relativesDescending(nexus: nexus, parentId: parentId, childId: childId) } } } From 1d261474b9e26da62e06538c06e58cd4c823df36 Mon Sep 17 00:00:00 2001 From: Christian Treffs Date: Tue, 1 Oct 2019 07:06:26 +0200 Subject: [PATCH 18/26] Implement basic scene graph tests --- Tests/FirebladeECSTests/SceneGraphTests.swift | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/Tests/FirebladeECSTests/SceneGraphTests.swift b/Tests/FirebladeECSTests/SceneGraphTests.swift index 4b46369..33239e6 100644 --- a/Tests/FirebladeECSTests/SceneGraphTests.swift +++ b/Tests/FirebladeECSTests/SceneGraphTests.swift @@ -63,16 +63,30 @@ class SceneGraphTests: XCTestCase { func testDescendRelatives() { let nttParrent = nexus.createEntity(with: Position(x: 1, y: 1)) - let nttChild1 = nexus.createEntity(with: Position(x: 2, y: 2)) + 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.forEach { (_: Position, _: Position) in - // TODO: + .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) } } From bc7c00f2a8afa5f94dc7ee8d8e8bc02a1093d73d Mon Sep 17 00:00:00 2001 From: Christian Treffs Date: Tue, 1 Oct 2019 07:12:09 +0200 Subject: [PATCH 19/26] Add documentation --- Sources/FirebladeECS/Nexus.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sources/FirebladeECS/Nexus.swift b/Sources/FirebladeECS/Nexus.swift index e05237e..fe7d354 100644 --- a/Sources/FirebladeECS/Nexus.swift +++ b/Sources/FirebladeECS/Nexus.swift @@ -29,6 +29,8 @@ public final class Nexus { /// - Value: Tightly packed EntityIdentifiers that represent the association of an entity to the family. @usableFromInline final var familyMembersByTraits: [FamilyTraitSet: UnorderedSparseSet] + /// - Key: A parent entity id. + /// - Value: Adjacency Set of all associated children. @usableFromInline final var parentChildrenMap: [EntityIdentifier: Set] public init() { From 0d9d1100dc113f30ca633d4a6d81a36254c379cc Mon Sep 17 00:00:00 2001 From: Christian Treffs Date: Tue, 1 Oct 2019 07:15:54 +0200 Subject: [PATCH 20/26] Extend nexus equality check --- Sources/FirebladeECS/Nexus.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/FirebladeECS/Nexus.swift b/Sources/FirebladeECS/Nexus.swift index fe7d354..ffe17d2 100644 --- a/Sources/FirebladeECS/Nexus.swift +++ b/Sources/FirebladeECS/Nexus.swift @@ -76,7 +76,8 @@ extension Nexus: Equatable { 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) } } From 6f40250fdb2de832546648792e3c7e4c3e6d4bcc Mon Sep 17 00:00:00 2001 From: Christian Treffs Date: Tue, 1 Oct 2019 07:46:28 +0200 Subject: [PATCH 21/26] Extend scene graph tests --- Tests/FirebladeECSTests/Base.swift | 8 ++++ Tests/FirebladeECSTests/SceneGraphTests.swift | 37 ++++++++++++++++++- Tests/FirebladeECSTests/XCTestManifests.swift | 3 +- 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/Tests/FirebladeECSTests/Base.swift b/Tests/FirebladeECSTests/Base.swift index 6705191..eb9e589 100644 --- a/Tests/FirebladeECSTests/Base.swift +++ b/Tests/FirebladeECSTests/Base.swift @@ -45,6 +45,14 @@ 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 diff --git a/Tests/FirebladeECSTests/SceneGraphTests.swift b/Tests/FirebladeECSTests/SceneGraphTests.swift index 33239e6..b9f322c 100644 --- a/Tests/FirebladeECSTests/SceneGraphTests.swift +++ b/Tests/FirebladeECSTests/SceneGraphTests.swift @@ -61,7 +61,7 @@ class SceneGraphTests: XCTestCase { XCTAssertEqual(nttParrent.numChildren, 0) } - func testDescendRelatives() { + 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) @@ -89,4 +89,39 @@ class SceneGraphTests: XCTestCase { 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 child = nexus.createEntity(with: Index(index: index), randComp) + parent.addChild(child) + 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, 10) + + var parentSum: Int = 0 + var childSum: Int = 0 + + nexus + .family(requires: Index.self) + .descendRelatives(from: root) + .forEach { (parent: Index, child: Index) in + XCTAssertEqual(parent.index + 1, child.index) + parentSum += parent.index + childSum += child.index + } + + XCTAssertEqual(parentSum, 36) + XCTAssertEqual(childSum, 45) + } + } diff --git a/Tests/FirebladeECSTests/XCTestManifests.swift b/Tests/FirebladeECSTests/XCTestManifests.swift index 1f17d34..2ae99ac 100644 --- a/Tests/FirebladeECSTests/XCTestManifests.swift +++ b/Tests/FirebladeECSTests/XCTestManifests.swift @@ -74,7 +74,8 @@ extension SceneGraphTests { // to regenerate. static let __allTests__SceneGraphTests = [ ("testAddChild", testAddChild), - ("testDescendRelatives", testDescendRelatives), + ("testDescendRelativesOnlyFamilyMembers", testDescendRelativesOnlyFamilyMembers), + ("testDescendRelativesSimple", testDescendRelativesSimple), ("testRemoveAllChildren", testRemoveAllChildren), ("testRemoveChild", testRemoveChild) ] From 4a2ad80dd613bd248c6ca37a76320286f6e1ba5a Mon Sep 17 00:00:00 2001 From: Christian Treffs Date: Tue, 1 Oct 2019 07:59:09 +0200 Subject: [PATCH 22/26] Extend scene graph implementation --- Sources/FirebladeECS/Family.swift | 2 +- Tests/FirebladeECSTests/SceneGraphTests.swift | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Sources/FirebladeECS/Family.swift b/Sources/FirebladeECS/Family.swift index 7c8b199..eded766 100644 --- a/Sources/FirebladeECS/Family.swift +++ b/Sources/FirebladeECS/Family.swift @@ -143,7 +143,7 @@ extension Family { @usableFromInline unowned let nexus: Nexus @usableFromInline let familyTraits: FamilyTraitSet - @usableFromInline var relatives: [(EntityIdentifier, EntityIdentifier)] + @usableFromInline var relatives: ContiguousArray<(EntityIdentifier, EntityIdentifier)> public init(family: Family, root: Entity) { self.nexus = family.nexus diff --git a/Tests/FirebladeECSTests/SceneGraphTests.swift b/Tests/FirebladeECSTests/SceneGraphTests.swift index b9f322c..7e9021e 100644 --- a/Tests/FirebladeECSTests/SceneGraphTests.swift +++ b/Tests/FirebladeECSTests/SceneGraphTests.swift @@ -94,8 +94,10 @@ class SceneGraphTests: XCTestCase { func addChild(to parent: Entity, index: Int) -> Entity { let randComp = otherComponents.randomElement()! - let child = nexus.createEntity(with: Index(index: index), randComp) + let badChild = nexus.createEntity(with: randComp) + let child = nexus.createEntity(with: Index(index: index)) parent.addChild(child) + parent.addChild(badChild) return child } @@ -106,16 +108,20 @@ class SceneGraphTests: XCTestCase { parent = addChild(to: parent, index: i) } - XCTAssertEqual(nexus.numEntities, 10) + 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 } From 99d08c5139888129f5f4e3d33277598ee5219994 Mon Sep 17 00:00:00 2001 From: Christian Treffs Date: Tue, 1 Oct 2019 08:13:05 +0200 Subject: [PATCH 23/26] Add nexus events --- Sources/FirebladeECS/Nexus+SceneGraph.swift | 15 ++++++++++++++- Sources/FirebladeECS/NexusEvents.swift | 10 ++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/Sources/FirebladeECS/Nexus+SceneGraph.swift b/Sources/FirebladeECS/Nexus+SceneGraph.swift index 0a3d5ac..d8576a4 100644 --- a/Sources/FirebladeECS/Nexus+SceneGraph.swift +++ b/Sources/FirebladeECS/Nexus+SceneGraph.swift @@ -15,14 +15,27 @@ extension Nexus { 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 parentChildrenMap[parent.identifier]?.remove(child.identifier) != nil + 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 } diff --git a/Sources/FirebladeECS/NexusEvents.swift b/Sources/FirebladeECS/NexusEvents.swift index ff7070f..e686e6d 100644 --- a/Sources/FirebladeECS/NexusEvents.swift +++ b/Sources/FirebladeECS/NexusEvents.swift @@ -46,3 +46,13 @@ public struct FamilyCreated: NexusEvent { public struct FamilyDestroyed: NexusEvent { 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 +} From 3d6fd0684138d380888e076124b8f375917dc822 Mon Sep 17 00:00:00 2001 From: Christian Treffs Date: Tue, 1 Oct 2019 11:26:40 +0200 Subject: [PATCH 24/26] Update README --- README.md | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 02591ff..c67c227 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # Fireblade ECS (Entity-Component-System) [![Build Status](https://travis-ci.com/fireblade-engine/ecs.svg?branch=master)](https://travis-ci.com/fireblade-engine/ecs) -[![version 0.9.1](https://img.shields.io/badge/version-0.9.1-brightgreen.svg)](releases/tag/v0.9.1) [![license](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE) -[![swift version](https://img.shields.io/badge/swift-5.0-brightgreen.svg)](#) -[![platforms](https://img.shields.io/badge/platforms-%20macOS%20|%20iOS%20|%20tvOS%20|%20watchOS%20|%20linux%20-brightgreen.svg)](#) +[![swift version](https://img.shields.io/badge/swift-5.0+-brightgreen.svg)](#) +[![platforms](https://img.shields.io/badge/platforms-%20macOS%20|%20iOS%20|%20tvOS%20|%20watchOS-brightgreen.svg)](#) +[![platforms](https://img.shields.io/badge/platforms-linux-brightgreen.svg)](#) 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). @@ -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 you 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. From a6cbbc9170091d84d3fb567107ad3b85b037e9b8 Mon Sep 17 00:00:00 2001 From: Christian Treffs Date: Tue, 1 Oct 2019 11:29:35 +0200 Subject: [PATCH 25/26] Update travis-ci config to get os specific badges --- .travis.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.travis.yml b/.travis.yml index ac39e4b..c989ccd 100644 --- a/.travis.yml +++ b/.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 From 116b3841df9dd27dcb2a59527208647017ae78db Mon Sep 17 00:00:00 2001 From: Christian Treffs Date: Tue, 1 Oct 2019 11:41:35 +0200 Subject: [PATCH 26/26] Update README --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c67c227..53db762 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Fireblade ECS (Entity-Component-System) [![Build Status](https://travis-ci.com/fireblade-engine/ecs.svg?branch=master)](https://travis-ci.com/fireblade-engine/ecs) [![license](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE) -[![swift version](https://img.shields.io/badge/swift-5.0+-brightgreen.svg)](#) +[![swift version](https://img.shields.io/badge/swift-5.0+-brightgreen.svg)](https://swift.org/download) [![platforms](https://img.shields.io/badge/platforms-%20macOS%20|%20iOS%20|%20tvOS%20|%20watchOS-brightgreen.svg)](#) [![platforms](https://img.shields.io/badge/platforms-linux-brightgreen.svg)](#) @@ -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( @@ -208,8 +208,8 @@ nexus.family(requires: Position.self) .forEach { (parent: Position, child: Position) in // parent: the current parent component // child: the current child component - - // update you components hierarchically + + // update your components hierarchically child.x += parent.x child.y += parent.y }