Merge branch 'feature/bulk-create-entities' into develop
This commit is contained in:
commit
242f7993d6
33
README.md
33
README.md
|
|
@ -66,24 +66,41 @@ let nexus = Nexus()
|
||||||
then create entities by letting the `Nexus` generate them.
|
then create entities by letting the `Nexus` generate them.
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
let myEntity = nexus.createEntity()
|
// an entity without components
|
||||||
|
let newEntity = nexus.createEntity()
|
||||||
```
|
```
|
||||||
|
|
||||||
You can define `Components` like this
|
To define components conform your class to the `Component` protocol
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
class Movement: Component {
|
final class Position: Component {
|
||||||
var position: (x: Double, y: Double) = (0.0, 1.0)
|
var x: Int = 0
|
||||||
var velocity: Double = 0.1
|
var y: Int = 0
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
and assign instances of them to an `Entity` with
|
and assign instances of it to an `Entity` with
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
let movement = Movement()
|
let position = Position(x: 1, y: 2)
|
||||||
myEntity.assign(movement)
|
entity.assign(position)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can be more efficient by assigning components while creating an entity.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
// an entity with two components assigned.
|
||||||
|
nexus.createEntity {
|
||||||
|
Position(x: 1, y: 2)
|
||||||
|
Color(.red)
|
||||||
|
}
|
||||||
|
|
||||||
|
// bulk create entities with multiple components assigned.
|
||||||
|
nexus.createEntities(count: 100) { _ in
|
||||||
|
Position()
|
||||||
|
Color()
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
### 👪 Families
|
### 👪 Families
|
||||||
|
|
||||||
This ECS uses a grouping approach for entities with the same component types to optimize cache locality and ease up access to them.
|
This ECS uses a grouping approach for entities with the same component types to optimize cache locality and ease up access to them.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
//
|
||||||
|
// Nexus+ComponentsBuilder.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Christian Treffs on 30.07.20.
|
||||||
|
//
|
||||||
|
|
||||||
|
@_functionBuilder
|
||||||
|
public enum ComponentsBuilderPreview { }
|
||||||
|
public typealias ComponentsBuilder = ComponentsBuilderPreview
|
||||||
|
|
||||||
|
extension ComponentsBuilder {
|
||||||
|
public static func buildBlock(_ components: Component...) -> [Component] {
|
||||||
|
components
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Context {
|
||||||
|
/// The index of the newly created entity.
|
||||||
|
///
|
||||||
|
/// This is **NOT** equal to the entity identifier.
|
||||||
|
public let index: Int
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Nexus {
|
||||||
|
/// Create an entity assigning one component.
|
||||||
|
///
|
||||||
|
/// Usage:
|
||||||
|
/// ```
|
||||||
|
/// let newEntity = nexus.createEntity {
|
||||||
|
/// Position(x: 1, y: 2)
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
/// - Parameter builder: The component builder.
|
||||||
|
/// - Returns: The newly created entity with the provided component assigned.
|
||||||
|
@discardableResult
|
||||||
|
public func createEntity(@ComponentsBuilder using builder: () -> Component) -> Entity {
|
||||||
|
self.createEntity(with: builder())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create an entity assigning multiple components.
|
||||||
|
///
|
||||||
|
/// Usage:
|
||||||
|
/// ```
|
||||||
|
/// let newEntity = nexus.createEntity {
|
||||||
|
/// Position(x: 1, y: 2)
|
||||||
|
/// Name(name: "Some name")
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
/// - Parameter builder: The component builder.
|
||||||
|
/// - Returns: The newly created entity with the provided components assigned.
|
||||||
|
@discardableResult
|
||||||
|
public func createEntity(@ComponentsBuilder using builder: () -> [Component]) -> Entity {
|
||||||
|
self.createEntity(with: builder())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create multiple entities assigning one component each.
|
||||||
|
///
|
||||||
|
/// Usage:
|
||||||
|
/// ```
|
||||||
|
/// let newEntities = nexus.createEntities(count: 100) { ctx in
|
||||||
|
/// Velocity(a: Float(ctx.index))
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
/// - Parameters:
|
||||||
|
/// - count: The count of entities to create.
|
||||||
|
/// - builder: The component builder providing context.
|
||||||
|
/// - Returns: The newly created entities with the provided component assigned.
|
||||||
|
@discardableResult
|
||||||
|
public func createEntities(count: Int, @ComponentsBuilder using builder: (ComponentsBuilder.Context) -> Component) -> [Entity] {
|
||||||
|
(0..<count).map { self.createEntity(with: builder(ComponentsBuilder.Context(index: $0))) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create multiple entities assigning multiple components each.
|
||||||
|
///
|
||||||
|
/// Usage:
|
||||||
|
/// ```
|
||||||
|
/// let newEntities = nexus.createEntities(count: 100) { ctx in
|
||||||
|
/// Position(x: ctx.index, y: ctx.index)
|
||||||
|
/// Name(name: "\(ctx.index)")
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
/// - Parameters:
|
||||||
|
/// - count: The count of entities to create.
|
||||||
|
/// - builder: The component builder providing context.
|
||||||
|
/// - Returns: The newly created entities with the provided components assigned.
|
||||||
|
@discardableResult
|
||||||
|
public func createEntities(count: Int, @ComponentsBuilder using builder: (ComponentsBuilder.Context) -> [Component]) -> [Entity] {
|
||||||
|
(0..<count).map { self.createEntity(with: builder(ComponentsBuilder.Context(index: $0))) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -21,6 +21,13 @@ extension Nexus {
|
||||||
return newEntity
|
return newEntity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
public func createEntity<C>(with components: C) -> Entity where C: Collection, C.Element == Component {
|
||||||
|
let entity = self.createEntity()
|
||||||
|
components.forEach { entity.assign($0) }
|
||||||
|
return entity
|
||||||
|
}
|
||||||
|
|
||||||
/// Number of entities in nexus.
|
/// Number of entities in nexus.
|
||||||
public var numEntities: Int {
|
public var numEntities: Int {
|
||||||
entityStorage.count
|
entityStorage.count
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
//
|
||||||
|
// EntityCreationTests.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Christian Treffs on 30.07.20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import FirebladeECS
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
final class EntityCreationTests: XCTestCase {
|
||||||
|
|
||||||
|
func testCreateEntityOneComponent() throws {
|
||||||
|
let nexus = Nexus()
|
||||||
|
let entity = nexus.createEntity {
|
||||||
|
Position(x: 1, y: 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertEqual(entity[\Position.x], 1)
|
||||||
|
XCTAssertEqual(entity[\Position.y], 2)
|
||||||
|
|
||||||
|
XCTAssertEqual(nexus.numEntities, 1)
|
||||||
|
XCTAssertEqual(nexus.numComponents, 1)
|
||||||
|
XCTAssertEqual(nexus.numFamilies, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCreateEntityMultipleComponents() throws {
|
||||||
|
let nexus = Nexus()
|
||||||
|
|
||||||
|
let entity = nexus.createEntity {
|
||||||
|
Position(x: 1, y: 2)
|
||||||
|
Name(name: "Hello")
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertEqual(entity[\Position.x], 1)
|
||||||
|
XCTAssertEqual(entity[\Position.y], 2)
|
||||||
|
|
||||||
|
XCTAssertEqual(entity[\Name.name], "Hello")
|
||||||
|
|
||||||
|
XCTAssertEqual(nexus.numEntities, 1)
|
||||||
|
XCTAssertEqual(nexus.numComponents, 2)
|
||||||
|
XCTAssertEqual(nexus.numFamilies, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testBulkCreateEntitiesOneComponent() throws {
|
||||||
|
let nexus = Nexus()
|
||||||
|
|
||||||
|
let entities = nexus.createEntities(count: 100) { ctx in
|
||||||
|
Velocity(a: Float(ctx.index))
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertEqual(entities[0][\Velocity.a], 0)
|
||||||
|
XCTAssertEqual(entities[99][\Velocity.a], 99)
|
||||||
|
|
||||||
|
XCTAssertEqual(nexus.numEntities, 100)
|
||||||
|
XCTAssertEqual(nexus.numComponents, 100)
|
||||||
|
XCTAssertEqual(nexus.numFamilies, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testBulkCreateEntitiesMultipleComponents() throws {
|
||||||
|
let nexus = Nexus()
|
||||||
|
|
||||||
|
let entities = nexus.createEntities(count: 100) { ctx in
|
||||||
|
Position(x: ctx.index, y: ctx.index)
|
||||||
|
Name(name: "\(ctx.index)")
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertEqual(entities[0][\Position.x], 0)
|
||||||
|
XCTAssertEqual(entities[0][\Position.y], 0)
|
||||||
|
XCTAssertEqual(entities[0][\Name.name], "0")
|
||||||
|
XCTAssertEqual(entities[99][\Position.x], 99)
|
||||||
|
XCTAssertEqual(entities[99][\Position.y], 99)
|
||||||
|
XCTAssertEqual(entities[99][\Name.name], "99")
|
||||||
|
|
||||||
|
XCTAssertEqual(nexus.numEntities, 100)
|
||||||
|
XCTAssertEqual(nexus.numComponents, 200)
|
||||||
|
XCTAssertEqual(nexus.numFamilies, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -20,6 +20,18 @@ extension ComponentTests {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension EntityCreationTests {
|
||||||
|
// DO NOT MODIFY: This is autogenerated, use:
|
||||||
|
// `swift test --generate-linuxmain`
|
||||||
|
// to regenerate.
|
||||||
|
static let __allTests__EntityCreationTests = [
|
||||||
|
("testBulkCreateEntitiesMultipleComponents", testBulkCreateEntitiesMultipleComponents),
|
||||||
|
("testBulkCreateEntitiesOneComponent", testBulkCreateEntitiesOneComponent),
|
||||||
|
("testCreateEntityMultipleComponents", testCreateEntityMultipleComponents),
|
||||||
|
("testCreateEntityOneComponent", testCreateEntityOneComponent)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
extension EntityTests {
|
extension EntityTests {
|
||||||
// DO NOT MODIFY: This is autogenerated, use:
|
// DO NOT MODIFY: This is autogenerated, use:
|
||||||
// `swift test --generate-linuxmain`
|
// `swift test --generate-linuxmain`
|
||||||
|
|
@ -162,6 +174,7 @@ public func __allTests() -> [XCTestCaseEntry] {
|
||||||
return [
|
return [
|
||||||
testCase(ComponentIdentifierTests.__allTests__ComponentIdentifierTests),
|
testCase(ComponentIdentifierTests.__allTests__ComponentIdentifierTests),
|
||||||
testCase(ComponentTests.__allTests__ComponentTests),
|
testCase(ComponentTests.__allTests__ComponentTests),
|
||||||
|
testCase(EntityCreationTests.__allTests__EntityCreationTests),
|
||||||
testCase(EntityTests.__allTests__EntityTests),
|
testCase(EntityTests.__allTests__EntityTests),
|
||||||
testCase(FamilyCodingTests.__allTests__FamilyCodingTests),
|
testCase(FamilyCodingTests.__allTests__FamilyCodingTests),
|
||||||
testCase(FamilyTests.__allTests__FamilyTests),
|
testCase(FamilyTests.__allTests__FamilyTests),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue