From cb248d87cfe5be2b10aeda5a26242dfb5fc1ac6c Mon Sep 17 00:00:00 2001 From: Christian Treffs Date: Wed, 13 Feb 2019 17:32:47 +0100 Subject: [PATCH] Introduce single --- Sources/FirebladeECS/Nexus+Entity.swift | 11 ++-- Sources/FirebladeECS/Nexus+TypedSingle.swift | 19 ++++++ Sources/FirebladeECS/TypedSingle.swift | 38 +++++++++++ Tests/FirebladeECSTests/SingleTests.swift | 67 ++++++++++++++++++++ 4 files changed, 131 insertions(+), 4 deletions(-) create mode 100644 Sources/FirebladeECS/Nexus+TypedSingle.swift create mode 100644 Sources/FirebladeECS/TypedSingle.swift create mode 100644 Tests/FirebladeECSTests/SingleTests.swift diff --git a/Sources/FirebladeECS/Nexus+Entity.swift b/Sources/FirebladeECS/Nexus+Entity.swift index 0238c31..c56fb18 100644 --- a/Sources/FirebladeECS/Nexus+Entity.swift +++ b/Sources/FirebladeECS/Nexus+Entity.swift @@ -13,15 +13,18 @@ extension Nexus { return nextReused.index } - public func create(entity name: String? = nil) -> Entity { + // swiftlint:disable function_default_parameter_at_end + @discardableResult + public func create(entity name: String? = nil, with assignedComponents: Component...) -> Entity { let newEntityIndex: EntityIndex = nextEntityIdx() let newEntityIdentifier: EntityIdentifier = newEntityIndex.identifier - let newEntity = Entity(nexus: self, id: newEntityIdentifier, name: name) entityStorage.insert(newEntity, at: newEntityIndex) notify(EntityCreated(entityId: newEntityIdentifier)) - return newEntity - } + assignedComponents.forEach { newEntity.assign($0) } + return newEntity + } + // swiftlint:enable function_default_parameter_at_end /// Number of entities in nexus. public var numEntities: Int { diff --git a/Sources/FirebladeECS/Nexus+TypedSingle.swift b/Sources/FirebladeECS/Nexus+TypedSingle.swift new file mode 100644 index 0000000..7ddd63d --- /dev/null +++ b/Sources/FirebladeECS/Nexus+TypedSingle.swift @@ -0,0 +1,19 @@ +// +// Nexus+TypedSingle.swift +// FirebladeECS +// +// Created by Christian Treffs on 13.02.19. +// + +public extension Nexus { + func single( + requires componentA: A.Type, + excludesAll excludedComponents: Component.Type... + ) -> TypedSingle1 where A: Component { + return TypedSingle1( + self, + requires: componentA, + excludesAll: excludedComponents + ) + } +} diff --git a/Sources/FirebladeECS/TypedSingle.swift b/Sources/FirebladeECS/TypedSingle.swift new file mode 100644 index 0000000..af8b795 --- /dev/null +++ b/Sources/FirebladeECS/TypedSingle.swift @@ -0,0 +1,38 @@ +// +// TypedSingle.swift +// FirebladeECS +// +// Created by Christian Treffs on 13.02.19. +// + +public struct TypedSingle1: Equatable where A: Component { + public let nexus: Nexus + public let traits: FamilyTraitSet + + public init(_ nexus: Nexus, requires compA: A.Type, excludesAll: [Component.Type]) { + self.nexus = nexus + traits = FamilyTraitSet(requiresAll: [compA], excludesAll: excludesAll) + nexus.onFamilyInit(traits: traits) + } + + @inlinable public var entityId: EntityIdentifier? { + guard let members = nexus.members(withFamilyTraits: traits) else { + return nil + } + guard let singleMemberId: EntityIdentifier = members.first else { + return nil + } + return singleMemberId + } + + @inlinable public var entity: Entity? { + guard let entityId = entityId else { + return nil + } + return nexus.get(entity: entityId) + } + + @inlinable public var component: A? { + return entity?.get(component: A.self) + } +} diff --git a/Tests/FirebladeECSTests/SingleTests.swift b/Tests/FirebladeECSTests/SingleTests.swift new file mode 100644 index 0000000..4c52f23 --- /dev/null +++ b/Tests/FirebladeECSTests/SingleTests.swift @@ -0,0 +1,67 @@ +// +// SingleTests.swift +// FirebladeECSTests +// +// Created by Christian Treffs on 13.02.19. +// + + +@testable import FirebladeECS +import XCTest + +class SingleTests: XCTestCase { + var nexus: Nexus! + + override func setUp() { + super.setUp() + nexus = Nexus() + } + + override func tearDown() { + nexus = nil + super.tearDown() + } + + func testSingleCreation() { + let single = nexus.single(requires: Position.self, + excludesAll: Name.self) + XCTAssertEqual(single.nexus, self.nexus) + XCTAssertTrue(single.nexus === self.nexus) + XCTAssertEqual(single.traits.requiresAll.count, 1) + XCTAssertEqual(single.traits.excludesAll.count, 1) + + XCTAssertEqual(nexus.familyMembersByTraits.keys.count, 1) + XCTAssertEqual(nexus.familyMembersByTraits.values.count, 1) + + let traits = FamilyTraitSet(requiresAll: [Position.self], excludesAll: [Name.self]) + XCTAssertEqual(single.traits, traits) + } + + func testSingleReuse() { + let singleA = nexus.single(requires: Position.self, + excludesAll: Name.self) + + let singleB = nexus.single(requires: Position.self, + excludesAll: Name.self) + + XCTAssertEqual(nexus.familyMembersByTraits.keys.count, 1) + XCTAssertEqual(nexus.familyMembersByTraits.values.count, 1) + + XCTAssertEqual(singleA, singleB) + } + + + func testSingleEntityAndComponentCreation() { + let single = nexus.single(requires: Position.self, + excludesAll: Name.self) + XCTAssertNil(single.entity) + XCTAssertNil(single.component) + let pos = Position(x: 1, y: 2) + nexus.create(with: pos) + XCTAssertNotNil(single.entity) + XCTAssertNotNil(single.component) + XCTAssertEqual(single.component?.x, pos.x) + XCTAssertEqual(single.component?.y, pos.y) + + } +}