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.
|
||||
|
||||
```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
|
||||
class Movement: Component {
|
||||
var position: (x: Double, y: Double) = (0.0, 1.0)
|
||||
var velocity: Double = 0.1
|
||||
final class Position: Component {
|
||||
var x: Int = 0
|
||||
var y: Int = 0
|
||||
}
|
||||
```
|
||||
and assign instances of them to an `Entity` with
|
||||
and assign instances of it to an `Entity` with
|
||||
|
||||
```swift
|
||||
let movement = Movement()
|
||||
myEntity.assign(movement)
|
||||
let position = Position(x: 1, y: 2)
|
||||
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
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@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.
|
||||
public var numEntities: Int {
|
||||
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 {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
|
|
@ -162,6 +174,7 @@ public func __allTests() -> [XCTestCaseEntry] {
|
|||
return [
|
||||
testCase(ComponentIdentifierTests.__allTests__ComponentIdentifierTests),
|
||||
testCase(ComponentTests.__allTests__ComponentTests),
|
||||
testCase(EntityCreationTests.__allTests__EntityCreationTests),
|
||||
testCase(EntityTests.__allTests__EntityTests),
|
||||
testCase(FamilyCodingTests.__allTests__FamilyCodingTests),
|
||||
testCase(FamilyTests.__allTests__FamilyTests),
|
||||
|
|
|
|||
Loading…
Reference in New Issue