├── .gitattributes
├── .gitignore
├── .ignore
├── LICENSE
├── Macros
├── query_get.swift.template
└── spawn.swift.template
├── Package.resolved
├── Package.swift
├── README.md
├── Sources
├── Benchmarks
│ ├── main.swift
│ ├── uot-bench-optimized.swift
│ └── uot-bench.swift
└── Kiwi
│ ├── arch_store.swift
│ ├── archetype.swift
│ ├── bitmap.swift
│ ├── component.swift
│ ├── componentIterator.swift
│ ├── entity.swift
│ ├── error.swift
│ ├── flags.swift
│ ├── generated
│ ├── query_get.swift
│ └── spawn.swift
│ ├── iteratorUtils.swift
│ ├── query.swift
│ ├── util.swift
│ └── world.swift
├── Tests
└── kiwi-ecs-tests
│ └── kiwi_ecs_swiftTests.swift
├── expand_macros.sh
└── macro.rb
/.gitattributes:
--------------------------------------------------------------------------------
1 | Sources/kiwi_ecs/generated/* linguist-generated
2 |
3 | *.swift.template linguist-language=swift
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | **/.DS_Store
2 | /.build
3 | /Packages
4 | /*.xcodeproj
5 | xcuserdata/
6 | DerivedData/
7 | .swiftpm/*
8 | Launch_*
9 | plot_bench.gnuplot
10 |
--------------------------------------------------------------------------------
/.ignore:
--------------------------------------------------------------------------------
1 | Launch_*
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/Macros/query_get.swift.template:
--------------------------------------------------------------------------------
1 | extension Sequence<(EntityId, UnsafeBufferPointer)> {
2 | @inlinable
3 | public func get() -> some Sequence {
4 | return self.map { (_: EntityId, comps: UnsafeBufferPointer) -> A in
5 | comps[0].assumingMemoryBound(to: A.self).pointee
6 | }
7 | }
8 |
9 | @inlinable
10 | public func getWithIds() -> some Sequence<(EntityId, A)> {
11 | return self.map { (id: EntityId, comps: UnsafeBufferPointer) -> (EntityId, A) in
12 | (id, comps[0].assumingMemoryBound(to: A.self).pointee)
13 | }
14 | }
15 |
16 | <%{for $i in 2..=ENV.QGET}
17 | @inlinable
18 | public func get<<%{for $j in 1..=$i}C$j: Component §unless($i == $j, ",")%>>() -> some Sequence<(<%{for $j in 1..=$i}C$j §unless($i == $j, ",")%>)> {
19 | return self.map { (_: EntityId, comps: UnsafeBufferPointer) -> (<%{for $j in 1..=$i}C$j §unless($i == $j, ",")%>) in
20 | (<%{for $j in 1..=$i} comps[§eval($j - 1)].assumingMemoryBound(to: C$j.self).pointee §unless($i == $j, ",") %>)
21 | }
22 | }
23 | %>
24 |
25 | <%{for $i in 2..=ENV.QGET}
26 | @inlinable
27 | public func getWithIds<<%{for $j in 1..=$i}C$j: Component §unless($i == $j, ",")%>>() -> some Sequence<(EntityId, <%{for $j in 1..=$i}C$j §unless($i == $j, ",")%>)> {
28 | return self.map { (id: EntityId, comps: UnsafeBufferPointer) -> (EntityId, <%{for $j in 1..=$i}C$j §unless($i == $j, ",")%>) in
29 | (id, <%{for $j in 1..=$i} comps[§eval($j - 1)].assumingMemoryBound(to: C$j.self).pointee §unless($i == $j, ",") %>)
30 | }
31 | }
32 | %>
33 | }
34 |
35 | extension Sequence<(EntityId, UnsafeMutableBufferPointer)> {
36 | @inlinable
37 | public func mutate(_ edit: (inout A) -> ()) {
38 | self.forEach { (_: EntityId, comps: UnsafeMutableBufferPointer) -> () in
39 | edit(&comps[0].assumingMemoryBound(to: A.self).pointee)
40 | }
41 | }
42 |
43 | @inlinable
44 | public func mutateWithIds(_ edit: (EntityId, inout A) -> ()) {
45 | self.forEach { (id: EntityId, comps: UnsafeMutableBufferPointer) -> () in
46 | edit(id, &comps[0].assumingMemoryBound(to: A.self).pointee)
47 | }
48 | }
49 |
50 | <%{for $i in 2..=ENV.QGET}
51 | @inlinable
52 | public func mutate
53 | <<%{for $j in 1..=$i}C$j: Component §unless($i == $j, ",")%>>
54 | (_ edit: (<%{for $j in 1..=$i}inout C$j §unless($i == $j, ",")%>) -> ()) {
55 |
56 | self.forEach { (_: EntityId, comps: UnsafeMutableBufferPointer) -> () in
57 | edit(<%{for $j in 1..=$i}&comps[§eval($j - 1)].assumingMemoryBound(to: C$j.self).pointee §unless($i == $j, ",")%>)
58 | }
59 | }
60 |
61 | @inlinable
62 | public func mutateWithIds
63 | <<%{for $j in 1..=$i}C$j: Component §unless($i == $j, ",")%>>
64 | (_ edit: (EntityId, <%{for $j in 1..=$i}inout C$j §unless($i == $j, ",")%>) -> ()) {
65 | self.forEach { (id: EntityId, comps: UnsafeMutableBufferPointer) -> () in
66 | edit(id, <%{for $j in 1..=$i}&comps[§eval($j - 1)].assumingMemoryBound(to: C$j.self).pointee §unless($i == $j, ",")%>)
67 | }
68 | }
69 | %>
70 | }
71 |
--------------------------------------------------------------------------------
/Macros/spawn.swift.template:
--------------------------------------------------------------------------------
1 | extension World {
2 | <%{for $i in 1..=ENV.SPAWN}
3 | /// Spawn a new entity with the specified components
4 | @discardableResult
5 | public mutating func spawn<<%{for $j in 1..=$i}Component$j: Component §unless($j == $i, ",")%>>
6 | (<%{for $j in 1..=$i}_ component$j: Component$j §unless($j == $i, ",")%>) -> EntityId {
7 | var ids: [ComponentId] = [<%{for $j in 1..=$i}Component$j.id §unless($j == $i, ",")%>]
8 | ids = ids.sorted() // on separate lines for compile-time performance
9 |
10 | let archId: ArchetypeId = self.archStore.getArchetypeId(components: ids)
11 | let entId: EntityId = self.entityStore.newId()
12 | self.archStore.getMut(archetype: archId) { archPtr in
13 | let archRow = archPtr.pointee.newArchRowId(entity: entId)
14 |
15 | // register entity
16 | self.entityStore.spawn(entity: entId, arch: archId, archRow: archRow)
17 |
18 | <%{for $j in 1..=$i}
19 | archPtr.pointee.setComponent(row: archRow, component: component$j)
20 | %>
21 | }
22 |
23 | return entId
24 | }
25 | %>
26 | }
27 |
--------------------------------------------------------------------------------
/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "object": {
3 | "pins": [
4 | {
5 | "package": "SwiftDocCPlugin",
6 | "repositoryURL": "https://github.com/apple/swift-docc-plugin",
7 | "state": {
8 | "branch": null,
9 | "revision": "10bc670db657d11bdd561e07de30a9041311b2b1",
10 | "version": "1.1.0"
11 | }
12 | },
13 | {
14 | "package": "SymbolKit",
15 | "repositoryURL": "https://github.com/apple/swift-docc-symbolkit",
16 | "state": {
17 | "branch": null,
18 | "revision": "b45d1f2ed151d057b54504d653e0da5552844e34",
19 | "version": "1.0.0"
20 | }
21 | }
22 | ]
23 | },
24 | "version": 1
25 | }
26 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.5
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "kiwi",
8 | platforms: [.macOS(.v10_15)],
9 | products: [
10 | .library(
11 | name: "Kiwi",
12 | targets: ["Kiwi"]),
13 | ],
14 | dependencies: [
15 | .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0")
16 | ],
17 | targets: [
18 | .target(
19 | name: "Kiwi",
20 | dependencies: []
21 | // swiftSettings: [.unsafeFlags([
22 | // "-Xfrontend",
23 | // "-warn-long-function-bodies=100",
24 | // "-Xfrontend",
25 | // "-warn-long-expression-type-checking=100",
26 | // ])]
27 | ),
28 | .executableTarget(
29 | name: "Benchmarks",
30 | dependencies: [
31 | "Kiwi"
32 | ]
33 | ),
34 | .testTarget(
35 | name: "kiwi-ecs-tests",
36 | dependencies: ["Kiwi"],
37 | swiftSettings: [.unsafeFlags([
38 | "-Xfrontend",
39 | "-warn-long-function-bodies=100",
40 | "-Xfrontend",
41 | "-warn-long-expression-type-checking=50",
42 | ])]
43 | )
44 | ]
45 | )
46 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Kiwi
2 |
3 | Kiwi is a performant and versatile entity component system focussing on fast
4 | iteration and a nice api.
5 |
6 | To get started, read the [usage guide](#usage) below.
7 | A documentation site will be set up later.
8 |
9 | ## Usage
10 |
11 | Add the library to your `Package.swift`
12 |
13 | ```diff
14 | let package = Package(
15 | // ...
16 | dependencies: [
17 | + .package(url: "https://github.com/jomy10/kiwi-ecs-swift", branch: "master")
18 | ],
19 | targets: [
20 | .target(
21 | name: "You target",
22 | dependencies: [
23 | + .target(name: "Kiwi", package: "kiwi-ecs-swift")
24 | ]
25 | )
26 | ]
27 | )
28 | ```
29 |
30 | ### The world
31 |
32 | The world is the main object that controls the ecs.
33 |
34 | ```swift
35 | var world = World()
36 | defer { world.destroy() }
37 | ```
38 |
39 | `world.destroy()` is used to clean up memory. This should always be executed when the
40 | world is not needed anymore.
41 |
42 | Keep in mind that `World` is a struct, so when you pass it around be sure to use `inout`.
43 |
44 | ### Components
45 |
46 | Creating components is as simple as declaring a struct:
47 |
48 | ```swift
49 | struct Position: Component {
50 | var x: Int
51 | var y: Int
52 | }
53 | ```
54 |
55 | For performance reasons you might want to declare an additional property on these structs,
56 | but this is not strictly required:
57 |
58 | ```swift
59 | struct Position: Component {
60 | static let id: Int = UUID().hashValue
61 |
62 | var x: Int
63 | var y: Int
64 | }
65 | ```
66 |
67 | ### Entities
68 |
69 | An entity is spawned with a set of components:
70 |
71 | ```swift
72 | let entityId = world.spawn(Position(x: 10, y: 10))
73 | world.spawn(Position(x: 3, y: 5), Velocity(x: 1.5, y: 0.0))
74 | ```
75 |
76 | The `world.spawn(_ components: Component...)` function will return the id of the newly spawned entity.
77 |
78 | Killing an entity can be done using `world.kill(entity id: EntityId)`:
79 |
80 | ```swift
81 | world.kill(entity: id)
82 | ```
83 |
84 | ### Systems
85 |
86 | #### Queries
87 |
88 | Queries can be constructed as follows:
89 |
90 | ```swift
91 | //===================
92 | // Immutable queries
93 | //===================
94 |
95 | // Query all entities having a position component
96 | world.query(Position.self)
97 | .get() // required in every query immutable query
98 | .forEach { (pos: Position) in // type hint here is neccesary!
99 | print(pos)
100 | }
101 |
102 | // Query all entites having a position and a velocity component and their entity ids
103 | world.query(Position.self, Velocity.self)
104 | .getWithIds() // Besides querying the component, also query the entity ids
105 | .forEach { (id: EntityId, pos: Position, vel: Velocity) in
106 | // ...
107 | }
108 |
109 | //===================
110 | // Mutable queries
111 | //===================
112 |
113 | // Query all entities having a position and velocity component mutably
114 | world.queryMut(Position.self, Velocity.self)
115 | .mutate { (pos: inout Position, vel: inout Velocity) // type hints and inout keyword neccesary
116 | pos.x += vel.x
117 | pos.y += vel.y
118 | }
119 |
120 | // Query all entities having a position component mutably as well as their entity ids
121 | world.queryMut(Position.self)
122 | .mutateWithIds { (id: EntityId, pos: inout Position) in
123 | // ...
124 | }
125 | ```
126 |
127 | #### System Groups
128 |
129 | system groups are currently unimplemented and are planned for a future release.
130 |
131 | ### Flags
132 |
133 | Components can't be zero-sized. This is where a flag can be used.
134 |
135 | #### Defining flags
136 |
137 | ```swift
138 | enum Flags: FlagId {
139 | case Player
140 | case Enemy
141 | }
142 | ```
143 |
144 | #### Setting flags
145 |
146 | ```swift
147 | let id = world.spawn()
148 |
149 | world.setFlag(entity: id, Flags.Player)
150 | ```
151 |
152 | #### Removing a flag
153 |
154 | ```swift
155 | world.removeFlag(entity: id, Flags.Player)
156 | ```
157 |
158 | #### Checking wether an entity has a flag
159 |
160 | ```swift
161 | world.hasFlag(entity: id, Flags.Player)
162 | ```
163 |
164 | #### Filtering queries with flags
165 |
166 | ```swift
167 | world.query(Position.self)
168 | .getWithIds()
169 | .filter { (id: EntityId, _: Position) in world.hasFlag(entity: id, Flags.Player) }
170 | .forEach { (_, pos) in
171 | print(pos)
172 | }
173 | ```
174 |
175 | The `hasFlags` function is also available.
176 |
177 | ### Road map
178 |
179 | - [ ] System groups
180 |
181 | ### Contributing
182 |
183 | Contributers are welcome to open an issue requesting new features or fixes or opening a
184 | pull request for them.
185 |
186 | ### License
187 |
188 | The library is currenlty licensed under LGPLv3.
189 |
--------------------------------------------------------------------------------
/Sources/Benchmarks/main.swift:
--------------------------------------------------------------------------------
1 |
2 | let bench: String
3 | if CommandLine.argc < 2 {
4 | bench = "uot"
5 | } else {
6 | bench = CommandLine.arguments[1]
7 | }
8 |
9 | // let bench = CommandLine.arguments[1]
10 |
11 | var benchmarks: [String:()->()] = [
12 | "uot": uotOpt,
13 | ]
14 |
15 | if benchmarks[bench]?() == nil {
16 | print("Invalid benchmark name: '\(bench)'")
17 | }
18 |
--------------------------------------------------------------------------------
/Sources/Benchmarks/uot-bench-optimized.swift:
--------------------------------------------------------------------------------
1 | import Kiwi
2 | import Foundation
3 |
4 | @usableFromInline
5 | struct Vec2: Equatable {
6 | @usableFromInline
7 | var x: Float64
8 | @usableFromInline
9 | var y: Float64
10 | }
11 |
12 | @usableFromInline
13 | struct Position: Component, Equatable {
14 | @usableFromInline
15 | static let id = UUID().hashValue
16 |
17 | @usableFromInline
18 | var _val: Vec2
19 |
20 | public init(x: Float64, y: Float64) {
21 | self._val = Vec2(x: x, y: y)
22 | }
23 |
24 | public var x: Float64 {
25 | get { self._val.x }
26 | set { self._val.x = newValue }
27 | }
28 | public var y: Float64 {
29 | get { self._val.y }
30 | set { self._val.y = newValue }
31 | }
32 | }
33 |
34 | @usableFromInline
35 | struct Velocity: Component {
36 | @usableFromInline
37 | static let id = UUID().hashValue
38 |
39 | @usableFromInline
40 | var _val: Vec2
41 |
42 | public init(x: Float64, y: Float64) {
43 | self._val = Vec2(x: x, y: y)
44 | }
45 |
46 | public var x: Float64 {
47 | get { self._val.x }
48 | set { self._val.x = newValue }
49 | }
50 | public var y: Float64 {
51 | get { self._val.y }
52 | set { self._val.y = newValue }
53 | }
54 | }
55 |
56 | @usableFromInline
57 | struct Collider: Component {
58 | @usableFromInline
59 | static let id = UUID().hashValue
60 |
61 | @usableFromInline
62 | var radius: Float64
63 | }
64 |
65 | @usableFromInline
66 | struct Count: Component {
67 | @usableFromInline
68 | static let id = UUID().hashValue
69 |
70 | @usableFromInline
71 | var count: Int32
72 | init() { self.count = 0 }
73 | }
74 |
75 | let iterations = 1000
76 | let maxPosition = 100.0
77 | let maxSpeed = 10.0
78 | let maxCollider = 1.0
79 |
80 | func uotOpt() {
81 | let size: Int
82 | if CommandLine.argc < 2 {
83 | size = 1000
84 | } else {
85 | size = Int(CommandLine.arguments[2])!
86 | }
87 | let collisionLimit: Int32
88 | if CommandLine.argc < 3 {
89 | collisionLimit = 0
90 | } else {
91 | collisionLimit = Int32(CommandLine.arguments[3])!
92 | }
93 |
94 | try! benchPhysicsOptimized(size, collisionLimit)
95 | }
96 |
97 | @inlinable
98 | func moveCircles(_ world: inout World, fixedTime: Float64, maxPosition: Float64) {
99 | world.queryMut(Position.self, Velocity.self)
100 | .mutateWithIds { (id: EntityId, pos: inout Position, vel: inout Velocity) in
101 | pos.x += vel.x * fixedTime
102 | pos.y += vel.y * fixedTime
103 |
104 | // Bump into bounding rect
105 | if pos.x <= 0 || pos.x >= maxPosition {
106 | vel.x = -vel.x
107 | }
108 | if pos.y <= 0 || pos.y >= maxPosition {
109 | vel.y = -vel.y
110 | }
111 | }
112 | }
113 |
114 | @inlinable
115 | func checkCollisions(_ world: inout World, _ deathCount: inout Int, collisionLimit: Int32) {
116 | let innerQuery: some Sequence<(EntityId, Position, Collider)> = world.query(Position.self, Collider.self).getWithIds()
117 |
118 | world.queryMut(Position.self, Collider.self, Count.self)
119 | .mutateWithIds { (aId: EntityId, aPos: inout Position, aCol: inout Collider, aCnt: inout Count) in
120 | for (bId, bPos, bCol) in innerQuery {
121 | if aId == bId { continue }
122 |
123 | let dx = aPos.x - bPos.x
124 | let dy = aPos.x - bPos.y
125 | let distSq = (dx * dx) + (dy * dy)
126 |
127 | let dr = aCol.radius + bCol.radius
128 | let drSq = dr * dr
129 |
130 | if drSq > distSq {
131 | aCnt.count += 1
132 | }
133 |
134 | // Kill and spawn one
135 | if collisionLimit > 0 && aCnt.count > collisionLimit {
136 | world.kill(entity: aId)
137 | deathCount += 1
138 | break
139 | }
140 | }
141 | }
142 | }
143 |
144 | func benchPhysicsOptimized(_ size: Int, _ collisionLimit: Int32) throws {
145 | var world = World()
146 | defer { world.destroy() }
147 |
148 | for _ in 0..= maxPosition {
89 | // vel.x = -vel.x
90 | // }
91 | // if pos.y <= 0.0 || pos.y >= maxPosition {
92 | // vel.y = -vel.y
93 | // }
94 |
95 | // }
96 | // }
97 |
98 | // @inlinable
99 | // func checkCollisions(_ world: inout World, _ collisionLimit: UInt32, _ deathCount: inout Int) {
100 | // var dead: Set = Set()
101 |
102 | // let query: some Sequence<(EntityId, Pos, Collider)> = world.query(Pos.self, Collider.self).getWithIds()
103 |
104 | // world.queryMut(Pos.self, Collider.self)
105 | // .mutateWithIds { (id1: EntityId, pos1: inout Pos, col1: inout Collider) in
106 | // query.forEach { (id2, pos2, col2) in
107 | // let dx = pos1.x - pos2.x
108 | // let dy = pos2.y - pos2.y
109 | // let distSq = (dx * dx) + (dy * dy)
110 |
111 | // let dr = col1.radius - col2.radius
112 | // let drSq = dr * dr
113 |
114 | // if drSq > distSq {
115 | // col1.count += 1
116 | // }
117 |
118 | // // kill and spawn one
119 | // if collisionLimit > 0 && col1.count > collisionLimit {
120 | // deathCount += 1
121 | // dead.insert(id1)
122 | // }
123 | // }
124 | // }
125 |
126 | // dead.forEach { (deadId) in
127 | // world.kill(entity: deadId)
128 | // }
129 | // }
130 |
--------------------------------------------------------------------------------
/Sources/Kiwi/arch_store.swift:
--------------------------------------------------------------------------------
1 | // @usableFromInline
2 | public typealias ArchetypeId = UInt32
3 |
4 | struct ArchStore {
5 | private var archetypes: [Archetype]
6 | internal var compMap: [[ComponentId]:ArchetypeId]
7 | }
8 |
9 | internal extension ArchStore {
10 | init() {
11 | self.archetypes = []
12 | self.compMap = [:]
13 | }
14 |
15 | mutating func deallocArchetypes() {
16 | self.archetypes.forEach { $0.dealloc() }
17 | }
18 |
19 | @inlinable
20 | func get(archetype: ArchetypeId, _ body: (UnsafePointer) throws -> Result) rethrows -> Result {
21 | try withUnsafePointer(to: self.archetypes[Int(archetype)]) { ptr in
22 | try body(ptr)
23 | }
24 | }
25 |
26 | @inlinable
27 | mutating func getMut(archetype: ArchetypeId, _ body: (UnsafeMutablePointer) throws -> Result) rethrows -> Result {
28 | try withUnsafeMutablePointer(to: &self.archetypes[Int(archetype)]) { ptr in
29 | try body(ptr)
30 | }
31 | }
32 |
33 | // `components` should be a sorted array
34 | @inlinable
35 | mutating func getArchetypeId(components: [ComponentId]) -> ArchetypeId {
36 | if let archId = compMap[components] {
37 | return archId
38 | } else {
39 | // Create new archetype
40 | let id = ArchetypeId(self.archetypes.count)
41 | self.archetypes.append(Archetype(components: components))
42 | self.compMap[components] = id
43 | return id
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Sources/Kiwi/archetype.swift:
--------------------------------------------------------------------------------
1 | @usableFromInline
2 | typealias ArchRowId = UInt32
3 |
4 | //===================
5 | // Component Storage
6 | //===================
7 |
8 | /// Contains the component values for all entities in an archetype of one
9 | /// type of component
10 | struct ComponentColumn {
11 | var components: [T]
12 |
13 | init() {
14 | self.components = []
15 | }
16 | }
17 |
18 | //===========
19 | // Archetype
20 | //===========
21 |
22 | struct Archetype {
23 | /// { id => ComponentColumn }
24 | private var components: [ComponentId: UnsafeMutableRawPointer]
25 | /// Used only for finding new ids for the next entity added
26 | private var availableEntityRows: [ArchRowId]
27 | /// index into this array = ArchRowId
28 | /// when entry is nil, the ArchRowId is available. Used to filter the
29 | /// components arrrays to contain only alive entities
30 | private var entities: [EntityId?]
31 | private let allocatedComponentsBuffer: UnsafeMutablePointer>
32 | }
33 |
34 | internal extension Archetype {
35 | /// - components: the ids of the components
36 | init(components componentIds: [ComponentId]) {//, sizes: [Int]) {
37 | self.components = Dictionary(minimumCapacity: componentIds.count)
38 | self.availableEntityRows = []
39 | self.entities = []
40 |
41 | let compColumns = UnsafeMutablePointer>.allocate(capacity: componentIds.count)
42 | self.allocatedComponentsBuffer = compColumns
43 |
44 | componentIds.enumerated().forEach { (i, compId) in
45 | let colPtr = compColumns.advanced(by: i)
46 | colPtr.initialize(to: ComponentColumn())
47 | self.components[compId] = UnsafeMutableRawPointer(colPtr)
48 | }
49 | }
50 |
51 | func dealloc() {
52 | self.allocatedComponentsBuffer.deallocate()
53 | }
54 |
55 | /// Get an empty entity archrow id
56 | @inlinable
57 | mutating func newArchRowId(entity: EntityId) -> ArchRowId {
58 | if let id = self.availableEntityRows.popLast() {
59 | self.entities[Int(id)] = entity
60 | return id
61 | } else {
62 | let id = self.entities.count
63 | self.entities.append(entity)
64 | return ArchRowId(id)
65 | }
66 | }
67 |
68 | @inlinable
69 | mutating func setComponentThrowable(row: ArchRowId, component: T) throws {
70 | guard let ptr = self.components[T.id]?.bindMemory(to: ComponentColumn.self, capacity: 1) else {
71 | throw KiwiError(.EntityDoesNotHaveComponent, message: "Component \(component) (\(T.id)) cannot be assigned to the entity because it does not have the specified component")
72 | }
73 |
74 | if ptr.pointee.components.count <= row {
75 | ptr.pointee.components.append(component)
76 | } else {
77 | ptr.pointee.components[Int(row)] = component
78 | }
79 | }
80 |
81 | @inlinable
82 | mutating func setComponent(row: ArchRowId, component: T) {
83 | guard let ptr = self.components[T.id]?.bindMemory(to: ComponentColumn.self, capacity: 1) else {
84 | fatalError("Component \(component) (\(T.id)) cannot be assigned to the entity because it does not have the specified component")
85 | }
86 |
87 | if ptr.pointee.components.count <= row {
88 | ptr.pointee.components.append(component)
89 | } else {
90 | ptr.pointee.components[Int(row)] = component
91 | }
92 | }
93 |
94 | /// - Attention: fatal error if the entity does not have the component or the entity does not exist
95 | @inlinable
96 | func getComponent(row: ArchRowId) throws -> T {
97 | guard let ptr = self.components[T.id]?.bindMemory(to: ComponentColumn.self, capacity: 1) else {
98 | throw KiwiError(.EntityDoesNotHaveComponent)
99 | }
100 |
101 | return ptr.pointee.components[Int(row)]
102 | }
103 |
104 | @inlinable
105 | mutating func getComponentMut(row: ArchRowId, _ body: (UnsafeMutablePointer) throws -> ()) throws {
106 | guard let compColPtr = self.components[T.id]?.bindMemory(to: ComponentColumn.self, capacity: 1) else {
107 | throw KiwiError(.EntityDoesNotHaveComponent, message: "Component (\(T.id)) cannot be assigned to the entity because it does not have the specified component")
108 | }
109 |
110 | try compColPtr.pointee.components.withUnsafeMutableBufferPointer { componentsPtr in
111 | // row should always be valid
112 | try body(&componentsPtr[Int(row)])
113 | }
114 | }
115 |
116 | @inlinable
117 | func hasComponent(component id: ComponentId) -> Bool {
118 | self.components[id] != nil
119 | }
120 |
121 | @inlinable
122 | mutating func removeEntity(row: ArchRowId) {
123 | self.availableEntityRows.append(row)
124 | self.entities[Int(row)] = nil
125 | }
126 |
127 | func components(_ componentsInfo: [(ComponentId, Int)]) -> ComponentsIterator {
128 | let componentsPtrs = componentsInfo.map { componentInfo in
129 | (
130 | componentInfo.1,
131 | UnsafeRawPointer(self.components[componentInfo.0]!).assumingMemoryBound(to: ComponentColumn.self)
132 | )
133 | }
134 | return ComponentsIterator(entities: self.entities.lazy, components: componentsPtrs)
135 | }
136 |
137 | mutating func componentsMut(_ componentsInfo: [(ComponentId, Int)]) -> MutableComponentsIterator {
138 | let componentsPtrs = componentsInfo.map { componentInfo in
139 | (
140 | componentInfo.1,
141 | self.components[componentInfo.0]!.assumingMemoryBound(to: ComponentColumn.self)
142 | )
143 | }
144 | return MutableComponentsIterator(entities: self.entities.lazy, components: componentsPtrs)
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/Sources/Kiwi/bitmap.swift:
--------------------------------------------------------------------------------
1 | @usableFromInline
2 | struct Bitmap {
3 | @usableFromInline
4 | var bits: [Int]
5 | @usableFromInline
6 | let size: Int
7 |
8 | @inlinable
9 | init() {
10 | self.bits = []
11 | self.size = MemoryLayout.size
12 | }
13 | }
14 |
15 | extension Bitmap {
16 | @inlinable
17 | func contains(_ i: Int) -> Bool {
18 | let idx: Int = i / self.size
19 | let idx2: Int = i % self.size
20 | if self.bits.count <= idx {
21 | return false
22 | } else {
23 | return self.bits[idx] & (1 << idx2) == (1 << idx2)
24 | }
25 | }
26 |
27 | @inlinable
28 | mutating func set(_ i: Int) {
29 | let idx: Int = i / self.size
30 | let idx2: Int = i % self.size
31 | if self.bits.count <= idx {
32 | self.bits.reserveCapacity(idx)
33 | (self.bits.count...idx).forEach { _ in
34 | self.bits.append(0)
35 | }
36 | }
37 |
38 | self.bits[idx] |= (1 << idx2)
39 | }
40 |
41 | @inlinable
42 | mutating func remove(_ i: Int) {
43 | let idx: Int = i / self.size
44 | let idx2: Int = i % self.size
45 | if self.bits.count <= idx {
46 | return
47 | }
48 |
49 | self.bits[idx] &= ~(1 << idx2)
50 | }
51 |
52 | @inlinable
53 | mutating func clear() {
54 | for i in 0...size
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Sources/Kiwi/componentIterator.swift:
--------------------------------------------------------------------------------
1 | struct ComponentsIterator: Sequence, IteratorProtocol {
2 | private var entities: LazySequence<[EntityId?]>.Iterator
3 | /// (memorySize, ptrs to arrays of the different components)
4 | private var components: [(Int, UnsafePointer>)]
5 | private var i = 0
6 | private var buffer: UnsafeSequenceBuffer
7 |
8 | init(entities: LazySequence<[EntityId?]>, components: [(Int, UnsafePointer>)]) {
9 | self.entities = entities.makeIterator()
10 | self.components = components
11 | self.buffer = UnsafeSequenceBuffer(bufSize: self.components.count)
12 | }
13 |
14 | /// Returns a tuple of:
15 | /// - the entity id
16 | /// - an array (unknow-sized tuple) of type UnsafeRawPointer containing pointers to the requested components of this entity
17 | @inlinable
18 | mutating func next() -> (EntityId, UnsafeBufferPointer)? {
19 | if let id = entities.next() {
20 | if let id = id {
21 | defer { self.i += 1 }
22 | // Map to an array of bytes, which can be converted into (Component1, Component2, ...)
23 | bufferedMap(buffer: &self.buffer, self.components) { (size: Int, compColPtr: UnsafePointer>) -> UnsafeRawPointer in
24 | return compColPtr.pointee.components.withUnsafeBytes { (bufCompPtr: UnsafeRawBufferPointer) in
25 | return bufCompPtr.baseAddress.unsafelyUnwrapped + (i * size)
26 | }
27 | }
28 | return (id, UnsafeBufferPointer(self.buffer.unsafeGetFull()))
29 | } else {
30 | // when id is nil, then the entity is dead, so go to next index
31 | // (~ filter)
32 | self.i += 1
33 | return self.next()
34 | }
35 | } else {
36 | // no more entities
37 | return nil
38 | }
39 | }
40 | }
41 |
42 | struct MutableComponentsIterator: Sequence, IteratorProtocol {
43 | private var entities: LazySequence<[EntityId?]>.Iterator
44 | private var components: [(Int, UnsafeMutablePointer>)]
45 | private var i = 0
46 | private var buffer: UnsafeSequenceBuffer
47 |
48 | init(entities: LazySequence<[EntityId?]>, components: [(Int, UnsafeMutablePointer>)]) {
49 | self.entities = entities.makeIterator()
50 | self.components = components
51 | self.buffer = UnsafeSequenceBuffer(bufSize: self.components.count)
52 | }
53 |
54 | @inlinable
55 | mutating func next() -> (EntityId, UnsafeMutableBufferPointer)? {
56 | if let id = entities.next() {
57 | if let id = id {
58 | defer { self.i += 1 }
59 |
60 | bufferedMap(buffer: &self.buffer, self.components) { (size: Int, compColPtr: UnsafeMutablePointer>) -> UnsafeMutableRawPointer in
61 | return compColPtr.pointee.components.withUnsafeMutableBytes { (bufCompPtr: UnsafeMutableRawBufferPointer) in
62 | return bufCompPtr.baseAddress.unsafelyUnwrapped + (i * size)
63 | }
64 | }
65 | return (id, self.buffer.unsafeGetFull())
66 | // return (id, self.components.map { (size: Int, compColPtr: UnsafeMutablePointer>) -> UnsafeMutableRawPointer in
67 | // return compColPtr.pointee.components.withUnsafeMutableBytes { (bufPtr: UnsafeMutableRawBufferPointer) in
68 | // return bufPtr.baseAddress.unsafelyUnwrapped + (i * size)
69 | // }
70 | // })
71 | } else {
72 | self.i += 1
73 | return self.next()
74 | }
75 | } else {
76 | return nil
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/Sources/Kiwi/entity.swift:
--------------------------------------------------------------------------------
1 | public typealias EntityId = UInt32
2 |
3 | //========
4 | // Entity
5 | //========
6 |
7 | struct Entity {
8 | let archId: ArchetypeId
9 | let archRow: ArchRowId
10 |
11 | @usableFromInline
12 | init(arch archId: ArchetypeId, row: ArchRowId) {
13 | self.archId = archId
14 | self.archRow = row
15 | }
16 | }
17 |
18 | //=============
19 | // EntityStore
20 | //=============
21 |
22 | struct EntityStore {
23 | @usableFromInline
24 | var entities: [Entity]
25 | @usableFromInline
26 | var flags: [Bitmap]
27 | @usableFromInline
28 | var nextId: EntityId
29 | /// Contains all dead entity ids, i.e. available ids for the next entity
30 | @usableFromInline
31 | var deadEntities: Set
32 | }
33 |
34 | internal extension EntityStore {
35 | @usableFromInline
36 | init() {
37 | self.entities = []
38 | self.flags = []
39 | self.nextId = 0
40 | self.deadEntities = Set()
41 | }
42 |
43 | /// Gets a new id
44 | @inlinable
45 | mutating func newId() -> EntityId {
46 | if let id: EntityId = self.deadEntities.popFirst() {
47 | return id
48 | } else {
49 | let id: EntityId = self.nextId
50 | self.nextId += 1
51 | return id
52 | }
53 | }
54 |
55 | /// Spawn a new entity with the given ids
56 | @inlinable
57 | mutating func spawn(entity entId: EntityId, arch archId: ArchetypeId, archRow: ArchRowId) {
58 | if self.entities.count <= entId {
59 | self.entities.append(Entity(arch: archId, row: archRow))
60 | } else {
61 | self.entities[Int(entId)] = Entity(arch: archId, row: archRow)
62 | self.resetFlags(entity: entId)
63 | }
64 | }
65 |
66 | @inlinable
67 | func get(entity: EntityId) -> Entity {
68 | self.entities[Int(entity)]
69 | }
70 |
71 | /// Safe get
72 | @inlinable
73 | func get(entity: EntityId) -> Entity? {
74 | if self.entities.count <= entity {
75 | return nil
76 | } else {
77 | return self.entities[Int(entity)]
78 | }
79 | }
80 |
81 | /// Marks an entity as dead
82 | @inlinable
83 | mutating func kill(entity: EntityId) {
84 | self.deadEntities.insert(entity)
85 | }
86 |
87 | @inlinable
88 | func entityCount() -> Int {
89 | // specify Int() to reduce compile time
90 | return (0.. Bool {
101 | if self.flags.count <= entity {
102 | return false
103 | } else {
104 | return self.flags[Int(entity)].contains(Int(flag))
105 | }
106 | }
107 |
108 | @inlinable
109 | mutating func setFlag(entity: EntityId, _ flag: FlagId) {
110 | if self.flags.count <= entity {
111 | self.flags.reserveCapacity(Int(entity))
112 | (self.flags.count...Int(entity)).forEach { _ in
113 | self.flags.append(Bitmap())
114 | }
115 | }
116 |
117 | self.flags[Int(entity)].set(Int(flag))
118 | }
119 |
120 | @inlinable
121 | mutating func removeFlag(entity: EntityId, _ flag: FlagId) {
122 | if self.flags.count <= entity {
123 | return
124 | }
125 |
126 | self.flags[Int(entity)].remove(Int(flag))
127 | }
128 |
129 | @inlinable
130 | mutating func resetFlags(entity: EntityId) {
131 | self.flags[Int(entity)].clear()
132 | }
133 |
134 | @inlinable
135 | func entityIds() -> some Sequence {
136 | (0..(entity: EntityId, _ flag: F) -> Bool where F.RawValue == FlagId {
6 |
7 | // extension Sequence<(EntityId, UnsafeBufferPointer)> {
8 | // /// Filter a query based on an entity's flag
9 | // @inlinable
10 | // public func having(_ world: inout World, flags: F...)
11 | // -> some Sequence<(EntityId, UnsafeBufferPointer)>
12 | // where F.RawValue == FlagId
13 | // {
14 | // self.filter { (id: EntityId, _) in
15 | // flags.allSatisfy { (flag: F) in
16 | // world.hasFlag(entity: id, flag)
17 | // }
18 | // }
19 | // }
20 | // }
21 |
22 | // extension Sequence<(EntityId, UnsafeMutableBufferPointer)> {
23 | // /// Filter a query based on an entity's flag
24 | // @inlinable
25 | // public func having(_ world: inout World, flags: F...)
26 | // -> some Sequence<(EntityId, UnsafeMutableBufferPointer)>
27 | // where F.RawValue == FlagId
28 | // {
29 | // self.filter { (id: EntityId, _) in
30 | // flags.allSatisfy { (flag: F) in
31 | // world.hasFlag(entity: id, flag)
32 | // }
33 | // }
34 | // }
35 | // }
36 |
37 | // extension Sequence<(EntityId)> {
38 | // /// Filter a query based on an entity's flag
39 | // @inlinable
40 | // public func having(_ world: inout World, flags: F...)
41 | // -> some Sequence<(EntityId)>
42 | // where F.RawValue == FlagId
43 | // {
44 | // self.filter { (id: EntityId) in
45 | // flags.allSatisfy { (flag: F) in
46 | // world.hasFlag(entity: id, flag)
47 | // }
48 | // }
49 | // }
50 | // }
51 |
--------------------------------------------------------------------------------
/Sources/Kiwi/generated/spawn.swift:
--------------------------------------------------------------------------------
1 | extension World {
2 |
3 | /// Spawn a new entity with the specified components
4 | @discardableResult
5 | public mutating func spawn< Component1: Component >
6 | ( _ component1: Component1 ) -> EntityId {
7 | var ids: [ComponentId] = [ Component1.id ]
8 | ids = ids.sorted() // on separate lines for compile-time performance
9 |
10 | let archId: ArchetypeId = self.archStore.getArchetypeId(components: ids)
11 | let entId: EntityId = self.entityStore.newId()
12 | self.archStore.getMut(archetype: archId) { archPtr in
13 | let archRow = archPtr.pointee.newArchRowId(entity: entId)
14 |
15 | // register entity
16 | self.entityStore.spawn(entity: entId, arch: archId, archRow: archRow)
17 |
18 |
19 | archPtr.pointee.setComponent(row: archRow, component: component1)
20 |
21 | }
22 |
23 | return entId
24 | }
25 |
26 | /// Spawn a new entity with the specified components
27 | @discardableResult
28 | public mutating func spawn< Component1: Component , Component2: Component >
29 | ( _ component1: Component1 , _ component2: Component2 ) -> EntityId {
30 | var ids: [ComponentId] = [ Component1.id , Component2.id ]
31 | ids = ids.sorted() // on separate lines for compile-time performance
32 |
33 | let archId: ArchetypeId = self.archStore.getArchetypeId(components: ids)
34 | let entId: EntityId = self.entityStore.newId()
35 | self.archStore.getMut(archetype: archId) { archPtr in
36 | let archRow = archPtr.pointee.newArchRowId(entity: entId)
37 |
38 | // register entity
39 | self.entityStore.spawn(entity: entId, arch: archId, archRow: archRow)
40 |
41 |
42 | archPtr.pointee.setComponent(row: archRow, component: component1)
43 |
44 | archPtr.pointee.setComponent(row: archRow, component: component2)
45 |
46 | }
47 |
48 | return entId
49 | }
50 |
51 | /// Spawn a new entity with the specified components
52 | @discardableResult
53 | public mutating func spawn< Component1: Component , Component2: Component , Component3: Component >
54 | ( _ component1: Component1 , _ component2: Component2 , _ component3: Component3 ) -> EntityId {
55 | var ids: [ComponentId] = [ Component1.id , Component2.id , Component3.id ]
56 | ids = ids.sorted() // on separate lines for compile-time performance
57 |
58 | let archId: ArchetypeId = self.archStore.getArchetypeId(components: ids)
59 | let entId: EntityId = self.entityStore.newId()
60 | self.archStore.getMut(archetype: archId) { archPtr in
61 | let archRow = archPtr.pointee.newArchRowId(entity: entId)
62 |
63 | // register entity
64 | self.entityStore.spawn(entity: entId, arch: archId, archRow: archRow)
65 |
66 |
67 | archPtr.pointee.setComponent(row: archRow, component: component1)
68 |
69 | archPtr.pointee.setComponent(row: archRow, component: component2)
70 |
71 | archPtr.pointee.setComponent(row: archRow, component: component3)
72 |
73 | }
74 |
75 | return entId
76 | }
77 |
78 | /// Spawn a new entity with the specified components
79 | @discardableResult
80 | public mutating func spawn< Component1: Component , Component2: Component , Component3: Component , Component4: Component >
81 | ( _ component1: Component1 , _ component2: Component2 , _ component3: Component3 , _ component4: Component4 ) -> EntityId {
82 | var ids: [ComponentId] = [ Component1.id , Component2.id , Component3.id , Component4.id ]
83 | ids = ids.sorted() // on separate lines for compile-time performance
84 |
85 | let archId: ArchetypeId = self.archStore.getArchetypeId(components: ids)
86 | let entId: EntityId = self.entityStore.newId()
87 | self.archStore.getMut(archetype: archId) { archPtr in
88 | let archRow = archPtr.pointee.newArchRowId(entity: entId)
89 |
90 | // register entity
91 | self.entityStore.spawn(entity: entId, arch: archId, archRow: archRow)
92 |
93 |
94 | archPtr.pointee.setComponent(row: archRow, component: component1)
95 |
96 | archPtr.pointee.setComponent(row: archRow, component: component2)
97 |
98 | archPtr.pointee.setComponent(row: archRow, component: component3)
99 |
100 | archPtr.pointee.setComponent(row: archRow, component: component4)
101 |
102 | }
103 |
104 | return entId
105 | }
106 |
107 | /// Spawn a new entity with the specified components
108 | @discardableResult
109 | public mutating func spawn< Component1: Component , Component2: Component , Component3: Component , Component4: Component , Component5: Component >
110 | ( _ component1: Component1 , _ component2: Component2 , _ component3: Component3 , _ component4: Component4 , _ component5: Component5 ) -> EntityId {
111 | var ids: [ComponentId] = [ Component1.id , Component2.id , Component3.id , Component4.id , Component5.id ]
112 | ids = ids.sorted() // on separate lines for compile-time performance
113 |
114 | let archId: ArchetypeId = self.archStore.getArchetypeId(components: ids)
115 | let entId: EntityId = self.entityStore.newId()
116 | self.archStore.getMut(archetype: archId) { archPtr in
117 | let archRow = archPtr.pointee.newArchRowId(entity: entId)
118 |
119 | // register entity
120 | self.entityStore.spawn(entity: entId, arch: archId, archRow: archRow)
121 |
122 |
123 | archPtr.pointee.setComponent(row: archRow, component: component1)
124 |
125 | archPtr.pointee.setComponent(row: archRow, component: component2)
126 |
127 | archPtr.pointee.setComponent(row: archRow, component: component3)
128 |
129 | archPtr.pointee.setComponent(row: archRow, component: component4)
130 |
131 | archPtr.pointee.setComponent(row: archRow, component: component5)
132 |
133 | }
134 |
135 | return entId
136 | }
137 |
138 | /// Spawn a new entity with the specified components
139 | @discardableResult
140 | public mutating func spawn< Component1: Component , Component2: Component , Component3: Component , Component4: Component , Component5: Component , Component6: Component >
141 | ( _ component1: Component1 , _ component2: Component2 , _ component3: Component3 , _ component4: Component4 , _ component5: Component5 , _ component6: Component6 ) -> EntityId {
142 | var ids: [ComponentId] = [ Component1.id , Component2.id , Component3.id , Component4.id , Component5.id , Component6.id ]
143 | ids = ids.sorted() // on separate lines for compile-time performance
144 |
145 | let archId: ArchetypeId = self.archStore.getArchetypeId(components: ids)
146 | let entId: EntityId = self.entityStore.newId()
147 | self.archStore.getMut(archetype: archId) { archPtr in
148 | let archRow = archPtr.pointee.newArchRowId(entity: entId)
149 |
150 | // register entity
151 | self.entityStore.spawn(entity: entId, arch: archId, archRow: archRow)
152 |
153 |
154 | archPtr.pointee.setComponent(row: archRow, component: component1)
155 |
156 | archPtr.pointee.setComponent(row: archRow, component: component2)
157 |
158 | archPtr.pointee.setComponent(row: archRow, component: component3)
159 |
160 | archPtr.pointee.setComponent(row: archRow, component: component4)
161 |
162 | archPtr.pointee.setComponent(row: archRow, component: component5)
163 |
164 | archPtr.pointee.setComponent(row: archRow, component: component6)
165 |
166 | }
167 |
168 | return entId
169 | }
170 |
171 | /// Spawn a new entity with the specified components
172 | @discardableResult
173 | public mutating func spawn< Component1: Component , Component2: Component , Component3: Component , Component4: Component , Component5: Component , Component6: Component , Component7: Component >
174 | ( _ component1: Component1 , _ component2: Component2 , _ component3: Component3 , _ component4: Component4 , _ component5: Component5 , _ component6: Component6 , _ component7: Component7 ) -> EntityId {
175 | var ids: [ComponentId] = [ Component1.id , Component2.id , Component3.id , Component4.id , Component5.id , Component6.id , Component7.id ]
176 | ids = ids.sorted() // on separate lines for compile-time performance
177 |
178 | let archId: ArchetypeId = self.archStore.getArchetypeId(components: ids)
179 | let entId: EntityId = self.entityStore.newId()
180 | self.archStore.getMut(archetype: archId) { archPtr in
181 | let archRow = archPtr.pointee.newArchRowId(entity: entId)
182 |
183 | // register entity
184 | self.entityStore.spawn(entity: entId, arch: archId, archRow: archRow)
185 |
186 |
187 | archPtr.pointee.setComponent(row: archRow, component: component1)
188 |
189 | archPtr.pointee.setComponent(row: archRow, component: component2)
190 |
191 | archPtr.pointee.setComponent(row: archRow, component: component3)
192 |
193 | archPtr.pointee.setComponent(row: archRow, component: component4)
194 |
195 | archPtr.pointee.setComponent(row: archRow, component: component5)
196 |
197 | archPtr.pointee.setComponent(row: archRow, component: component6)
198 |
199 | archPtr.pointee.setComponent(row: archRow, component: component7)
200 |
201 | }
202 |
203 | return entId
204 | }
205 |
206 | /// Spawn a new entity with the specified components
207 | @discardableResult
208 | public mutating func spawn< Component1: Component , Component2: Component , Component3: Component , Component4: Component , Component5: Component , Component6: Component , Component7: Component , Component8: Component >
209 | ( _ component1: Component1 , _ component2: Component2 , _ component3: Component3 , _ component4: Component4 , _ component5: Component5 , _ component6: Component6 , _ component7: Component7 , _ component8: Component8 ) -> EntityId {
210 | var ids: [ComponentId] = [ Component1.id , Component2.id , Component3.id , Component4.id , Component5.id , Component6.id , Component7.id , Component8.id ]
211 | ids = ids.sorted() // on separate lines for compile-time performance
212 |
213 | let archId: ArchetypeId = self.archStore.getArchetypeId(components: ids)
214 | let entId: EntityId = self.entityStore.newId()
215 | self.archStore.getMut(archetype: archId) { archPtr in
216 | let archRow = archPtr.pointee.newArchRowId(entity: entId)
217 |
218 | // register entity
219 | self.entityStore.spawn(entity: entId, arch: archId, archRow: archRow)
220 |
221 |
222 | archPtr.pointee.setComponent(row: archRow, component: component1)
223 |
224 | archPtr.pointee.setComponent(row: archRow, component: component2)
225 |
226 | archPtr.pointee.setComponent(row: archRow, component: component3)
227 |
228 | archPtr.pointee.setComponent(row: archRow, component: component4)
229 |
230 | archPtr.pointee.setComponent(row: archRow, component: component5)
231 |
232 | archPtr.pointee.setComponent(row: archRow, component: component6)
233 |
234 | archPtr.pointee.setComponent(row: archRow, component: component7)
235 |
236 | archPtr.pointee.setComponent(row: archRow, component: component8)
237 |
238 | }
239 |
240 | return entId
241 | }
242 |
243 | /// Spawn a new entity with the specified components
244 | @discardableResult
245 | public mutating func spawn< Component1: Component , Component2: Component , Component3: Component , Component4: Component , Component5: Component , Component6: Component , Component7: Component , Component8: Component , Component9: Component >
246 | ( _ component1: Component1 , _ component2: Component2 , _ component3: Component3 , _ component4: Component4 , _ component5: Component5 , _ component6: Component6 , _ component7: Component7 , _ component8: Component8 , _ component9: Component9 ) -> EntityId {
247 | var ids: [ComponentId] = [ Component1.id , Component2.id , Component3.id , Component4.id , Component5.id , Component6.id , Component7.id , Component8.id , Component9.id ]
248 | ids = ids.sorted() // on separate lines for compile-time performance
249 |
250 | let archId: ArchetypeId = self.archStore.getArchetypeId(components: ids)
251 | let entId: EntityId = self.entityStore.newId()
252 | self.archStore.getMut(archetype: archId) { archPtr in
253 | let archRow = archPtr.pointee.newArchRowId(entity: entId)
254 |
255 | // register entity
256 | self.entityStore.spawn(entity: entId, arch: archId, archRow: archRow)
257 |
258 |
259 | archPtr.pointee.setComponent(row: archRow, component: component1)
260 |
261 | archPtr.pointee.setComponent(row: archRow, component: component2)
262 |
263 | archPtr.pointee.setComponent(row: archRow, component: component3)
264 |
265 | archPtr.pointee.setComponent(row: archRow, component: component4)
266 |
267 | archPtr.pointee.setComponent(row: archRow, component: component5)
268 |
269 | archPtr.pointee.setComponent(row: archRow, component: component6)
270 |
271 | archPtr.pointee.setComponent(row: archRow, component: component7)
272 |
273 | archPtr.pointee.setComponent(row: archRow, component: component8)
274 |
275 | archPtr.pointee.setComponent(row: archRow, component: component9)
276 |
277 | }
278 |
279 | return entId
280 | }
281 |
282 | /// Spawn a new entity with the specified components
283 | @discardableResult
284 | public mutating func spawn< Component1: Component , Component2: Component , Component3: Component , Component4: Component , Component5: Component , Component6: Component , Component7: Component , Component8: Component , Component9: Component , Component10: Component >
285 | ( _ component1: Component1 , _ component2: Component2 , _ component3: Component3 , _ component4: Component4 , _ component5: Component5 , _ component6: Component6 , _ component7: Component7 , _ component8: Component8 , _ component9: Component9 , _ component10: Component10 ) -> EntityId {
286 | var ids: [ComponentId] = [ Component1.id , Component2.id , Component3.id , Component4.id , Component5.id , Component6.id , Component7.id , Component8.id , Component9.id , Component10.id ]
287 | ids = ids.sorted() // on separate lines for compile-time performance
288 |
289 | let archId: ArchetypeId = self.archStore.getArchetypeId(components: ids)
290 | let entId: EntityId = self.entityStore.newId()
291 | self.archStore.getMut(archetype: archId) { archPtr in
292 | let archRow = archPtr.pointee.newArchRowId(entity: entId)
293 |
294 | // register entity
295 | self.entityStore.spawn(entity: entId, arch: archId, archRow: archRow)
296 |
297 |
298 | archPtr.pointee.setComponent(row: archRow, component: component1)
299 |
300 | archPtr.pointee.setComponent(row: archRow, component: component2)
301 |
302 | archPtr.pointee.setComponent(row: archRow, component: component3)
303 |
304 | archPtr.pointee.setComponent(row: archRow, component: component4)
305 |
306 | archPtr.pointee.setComponent(row: archRow, component: component5)
307 |
308 | archPtr.pointee.setComponent(row: archRow, component: component6)
309 |
310 | archPtr.pointee.setComponent(row: archRow, component: component7)
311 |
312 | archPtr.pointee.setComponent(row: archRow, component: component8)
313 |
314 | archPtr.pointee.setComponent(row: archRow, component: component9)
315 |
316 | archPtr.pointee.setComponent(row: archRow, component: component10)
317 |
318 | }
319 |
320 | return entId
321 | }
322 |
323 | /// Spawn a new entity with the specified components
324 | @discardableResult
325 | public mutating func spawn< Component1: Component , Component2: Component , Component3: Component , Component4: Component , Component5: Component , Component6: Component , Component7: Component , Component8: Component , Component9: Component , Component10: Component , Component11: Component >
326 | ( _ component1: Component1 , _ component2: Component2 , _ component3: Component3 , _ component4: Component4 , _ component5: Component5 , _ component6: Component6 , _ component7: Component7 , _ component8: Component8 , _ component9: Component9 , _ component10: Component10 , _ component11: Component11 ) -> EntityId {
327 | var ids: [ComponentId] = [ Component1.id , Component2.id , Component3.id , Component4.id , Component5.id , Component6.id , Component7.id , Component8.id , Component9.id , Component10.id , Component11.id ]
328 | ids = ids.sorted() // on separate lines for compile-time performance
329 |
330 | let archId: ArchetypeId = self.archStore.getArchetypeId(components: ids)
331 | let entId: EntityId = self.entityStore.newId()
332 | self.archStore.getMut(archetype: archId) { archPtr in
333 | let archRow = archPtr.pointee.newArchRowId(entity: entId)
334 |
335 | // register entity
336 | self.entityStore.spawn(entity: entId, arch: archId, archRow: archRow)
337 |
338 |
339 | archPtr.pointee.setComponent(row: archRow, component: component1)
340 |
341 | archPtr.pointee.setComponent(row: archRow, component: component2)
342 |
343 | archPtr.pointee.setComponent(row: archRow, component: component3)
344 |
345 | archPtr.pointee.setComponent(row: archRow, component: component4)
346 |
347 | archPtr.pointee.setComponent(row: archRow, component: component5)
348 |
349 | archPtr.pointee.setComponent(row: archRow, component: component6)
350 |
351 | archPtr.pointee.setComponent(row: archRow, component: component7)
352 |
353 | archPtr.pointee.setComponent(row: archRow, component: component8)
354 |
355 | archPtr.pointee.setComponent(row: archRow, component: component9)
356 |
357 | archPtr.pointee.setComponent(row: archRow, component: component10)
358 |
359 | archPtr.pointee.setComponent(row: archRow, component: component11)
360 |
361 | }
362 |
363 | return entId
364 | }
365 |
366 | /// Spawn a new entity with the specified components
367 | @discardableResult
368 | public mutating func spawn< Component1: Component , Component2: Component , Component3: Component , Component4: Component , Component5: Component , Component6: Component , Component7: Component , Component8: Component , Component9: Component , Component10: Component , Component11: Component , Component12: Component >
369 | ( _ component1: Component1 , _ component2: Component2 , _ component3: Component3 , _ component4: Component4 , _ component5: Component5 , _ component6: Component6 , _ component7: Component7 , _ component8: Component8 , _ component9: Component9 , _ component10: Component10 , _ component11: Component11 , _ component12: Component12 ) -> EntityId {
370 | var ids: [ComponentId] = [ Component1.id , Component2.id , Component3.id , Component4.id , Component5.id , Component6.id , Component7.id , Component8.id , Component9.id , Component10.id , Component11.id , Component12.id ]
371 | ids = ids.sorted() // on separate lines for compile-time performance
372 |
373 | let archId: ArchetypeId = self.archStore.getArchetypeId(components: ids)
374 | let entId: EntityId = self.entityStore.newId()
375 | self.archStore.getMut(archetype: archId) { archPtr in
376 | let archRow = archPtr.pointee.newArchRowId(entity: entId)
377 |
378 | // register entity
379 | self.entityStore.spawn(entity: entId, arch: archId, archRow: archRow)
380 |
381 |
382 | archPtr.pointee.setComponent(row: archRow, component: component1)
383 |
384 | archPtr.pointee.setComponent(row: archRow, component: component2)
385 |
386 | archPtr.pointee.setComponent(row: archRow, component: component3)
387 |
388 | archPtr.pointee.setComponent(row: archRow, component: component4)
389 |
390 | archPtr.pointee.setComponent(row: archRow, component: component5)
391 |
392 | archPtr.pointee.setComponent(row: archRow, component: component6)
393 |
394 | archPtr.pointee.setComponent(row: archRow, component: component7)
395 |
396 | archPtr.pointee.setComponent(row: archRow, component: component8)
397 |
398 | archPtr.pointee.setComponent(row: archRow, component: component9)
399 |
400 | archPtr.pointee.setComponent(row: archRow, component: component10)
401 |
402 | archPtr.pointee.setComponent(row: archRow, component: component11)
403 |
404 | archPtr.pointee.setComponent(row: archRow, component: component12)
405 |
406 | }
407 |
408 | return entId
409 | }
410 |
411 | /// Spawn a new entity with the specified components
412 | @discardableResult
413 | public mutating func spawn< Component1: Component , Component2: Component , Component3: Component , Component4: Component , Component5: Component , Component6: Component , Component7: Component , Component8: Component , Component9: Component , Component10: Component , Component11: Component , Component12: Component , Component13: Component >
414 | ( _ component1: Component1 , _ component2: Component2 , _ component3: Component3 , _ component4: Component4 , _ component5: Component5 , _ component6: Component6 , _ component7: Component7 , _ component8: Component8 , _ component9: Component9 , _ component10: Component10 , _ component11: Component11 , _ component12: Component12 , _ component13: Component13 ) -> EntityId {
415 | var ids: [ComponentId] = [ Component1.id , Component2.id , Component3.id , Component4.id , Component5.id , Component6.id , Component7.id , Component8.id , Component9.id , Component10.id , Component11.id , Component12.id , Component13.id ]
416 | ids = ids.sorted() // on separate lines for compile-time performance
417 |
418 | let archId: ArchetypeId = self.archStore.getArchetypeId(components: ids)
419 | let entId: EntityId = self.entityStore.newId()
420 | self.archStore.getMut(archetype: archId) { archPtr in
421 | let archRow = archPtr.pointee.newArchRowId(entity: entId)
422 |
423 | // register entity
424 | self.entityStore.spawn(entity: entId, arch: archId, archRow: archRow)
425 |
426 |
427 | archPtr.pointee.setComponent(row: archRow, component: component1)
428 |
429 | archPtr.pointee.setComponent(row: archRow, component: component2)
430 |
431 | archPtr.pointee.setComponent(row: archRow, component: component3)
432 |
433 | archPtr.pointee.setComponent(row: archRow, component: component4)
434 |
435 | archPtr.pointee.setComponent(row: archRow, component: component5)
436 |
437 | archPtr.pointee.setComponent(row: archRow, component: component6)
438 |
439 | archPtr.pointee.setComponent(row: archRow, component: component7)
440 |
441 | archPtr.pointee.setComponent(row: archRow, component: component8)
442 |
443 | archPtr.pointee.setComponent(row: archRow, component: component9)
444 |
445 | archPtr.pointee.setComponent(row: archRow, component: component10)
446 |
447 | archPtr.pointee.setComponent(row: archRow, component: component11)
448 |
449 | archPtr.pointee.setComponent(row: archRow, component: component12)
450 |
451 | archPtr.pointee.setComponent(row: archRow, component: component13)
452 |
453 | }
454 |
455 | return entId
456 | }
457 |
458 | /// Spawn a new entity with the specified components
459 | @discardableResult
460 | public mutating func spawn< Component1: Component , Component2: Component , Component3: Component , Component4: Component , Component5: Component , Component6: Component , Component7: Component , Component8: Component , Component9: Component , Component10: Component , Component11: Component , Component12: Component , Component13: Component , Component14: Component >
461 | ( _ component1: Component1 , _ component2: Component2 , _ component3: Component3 , _ component4: Component4 , _ component5: Component5 , _ component6: Component6 , _ component7: Component7 , _ component8: Component8 , _ component9: Component9 , _ component10: Component10 , _ component11: Component11 , _ component12: Component12 , _ component13: Component13 , _ component14: Component14 ) -> EntityId {
462 | var ids: [ComponentId] = [ Component1.id , Component2.id , Component3.id , Component4.id , Component5.id , Component6.id , Component7.id , Component8.id , Component9.id , Component10.id , Component11.id , Component12.id , Component13.id , Component14.id ]
463 | ids = ids.sorted() // on separate lines for compile-time performance
464 |
465 | let archId: ArchetypeId = self.archStore.getArchetypeId(components: ids)
466 | let entId: EntityId = self.entityStore.newId()
467 | self.archStore.getMut(archetype: archId) { archPtr in
468 | let archRow = archPtr.pointee.newArchRowId(entity: entId)
469 |
470 | // register entity
471 | self.entityStore.spawn(entity: entId, arch: archId, archRow: archRow)
472 |
473 |
474 | archPtr.pointee.setComponent(row: archRow, component: component1)
475 |
476 | archPtr.pointee.setComponent(row: archRow, component: component2)
477 |
478 | archPtr.pointee.setComponent(row: archRow, component: component3)
479 |
480 | archPtr.pointee.setComponent(row: archRow, component: component4)
481 |
482 | archPtr.pointee.setComponent(row: archRow, component: component5)
483 |
484 | archPtr.pointee.setComponent(row: archRow, component: component6)
485 |
486 | archPtr.pointee.setComponent(row: archRow, component: component7)
487 |
488 | archPtr.pointee.setComponent(row: archRow, component: component8)
489 |
490 | archPtr.pointee.setComponent(row: archRow, component: component9)
491 |
492 | archPtr.pointee.setComponent(row: archRow, component: component10)
493 |
494 | archPtr.pointee.setComponent(row: archRow, component: component11)
495 |
496 | archPtr.pointee.setComponent(row: archRow, component: component12)
497 |
498 | archPtr.pointee.setComponent(row: archRow, component: component13)
499 |
500 | archPtr.pointee.setComponent(row: archRow, component: component14)
501 |
502 | }
503 |
504 | return entId
505 | }
506 |
507 | /// Spawn a new entity with the specified components
508 | @discardableResult
509 | public mutating func spawn< Component1: Component , Component2: Component , Component3: Component , Component4: Component , Component5: Component , Component6: Component , Component7: Component , Component8: Component , Component9: Component , Component10: Component , Component11: Component , Component12: Component , Component13: Component , Component14: Component , Component15: Component >
510 | ( _ component1: Component1 , _ component2: Component2 , _ component3: Component3 , _ component4: Component4 , _ component5: Component5 , _ component6: Component6 , _ component7: Component7 , _ component8: Component8 , _ component9: Component9 , _ component10: Component10 , _ component11: Component11 , _ component12: Component12 , _ component13: Component13 , _ component14: Component14 , _ component15: Component15 ) -> EntityId {
511 | var ids: [ComponentId] = [ Component1.id , Component2.id , Component3.id , Component4.id , Component5.id , Component6.id , Component7.id , Component8.id , Component9.id , Component10.id , Component11.id , Component12.id , Component13.id , Component14.id , Component15.id ]
512 | ids = ids.sorted() // on separate lines for compile-time performance
513 |
514 | let archId: ArchetypeId = self.archStore.getArchetypeId(components: ids)
515 | let entId: EntityId = self.entityStore.newId()
516 | self.archStore.getMut(archetype: archId) { archPtr in
517 | let archRow = archPtr.pointee.newArchRowId(entity: entId)
518 |
519 | // register entity
520 | self.entityStore.spawn(entity: entId, arch: archId, archRow: archRow)
521 |
522 |
523 | archPtr.pointee.setComponent(row: archRow, component: component1)
524 |
525 | archPtr.pointee.setComponent(row: archRow, component: component2)
526 |
527 | archPtr.pointee.setComponent(row: archRow, component: component3)
528 |
529 | archPtr.pointee.setComponent(row: archRow, component: component4)
530 |
531 | archPtr.pointee.setComponent(row: archRow, component: component5)
532 |
533 | archPtr.pointee.setComponent(row: archRow, component: component6)
534 |
535 | archPtr.pointee.setComponent(row: archRow, component: component7)
536 |
537 | archPtr.pointee.setComponent(row: archRow, component: component8)
538 |
539 | archPtr.pointee.setComponent(row: archRow, component: component9)
540 |
541 | archPtr.pointee.setComponent(row: archRow, component: component10)
542 |
543 | archPtr.pointee.setComponent(row: archRow, component: component11)
544 |
545 | archPtr.pointee.setComponent(row: archRow, component: component12)
546 |
547 | archPtr.pointee.setComponent(row: archRow, component: component13)
548 |
549 | archPtr.pointee.setComponent(row: archRow, component: component14)
550 |
551 | archPtr.pointee.setComponent(row: archRow, component: component15)
552 |
553 | }
554 |
555 | return entId
556 | }
557 |
558 | /// Spawn a new entity with the specified components
559 | @discardableResult
560 | public mutating func spawn< Component1: Component , Component2: Component , Component3: Component , Component4: Component , Component5: Component , Component6: Component , Component7: Component , Component8: Component , Component9: Component , Component10: Component , Component11: Component , Component12: Component , Component13: Component , Component14: Component , Component15: Component , Component16: Component >
561 | ( _ component1: Component1 , _ component2: Component2 , _ component3: Component3 , _ component4: Component4 , _ component5: Component5 , _ component6: Component6 , _ component7: Component7 , _ component8: Component8 , _ component9: Component9 , _ component10: Component10 , _ component11: Component11 , _ component12: Component12 , _ component13: Component13 , _ component14: Component14 , _ component15: Component15 , _ component16: Component16 ) -> EntityId {
562 | var ids: [ComponentId] = [ Component1.id , Component2.id , Component3.id , Component4.id , Component5.id , Component6.id , Component7.id , Component8.id , Component9.id , Component10.id , Component11.id , Component12.id , Component13.id , Component14.id , Component15.id , Component16.id ]
563 | ids = ids.sorted() // on separate lines for compile-time performance
564 |
565 | let archId: ArchetypeId = self.archStore.getArchetypeId(components: ids)
566 | let entId: EntityId = self.entityStore.newId()
567 | self.archStore.getMut(archetype: archId) { archPtr in
568 | let archRow = archPtr.pointee.newArchRowId(entity: entId)
569 |
570 | // register entity
571 | self.entityStore.spawn(entity: entId, arch: archId, archRow: archRow)
572 |
573 |
574 | archPtr.pointee.setComponent(row: archRow, component: component1)
575 |
576 | archPtr.pointee.setComponent(row: archRow, component: component2)
577 |
578 | archPtr.pointee.setComponent(row: archRow, component: component3)
579 |
580 | archPtr.pointee.setComponent(row: archRow, component: component4)
581 |
582 | archPtr.pointee.setComponent(row: archRow, component: component5)
583 |
584 | archPtr.pointee.setComponent(row: archRow, component: component6)
585 |
586 | archPtr.pointee.setComponent(row: archRow, component: component7)
587 |
588 | archPtr.pointee.setComponent(row: archRow, component: component8)
589 |
590 | archPtr.pointee.setComponent(row: archRow, component: component9)
591 |
592 | archPtr.pointee.setComponent(row: archRow, component: component10)
593 |
594 | archPtr.pointee.setComponent(row: archRow, component: component11)
595 |
596 | archPtr.pointee.setComponent(row: archRow, component: component12)
597 |
598 | archPtr.pointee.setComponent(row: archRow, component: component13)
599 |
600 | archPtr.pointee.setComponent(row: archRow, component: component14)
601 |
602 | archPtr.pointee.setComponent(row: archRow, component: component15)
603 |
604 | archPtr.pointee.setComponent(row: archRow, component: component16)
605 |
606 | }
607 |
608 | return entId
609 | }
610 |
611 | /// Spawn a new entity with the specified components
612 | @discardableResult
613 | public mutating func spawn< Component1: Component , Component2: Component , Component3: Component , Component4: Component , Component5: Component , Component6: Component , Component7: Component , Component8: Component , Component9: Component , Component10: Component , Component11: Component , Component12: Component , Component13: Component , Component14: Component , Component15: Component , Component16: Component , Component17: Component >
614 | ( _ component1: Component1 , _ component2: Component2 , _ component3: Component3 , _ component4: Component4 , _ component5: Component5 , _ component6: Component6 , _ component7: Component7 , _ component8: Component8 , _ component9: Component9 , _ component10: Component10 , _ component11: Component11 , _ component12: Component12 , _ component13: Component13 , _ component14: Component14 , _ component15: Component15 , _ component16: Component16 , _ component17: Component17 ) -> EntityId {
615 | var ids: [ComponentId] = [ Component1.id , Component2.id , Component3.id , Component4.id , Component5.id , Component6.id , Component7.id , Component8.id , Component9.id , Component10.id , Component11.id , Component12.id , Component13.id , Component14.id , Component15.id , Component16.id , Component17.id ]
616 | ids = ids.sorted() // on separate lines for compile-time performance
617 |
618 | let archId: ArchetypeId = self.archStore.getArchetypeId(components: ids)
619 | let entId: EntityId = self.entityStore.newId()
620 | self.archStore.getMut(archetype: archId) { archPtr in
621 | let archRow = archPtr.pointee.newArchRowId(entity: entId)
622 |
623 | // register entity
624 | self.entityStore.spawn(entity: entId, arch: archId, archRow: archRow)
625 |
626 |
627 | archPtr.pointee.setComponent(row: archRow, component: component1)
628 |
629 | archPtr.pointee.setComponent(row: archRow, component: component2)
630 |
631 | archPtr.pointee.setComponent(row: archRow, component: component3)
632 |
633 | archPtr.pointee.setComponent(row: archRow, component: component4)
634 |
635 | archPtr.pointee.setComponent(row: archRow, component: component5)
636 |
637 | archPtr.pointee.setComponent(row: archRow, component: component6)
638 |
639 | archPtr.pointee.setComponent(row: archRow, component: component7)
640 |
641 | archPtr.pointee.setComponent(row: archRow, component: component8)
642 |
643 | archPtr.pointee.setComponent(row: archRow, component: component9)
644 |
645 | archPtr.pointee.setComponent(row: archRow, component: component10)
646 |
647 | archPtr.pointee.setComponent(row: archRow, component: component11)
648 |
649 | archPtr.pointee.setComponent(row: archRow, component: component12)
650 |
651 | archPtr.pointee.setComponent(row: archRow, component: component13)
652 |
653 | archPtr.pointee.setComponent(row: archRow, component: component14)
654 |
655 | archPtr.pointee.setComponent(row: archRow, component: component15)
656 |
657 | archPtr.pointee.setComponent(row: archRow, component: component16)
658 |
659 | archPtr.pointee.setComponent(row: archRow, component: component17)
660 |
661 | }
662 |
663 | return entId
664 | }
665 |
666 | /// Spawn a new entity with the specified components
667 | @discardableResult
668 | public mutating func spawn< Component1: Component , Component2: Component , Component3: Component , Component4: Component , Component5: Component , Component6: Component , Component7: Component , Component8: Component , Component9: Component , Component10: Component , Component11: Component , Component12: Component , Component13: Component , Component14: Component , Component15: Component , Component16: Component , Component17: Component , Component18: Component >
669 | ( _ component1: Component1 , _ component2: Component2 , _ component3: Component3 , _ component4: Component4 , _ component5: Component5 , _ component6: Component6 , _ component7: Component7 , _ component8: Component8 , _ component9: Component9 , _ component10: Component10 , _ component11: Component11 , _ component12: Component12 , _ component13: Component13 , _ component14: Component14 , _ component15: Component15 , _ component16: Component16 , _ component17: Component17 , _ component18: Component18 ) -> EntityId {
670 | var ids: [ComponentId] = [ Component1.id , Component2.id , Component3.id , Component4.id , Component5.id , Component6.id , Component7.id , Component8.id , Component9.id , Component10.id , Component11.id , Component12.id , Component13.id , Component14.id , Component15.id , Component16.id , Component17.id , Component18.id ]
671 | ids = ids.sorted() // on separate lines for compile-time performance
672 |
673 | let archId: ArchetypeId = self.archStore.getArchetypeId(components: ids)
674 | let entId: EntityId = self.entityStore.newId()
675 | self.archStore.getMut(archetype: archId) { archPtr in
676 | let archRow = archPtr.pointee.newArchRowId(entity: entId)
677 |
678 | // register entity
679 | self.entityStore.spawn(entity: entId, arch: archId, archRow: archRow)
680 |
681 |
682 | archPtr.pointee.setComponent(row: archRow, component: component1)
683 |
684 | archPtr.pointee.setComponent(row: archRow, component: component2)
685 |
686 | archPtr.pointee.setComponent(row: archRow, component: component3)
687 |
688 | archPtr.pointee.setComponent(row: archRow, component: component4)
689 |
690 | archPtr.pointee.setComponent(row: archRow, component: component5)
691 |
692 | archPtr.pointee.setComponent(row: archRow, component: component6)
693 |
694 | archPtr.pointee.setComponent(row: archRow, component: component7)
695 |
696 | archPtr.pointee.setComponent(row: archRow, component: component8)
697 |
698 | archPtr.pointee.setComponent(row: archRow, component: component9)
699 |
700 | archPtr.pointee.setComponent(row: archRow, component: component10)
701 |
702 | archPtr.pointee.setComponent(row: archRow, component: component11)
703 |
704 | archPtr.pointee.setComponent(row: archRow, component: component12)
705 |
706 | archPtr.pointee.setComponent(row: archRow, component: component13)
707 |
708 | archPtr.pointee.setComponent(row: archRow, component: component14)
709 |
710 | archPtr.pointee.setComponent(row: archRow, component: component15)
711 |
712 | archPtr.pointee.setComponent(row: archRow, component: component16)
713 |
714 | archPtr.pointee.setComponent(row: archRow, component: component17)
715 |
716 | archPtr.pointee.setComponent(row: archRow, component: component18)
717 |
718 | }
719 |
720 | return entId
721 | }
722 |
723 | /// Spawn a new entity with the specified components
724 | @discardableResult
725 | public mutating func spawn< Component1: Component , Component2: Component , Component3: Component , Component4: Component , Component5: Component , Component6: Component , Component7: Component , Component8: Component , Component9: Component , Component10: Component , Component11: Component , Component12: Component , Component13: Component , Component14: Component , Component15: Component , Component16: Component , Component17: Component , Component18: Component , Component19: Component >
726 | ( _ component1: Component1 , _ component2: Component2 , _ component3: Component3 , _ component4: Component4 , _ component5: Component5 , _ component6: Component6 , _ component7: Component7 , _ component8: Component8 , _ component9: Component9 , _ component10: Component10 , _ component11: Component11 , _ component12: Component12 , _ component13: Component13 , _ component14: Component14 , _ component15: Component15 , _ component16: Component16 , _ component17: Component17 , _ component18: Component18 , _ component19: Component19 ) -> EntityId {
727 | var ids: [ComponentId] = [ Component1.id , Component2.id , Component3.id , Component4.id , Component5.id , Component6.id , Component7.id , Component8.id , Component9.id , Component10.id , Component11.id , Component12.id , Component13.id , Component14.id , Component15.id , Component16.id , Component17.id , Component18.id , Component19.id ]
728 | ids = ids.sorted() // on separate lines for compile-time performance
729 |
730 | let archId: ArchetypeId = self.archStore.getArchetypeId(components: ids)
731 | let entId: EntityId = self.entityStore.newId()
732 | self.archStore.getMut(archetype: archId) { archPtr in
733 | let archRow = archPtr.pointee.newArchRowId(entity: entId)
734 |
735 | // register entity
736 | self.entityStore.spawn(entity: entId, arch: archId, archRow: archRow)
737 |
738 |
739 | archPtr.pointee.setComponent(row: archRow, component: component1)
740 |
741 | archPtr.pointee.setComponent(row: archRow, component: component2)
742 |
743 | archPtr.pointee.setComponent(row: archRow, component: component3)
744 |
745 | archPtr.pointee.setComponent(row: archRow, component: component4)
746 |
747 | archPtr.pointee.setComponent(row: archRow, component: component5)
748 |
749 | archPtr.pointee.setComponent(row: archRow, component: component6)
750 |
751 | archPtr.pointee.setComponent(row: archRow, component: component7)
752 |
753 | archPtr.pointee.setComponent(row: archRow, component: component8)
754 |
755 | archPtr.pointee.setComponent(row: archRow, component: component9)
756 |
757 | archPtr.pointee.setComponent(row: archRow, component: component10)
758 |
759 | archPtr.pointee.setComponent(row: archRow, component: component11)
760 |
761 | archPtr.pointee.setComponent(row: archRow, component: component12)
762 |
763 | archPtr.pointee.setComponent(row: archRow, component: component13)
764 |
765 | archPtr.pointee.setComponent(row: archRow, component: component14)
766 |
767 | archPtr.pointee.setComponent(row: archRow, component: component15)
768 |
769 | archPtr.pointee.setComponent(row: archRow, component: component16)
770 |
771 | archPtr.pointee.setComponent(row: archRow, component: component17)
772 |
773 | archPtr.pointee.setComponent(row: archRow, component: component18)
774 |
775 | archPtr.pointee.setComponent(row: archRow, component: component19)
776 |
777 | }
778 |
779 | return entId
780 | }
781 |
782 | /// Spawn a new entity with the specified components
783 | @discardableResult
784 | public mutating func spawn< Component1: Component , Component2: Component , Component3: Component , Component4: Component , Component5: Component , Component6: Component , Component7: Component , Component8: Component , Component9: Component , Component10: Component , Component11: Component , Component12: Component , Component13: Component , Component14: Component , Component15: Component , Component16: Component , Component17: Component , Component18: Component , Component19: Component , Component20: Component >
785 | ( _ component1: Component1 , _ component2: Component2 , _ component3: Component3 , _ component4: Component4 , _ component5: Component5 , _ component6: Component6 , _ component7: Component7 , _ component8: Component8 , _ component9: Component9 , _ component10: Component10 , _ component11: Component11 , _ component12: Component12 , _ component13: Component13 , _ component14: Component14 , _ component15: Component15 , _ component16: Component16 , _ component17: Component17 , _ component18: Component18 , _ component19: Component19 , _ component20: Component20 ) -> EntityId {
786 | var ids: [ComponentId] = [ Component1.id , Component2.id , Component3.id , Component4.id , Component5.id , Component6.id , Component7.id , Component8.id , Component9.id , Component10.id , Component11.id , Component12.id , Component13.id , Component14.id , Component15.id , Component16.id , Component17.id , Component18.id , Component19.id , Component20.id ]
787 | ids = ids.sorted() // on separate lines for compile-time performance
788 |
789 | let archId: ArchetypeId = self.archStore.getArchetypeId(components: ids)
790 | let entId: EntityId = self.entityStore.newId()
791 | self.archStore.getMut(archetype: archId) { archPtr in
792 | let archRow = archPtr.pointee.newArchRowId(entity: entId)
793 |
794 | // register entity
795 | self.entityStore.spawn(entity: entId, arch: archId, archRow: archRow)
796 |
797 |
798 | archPtr.pointee.setComponent(row: archRow, component: component1)
799 |
800 | archPtr.pointee.setComponent(row: archRow, component: component2)
801 |
802 | archPtr.pointee.setComponent(row: archRow, component: component3)
803 |
804 | archPtr.pointee.setComponent(row: archRow, component: component4)
805 |
806 | archPtr.pointee.setComponent(row: archRow, component: component5)
807 |
808 | archPtr.pointee.setComponent(row: archRow, component: component6)
809 |
810 | archPtr.pointee.setComponent(row: archRow, component: component7)
811 |
812 | archPtr.pointee.setComponent(row: archRow, component: component8)
813 |
814 | archPtr.pointee.setComponent(row: archRow, component: component9)
815 |
816 | archPtr.pointee.setComponent(row: archRow, component: component10)
817 |
818 | archPtr.pointee.setComponent(row: archRow, component: component11)
819 |
820 | archPtr.pointee.setComponent(row: archRow, component: component12)
821 |
822 | archPtr.pointee.setComponent(row: archRow, component: component13)
823 |
824 | archPtr.pointee.setComponent(row: archRow, component: component14)
825 |
826 | archPtr.pointee.setComponent(row: archRow, component: component15)
827 |
828 | archPtr.pointee.setComponent(row: archRow, component: component16)
829 |
830 | archPtr.pointee.setComponent(row: archRow, component: component17)
831 |
832 | archPtr.pointee.setComponent(row: archRow, component: component18)
833 |
834 | archPtr.pointee.setComponent(row: archRow, component: component19)
835 |
836 | archPtr.pointee.setComponent(row: archRow, component: component20)
837 |
838 | }
839 |
840 | return entId
841 | }
842 |
843 | /// Spawn a new entity with the specified components
844 | @discardableResult
845 | public mutating func spawn< Component1: Component , Component2: Component , Component3: Component , Component4: Component , Component5: Component , Component6: Component , Component7: Component , Component8: Component , Component9: Component , Component10: Component , Component11: Component , Component12: Component , Component13: Component , Component14: Component , Component15: Component , Component16: Component , Component17: Component , Component18: Component , Component19: Component , Component20: Component , Component21: Component >
846 | ( _ component1: Component1 , _ component2: Component2 , _ component3: Component3 , _ component4: Component4 , _ component5: Component5 , _ component6: Component6 , _ component7: Component7 , _ component8: Component8 , _ component9: Component9 , _ component10: Component10 , _ component11: Component11 , _ component12: Component12 , _ component13: Component13 , _ component14: Component14 , _ component15: Component15 , _ component16: Component16 , _ component17: Component17 , _ component18: Component18 , _ component19: Component19 , _ component20: Component20 , _ component21: Component21 ) -> EntityId {
847 | var ids: [ComponentId] = [ Component1.id , Component2.id , Component3.id , Component4.id , Component5.id , Component6.id , Component7.id , Component8.id , Component9.id , Component10.id , Component11.id , Component12.id , Component13.id , Component14.id , Component15.id , Component16.id , Component17.id , Component18.id , Component19.id , Component20.id , Component21.id ]
848 | ids = ids.sorted() // on separate lines for compile-time performance
849 |
850 | let archId: ArchetypeId = self.archStore.getArchetypeId(components: ids)
851 | let entId: EntityId = self.entityStore.newId()
852 | self.archStore.getMut(archetype: archId) { archPtr in
853 | let archRow = archPtr.pointee.newArchRowId(entity: entId)
854 |
855 | // register entity
856 | self.entityStore.spawn(entity: entId, arch: archId, archRow: archRow)
857 |
858 |
859 | archPtr.pointee.setComponent(row: archRow, component: component1)
860 |
861 | archPtr.pointee.setComponent(row: archRow, component: component2)
862 |
863 | archPtr.pointee.setComponent(row: archRow, component: component3)
864 |
865 | archPtr.pointee.setComponent(row: archRow, component: component4)
866 |
867 | archPtr.pointee.setComponent(row: archRow, component: component5)
868 |
869 | archPtr.pointee.setComponent(row: archRow, component: component6)
870 |
871 | archPtr.pointee.setComponent(row: archRow, component: component7)
872 |
873 | archPtr.pointee.setComponent(row: archRow, component: component8)
874 |
875 | archPtr.pointee.setComponent(row: archRow, component: component9)
876 |
877 | archPtr.pointee.setComponent(row: archRow, component: component10)
878 |
879 | archPtr.pointee.setComponent(row: archRow, component: component11)
880 |
881 | archPtr.pointee.setComponent(row: archRow, component: component12)
882 |
883 | archPtr.pointee.setComponent(row: archRow, component: component13)
884 |
885 | archPtr.pointee.setComponent(row: archRow, component: component14)
886 |
887 | archPtr.pointee.setComponent(row: archRow, component: component15)
888 |
889 | archPtr.pointee.setComponent(row: archRow, component: component16)
890 |
891 | archPtr.pointee.setComponent(row: archRow, component: component17)
892 |
893 | archPtr.pointee.setComponent(row: archRow, component: component18)
894 |
895 | archPtr.pointee.setComponent(row: archRow, component: component19)
896 |
897 | archPtr.pointee.setComponent(row: archRow, component: component20)
898 |
899 | archPtr.pointee.setComponent(row: archRow, component: component21)
900 |
901 | }
902 |
903 | return entId
904 | }
905 |
906 | /// Spawn a new entity with the specified components
907 | @discardableResult
908 | public mutating func spawn< Component1: Component , Component2: Component , Component3: Component , Component4: Component , Component5: Component , Component6: Component , Component7: Component , Component8: Component , Component9: Component , Component10: Component , Component11: Component , Component12: Component , Component13: Component , Component14: Component , Component15: Component , Component16: Component , Component17: Component , Component18: Component , Component19: Component , Component20: Component , Component21: Component , Component22: Component >
909 | ( _ component1: Component1 , _ component2: Component2 , _ component3: Component3 , _ component4: Component4 , _ component5: Component5 , _ component6: Component6 , _ component7: Component7 , _ component8: Component8 , _ component9: Component9 , _ component10: Component10 , _ component11: Component11 , _ component12: Component12 , _ component13: Component13 , _ component14: Component14 , _ component15: Component15 , _ component16: Component16 , _ component17: Component17 , _ component18: Component18 , _ component19: Component19 , _ component20: Component20 , _ component21: Component21 , _ component22: Component22 ) -> EntityId {
910 | var ids: [ComponentId] = [ Component1.id , Component2.id , Component3.id , Component4.id , Component5.id , Component6.id , Component7.id , Component8.id , Component9.id , Component10.id , Component11.id , Component12.id , Component13.id , Component14.id , Component15.id , Component16.id , Component17.id , Component18.id , Component19.id , Component20.id , Component21.id , Component22.id ]
911 | ids = ids.sorted() // on separate lines for compile-time performance
912 |
913 | let archId: ArchetypeId = self.archStore.getArchetypeId(components: ids)
914 | let entId: EntityId = self.entityStore.newId()
915 | self.archStore.getMut(archetype: archId) { archPtr in
916 | let archRow = archPtr.pointee.newArchRowId(entity: entId)
917 |
918 | // register entity
919 | self.entityStore.spawn(entity: entId, arch: archId, archRow: archRow)
920 |
921 |
922 | archPtr.pointee.setComponent(row: archRow, component: component1)
923 |
924 | archPtr.pointee.setComponent(row: archRow, component: component2)
925 |
926 | archPtr.pointee.setComponent(row: archRow, component: component3)
927 |
928 | archPtr.pointee.setComponent(row: archRow, component: component4)
929 |
930 | archPtr.pointee.setComponent(row: archRow, component: component5)
931 |
932 | archPtr.pointee.setComponent(row: archRow, component: component6)
933 |
934 | archPtr.pointee.setComponent(row: archRow, component: component7)
935 |
936 | archPtr.pointee.setComponent(row: archRow, component: component8)
937 |
938 | archPtr.pointee.setComponent(row: archRow, component: component9)
939 |
940 | archPtr.pointee.setComponent(row: archRow, component: component10)
941 |
942 | archPtr.pointee.setComponent(row: archRow, component: component11)
943 |
944 | archPtr.pointee.setComponent(row: archRow, component: component12)
945 |
946 | archPtr.pointee.setComponent(row: archRow, component: component13)
947 |
948 | archPtr.pointee.setComponent(row: archRow, component: component14)
949 |
950 | archPtr.pointee.setComponent(row: archRow, component: component15)
951 |
952 | archPtr.pointee.setComponent(row: archRow, component: component16)
953 |
954 | archPtr.pointee.setComponent(row: archRow, component: component17)
955 |
956 | archPtr.pointee.setComponent(row: archRow, component: component18)
957 |
958 | archPtr.pointee.setComponent(row: archRow, component: component19)
959 |
960 | archPtr.pointee.setComponent(row: archRow, component: component20)
961 |
962 | archPtr.pointee.setComponent(row: archRow, component: component21)
963 |
964 | archPtr.pointee.setComponent(row: archRow, component: component22)
965 |
966 | }
967 |
968 | return entId
969 | }
970 |
971 | /// Spawn a new entity with the specified components
972 | @discardableResult
973 | public mutating func spawn< Component1: Component , Component2: Component , Component3: Component , Component4: Component , Component5: Component , Component6: Component , Component7: Component , Component8: Component , Component9: Component , Component10: Component , Component11: Component , Component12: Component , Component13: Component , Component14: Component , Component15: Component , Component16: Component , Component17: Component , Component18: Component , Component19: Component , Component20: Component , Component21: Component , Component22: Component , Component23: Component >
974 | ( _ component1: Component1 , _ component2: Component2 , _ component3: Component3 , _ component4: Component4 , _ component5: Component5 , _ component6: Component6 , _ component7: Component7 , _ component8: Component8 , _ component9: Component9 , _ component10: Component10 , _ component11: Component11 , _ component12: Component12 , _ component13: Component13 , _ component14: Component14 , _ component15: Component15 , _ component16: Component16 , _ component17: Component17 , _ component18: Component18 , _ component19: Component19 , _ component20: Component20 , _ component21: Component21 , _ component22: Component22 , _ component23: Component23 ) -> EntityId {
975 | var ids: [ComponentId] = [ Component1.id , Component2.id , Component3.id , Component4.id , Component5.id , Component6.id , Component7.id , Component8.id , Component9.id , Component10.id , Component11.id , Component12.id , Component13.id , Component14.id , Component15.id , Component16.id , Component17.id , Component18.id , Component19.id , Component20.id , Component21.id , Component22.id , Component23.id ]
976 | ids = ids.sorted() // on separate lines for compile-time performance
977 |
978 | let archId: ArchetypeId = self.archStore.getArchetypeId(components: ids)
979 | let entId: EntityId = self.entityStore.newId()
980 | self.archStore.getMut(archetype: archId) { archPtr in
981 | let archRow = archPtr.pointee.newArchRowId(entity: entId)
982 |
983 | // register entity
984 | self.entityStore.spawn(entity: entId, arch: archId, archRow: archRow)
985 |
986 |
987 | archPtr.pointee.setComponent(row: archRow, component: component1)
988 |
989 | archPtr.pointee.setComponent(row: archRow, component: component2)
990 |
991 | archPtr.pointee.setComponent(row: archRow, component: component3)
992 |
993 | archPtr.pointee.setComponent(row: archRow, component: component4)
994 |
995 | archPtr.pointee.setComponent(row: archRow, component: component5)
996 |
997 | archPtr.pointee.setComponent(row: archRow, component: component6)
998 |
999 | archPtr.pointee.setComponent(row: archRow, component: component7)
1000 |
1001 | archPtr.pointee.setComponent(row: archRow, component: component8)
1002 |
1003 | archPtr.pointee.setComponent(row: archRow, component: component9)
1004 |
1005 | archPtr.pointee.setComponent(row: archRow, component: component10)
1006 |
1007 | archPtr.pointee.setComponent(row: archRow, component: component11)
1008 |
1009 | archPtr.pointee.setComponent(row: archRow, component: component12)
1010 |
1011 | archPtr.pointee.setComponent(row: archRow, component: component13)
1012 |
1013 | archPtr.pointee.setComponent(row: archRow, component: component14)
1014 |
1015 | archPtr.pointee.setComponent(row: archRow, component: component15)
1016 |
1017 | archPtr.pointee.setComponent(row: archRow, component: component16)
1018 |
1019 | archPtr.pointee.setComponent(row: archRow, component: component17)
1020 |
1021 | archPtr.pointee.setComponent(row: archRow, component: component18)
1022 |
1023 | archPtr.pointee.setComponent(row: archRow, component: component19)
1024 |
1025 | archPtr.pointee.setComponent(row: archRow, component: component20)
1026 |
1027 | archPtr.pointee.setComponent(row: archRow, component: component21)
1028 |
1029 | archPtr.pointee.setComponent(row: archRow, component: component22)
1030 |
1031 | archPtr.pointee.setComponent(row: archRow, component: component23)
1032 |
1033 | }
1034 |
1035 | return entId
1036 | }
1037 |
1038 | /// Spawn a new entity with the specified components
1039 | @discardableResult
1040 | public mutating func spawn< Component1: Component , Component2: Component , Component3: Component , Component4: Component , Component5: Component , Component6: Component , Component7: Component , Component8: Component , Component9: Component , Component10: Component , Component11: Component , Component12: Component , Component13: Component , Component14: Component , Component15: Component , Component16: Component , Component17: Component , Component18: Component , Component19: Component , Component20: Component , Component21: Component , Component22: Component , Component23: Component , Component24: Component >
1041 | ( _ component1: Component1 , _ component2: Component2 , _ component3: Component3 , _ component4: Component4 , _ component5: Component5 , _ component6: Component6 , _ component7: Component7 , _ component8: Component8 , _ component9: Component9 , _ component10: Component10 , _ component11: Component11 , _ component12: Component12 , _ component13: Component13 , _ component14: Component14 , _ component15: Component15 , _ component16: Component16 , _ component17: Component17 , _ component18: Component18 , _ component19: Component19 , _ component20: Component20 , _ component21: Component21 , _ component22: Component22 , _ component23: Component23 , _ component24: Component24 ) -> EntityId {
1042 | var ids: [ComponentId] = [ Component1.id , Component2.id , Component3.id , Component4.id , Component5.id , Component6.id , Component7.id , Component8.id , Component9.id , Component10.id , Component11.id , Component12.id , Component13.id , Component14.id , Component15.id , Component16.id , Component17.id , Component18.id , Component19.id , Component20.id , Component21.id , Component22.id , Component23.id , Component24.id ]
1043 | ids = ids.sorted() // on separate lines for compile-time performance
1044 |
1045 | let archId: ArchetypeId = self.archStore.getArchetypeId(components: ids)
1046 | let entId: EntityId = self.entityStore.newId()
1047 | self.archStore.getMut(archetype: archId) { archPtr in
1048 | let archRow = archPtr.pointee.newArchRowId(entity: entId)
1049 |
1050 | // register entity
1051 | self.entityStore.spawn(entity: entId, arch: archId, archRow: archRow)
1052 |
1053 |
1054 | archPtr.pointee.setComponent(row: archRow, component: component1)
1055 |
1056 | archPtr.pointee.setComponent(row: archRow, component: component2)
1057 |
1058 | archPtr.pointee.setComponent(row: archRow, component: component3)
1059 |
1060 | archPtr.pointee.setComponent(row: archRow, component: component4)
1061 |
1062 | archPtr.pointee.setComponent(row: archRow, component: component5)
1063 |
1064 | archPtr.pointee.setComponent(row: archRow, component: component6)
1065 |
1066 | archPtr.pointee.setComponent(row: archRow, component: component7)
1067 |
1068 | archPtr.pointee.setComponent(row: archRow, component: component8)
1069 |
1070 | archPtr.pointee.setComponent(row: archRow, component: component9)
1071 |
1072 | archPtr.pointee.setComponent(row: archRow, component: component10)
1073 |
1074 | archPtr.pointee.setComponent(row: archRow, component: component11)
1075 |
1076 | archPtr.pointee.setComponent(row: archRow, component: component12)
1077 |
1078 | archPtr.pointee.setComponent(row: archRow, component: component13)
1079 |
1080 | archPtr.pointee.setComponent(row: archRow, component: component14)
1081 |
1082 | archPtr.pointee.setComponent(row: archRow, component: component15)
1083 |
1084 | archPtr.pointee.setComponent(row: archRow, component: component16)
1085 |
1086 | archPtr.pointee.setComponent(row: archRow, component: component17)
1087 |
1088 | archPtr.pointee.setComponent(row: archRow, component: component18)
1089 |
1090 | archPtr.pointee.setComponent(row: archRow, component: component19)
1091 |
1092 | archPtr.pointee.setComponent(row: archRow, component: component20)
1093 |
1094 | archPtr.pointee.setComponent(row: archRow, component: component21)
1095 |
1096 | archPtr.pointee.setComponent(row: archRow, component: component22)
1097 |
1098 | archPtr.pointee.setComponent(row: archRow, component: component23)
1099 |
1100 | archPtr.pointee.setComponent(row: archRow, component: component24)
1101 |
1102 | }
1103 |
1104 | return entId
1105 | }
1106 |
1107 | /// Spawn a new entity with the specified components
1108 | @discardableResult
1109 | public mutating func spawn< Component1: Component , Component2: Component , Component3: Component , Component4: Component , Component5: Component , Component6: Component , Component7: Component , Component8: Component , Component9: Component , Component10: Component , Component11: Component , Component12: Component , Component13: Component , Component14: Component , Component15: Component , Component16: Component , Component17: Component , Component18: Component , Component19: Component , Component20: Component , Component21: Component , Component22: Component , Component23: Component , Component24: Component , Component25: Component >
1110 | ( _ component1: Component1 , _ component2: Component2 , _ component3: Component3 , _ component4: Component4 , _ component5: Component5 , _ component6: Component6 , _ component7: Component7 , _ component8: Component8 , _ component9: Component9 , _ component10: Component10 , _ component11: Component11 , _ component12: Component12 , _ component13: Component13 , _ component14: Component14 , _ component15: Component15 , _ component16: Component16 , _ component17: Component17 , _ component18: Component18 , _ component19: Component19 , _ component20: Component20 , _ component21: Component21 , _ component22: Component22 , _ component23: Component23 , _ component24: Component24 , _ component25: Component25 ) -> EntityId {
1111 | var ids: [ComponentId] = [ Component1.id , Component2.id , Component3.id , Component4.id , Component5.id , Component6.id , Component7.id , Component8.id , Component9.id , Component10.id , Component11.id , Component12.id , Component13.id , Component14.id , Component15.id , Component16.id , Component17.id , Component18.id , Component19.id , Component20.id , Component21.id , Component22.id , Component23.id , Component24.id , Component25.id ]
1112 | ids = ids.sorted() // on separate lines for compile-time performance
1113 |
1114 | let archId: ArchetypeId = self.archStore.getArchetypeId(components: ids)
1115 | let entId: EntityId = self.entityStore.newId()
1116 | self.archStore.getMut(archetype: archId) { archPtr in
1117 | let archRow = archPtr.pointee.newArchRowId(entity: entId)
1118 |
1119 | // register entity
1120 | self.entityStore.spawn(entity: entId, arch: archId, archRow: archRow)
1121 |
1122 |
1123 | archPtr.pointee.setComponent(row: archRow, component: component1)
1124 |
1125 | archPtr.pointee.setComponent(row: archRow, component: component2)
1126 |
1127 | archPtr.pointee.setComponent(row: archRow, component: component3)
1128 |
1129 | archPtr.pointee.setComponent(row: archRow, component: component4)
1130 |
1131 | archPtr.pointee.setComponent(row: archRow, component: component5)
1132 |
1133 | archPtr.pointee.setComponent(row: archRow, component: component6)
1134 |
1135 | archPtr.pointee.setComponent(row: archRow, component: component7)
1136 |
1137 | archPtr.pointee.setComponent(row: archRow, component: component8)
1138 |
1139 | archPtr.pointee.setComponent(row: archRow, component: component9)
1140 |
1141 | archPtr.pointee.setComponent(row: archRow, component: component10)
1142 |
1143 | archPtr.pointee.setComponent(row: archRow, component: component11)
1144 |
1145 | archPtr.pointee.setComponent(row: archRow, component: component12)
1146 |
1147 | archPtr.pointee.setComponent(row: archRow, component: component13)
1148 |
1149 | archPtr.pointee.setComponent(row: archRow, component: component14)
1150 |
1151 | archPtr.pointee.setComponent(row: archRow, component: component15)
1152 |
1153 | archPtr.pointee.setComponent(row: archRow, component: component16)
1154 |
1155 | archPtr.pointee.setComponent(row: archRow, component: component17)
1156 |
1157 | archPtr.pointee.setComponent(row: archRow, component: component18)
1158 |
1159 | archPtr.pointee.setComponent(row: archRow, component: component19)
1160 |
1161 | archPtr.pointee.setComponent(row: archRow, component: component20)
1162 |
1163 | archPtr.pointee.setComponent(row: archRow, component: component21)
1164 |
1165 | archPtr.pointee.setComponent(row: archRow, component: component22)
1166 |
1167 | archPtr.pointee.setComponent(row: archRow, component: component23)
1168 |
1169 | archPtr.pointee.setComponent(row: archRow, component: component24)
1170 |
1171 | archPtr.pointee.setComponent(row: archRow, component: component25)
1172 |
1173 | }
1174 |
1175 | return entId
1176 | }
1177 |
1178 | }
1179 |
--------------------------------------------------------------------------------
/Sources/Kiwi/iteratorUtils.swift:
--------------------------------------------------------------------------------
1 | struct UnsafeSequenceBuffer {
2 | private var buffer: ContiguousArray
3 | private var bufferBaseAddress: UnsafeMutablePointer
4 | let bufSize: Int
5 |
6 | /// `bufSize` needs to be specified because the implementation relies on a fully
7 | /// initialized buffer when using `unsafeGetFull` at the end
8 | init(bufSize: Int) {
9 | self.buffer = ContiguousArray()
10 | self.buffer.reserveCapacity(bufSize)
11 | self.bufferBaseAddress = self.buffer.withUnsafeMutableBufferPointer { ptr in
12 | return ptr.baseAddress!
13 | }
14 | self.bufSize = bufSize
15 | }
16 |
17 | @inlinable
18 | mutating func unsafeSet(index: Int, _ val: Element) {
19 | (self.bufferBaseAddress + index).initialize(to: val)
20 | // withUnsafeMutablePointer(to: &self.buffer) { (ptr: UnsafeMutablePointer) in
21 | // (ptr + index).initialize(to: val)
22 | // }
23 | }
24 |
25 | /// Buffer should be fully filled as this function will return the full buffer as an array
26 | /// regardless of wheter it was fully initialized
27 | @inlinable
28 | func unsafeGetFull() -> UnsafeMutableBufferPointer {
29 | return UnsafeMutableBufferPointer(start: self.bufferBaseAddress, count: self.bufSize)
30 | }
31 | }
32 |
33 | func bufferedMap(buffer: inout UnsafeSequenceBuffer, _ seq: some Sequence, _ transform: (Element) throws -> T) rethrows {
34 | // var iter = seq.makeIterator()
35 | // for i in 0.. some Sequence<(EntityId)> {
4 | return self.entityStore.entityIds()
5 | }
6 |
7 | /// Query entities immutably based on their components
8 | public func query(_ components: Component.Type...) -> some Sequence<(EntityId, UnsafeBufferPointer)> {
9 | self.archStore.compMap.lazy
10 | .filter { (archComponents, _) in
11 | components.map { $0.id }.allSatisfy(archComponents.contains)
12 | }.flatMap { (_, archId: ArchetypeId) in
13 | self.archStore.get(archetype: archId) { (archPtr: UnsafePointer) in
14 | return archPtr.pointee.components(components.map { ($0.id, $0.__componentSize) } )
15 | }
16 | }
17 | }
18 |
19 | /// Query entities mutably based on their components
20 | public func queryMut(_ components: Component.Type...) -> some Sequence<(EntityId, UnsafeMutableBufferPointer)> {
21 | self.archStore.compMap.lazy
22 | .filter { (archComponents, _) in
23 | components.map { $0.id }.allSatisfy(archComponents.contains)
24 | }.flatMap { (_, archId: ArchetypeId) in
25 | self.archStore.get(archetype: archId) { (archPtr: UnsafePointer) in
26 | return UnsafeMutablePointer(mutating: archPtr).pointee.componentsMut(components.map { ($0.id, $0.__componentSize)})
27 | }
28 | }
29 | }
30 | }
31 |
32 | extension Sequence<(EntityId)> {
33 | // Providing consisteny
34 | @inlinable
35 | public func get() -> Self { self }
36 | }
37 |
--------------------------------------------------------------------------------
/Sources/Kiwi/util.swift:
--------------------------------------------------------------------------------
1 | @inline(__always) func unreachable() -> Never {
2 | #if DEBUG
3 | print("Unreachable path: ecs bug")
4 | #endif
5 | return unsafeBitCast((), to: Never.self)
6 | }
7 |
--------------------------------------------------------------------------------
/Sources/Kiwi/world.swift:
--------------------------------------------------------------------------------
1 | // TODO: RcWorld -> class wrapper for World (generate with ruby script?)
2 |
3 | /// Base object storing all objects in the ecs
4 | ///
5 | /// When creating a new world, memory has to be cleaned up as well
6 | /// ```swift
7 | /// var world = World()
8 | /// defer { world.destroy() } // clean up memory
9 | /// ```
10 | public struct World {
11 | internal var entityStore: EntityStore
12 | internal var archStore: ArchStore
13 |
14 | public init() {
15 | self.entityStore = EntityStore()
16 | self.archStore = ArchStore()
17 | }
18 |
19 | // see generated macros
20 | /// Spawn a new entity with the specified components
21 | @discardableResult
22 | public mutating func spawn() -> EntityId {
23 | let archId: ArchetypeId = self.archStore.getArchetypeId(components: [])
24 | let entId = self.entityStore.newId()
25 | self.archStore.getMut(archetype: archId) { archPtr in
26 | let archRow = archPtr.pointee.newArchRowId(entity: entId)
27 |
28 | // register entity
29 | self.entityStore.spawn(entity: entId, arch: archId, archRow: archRow)
30 | }
31 |
32 | return entId
33 | }
34 |
35 | /// Get a component of an entity
36 | ///
37 | /// - Attention: fatal error if the entity does not exist or the entity does not have the component
38 | public func getComponent(of entId: EntityId) throws -> T {
39 | let entity: Entity = self.entityStore.get(entity: entId)
40 | return try self.archStore.get(archetype: entity.archId) { archPtr in
41 | try archPtr.pointee.getComponent(row: entity.archRow)
42 | }
43 | }
44 |
45 | /// Get a mutable pointer to a component
46 | ///
47 | /// - Attention: fatal error if the entity does not exist
48 | /// - Throws: if the entity does not have the component
49 | public mutating func getComponent(of entId: EntityId, _ body: (UnsafeMutablePointer) throws -> ()) throws {
50 | let entity: Entity = self.entityStore.get(entity: entId)
51 | try self.archStore.getMut(archetype: entity.archId) { archPtr in
52 | try archPtr.pointee.getComponentMut(row: entity.archRow, body)
53 | }
54 | }
55 |
56 | /// Get a component mutably
57 | ///
58 | /// - Attention: fatal error if the entity does not exist or the entity does not have the component
59 | @inlinable
60 | public mutating func getComponent(of entId: EntityId, _ body: (inout T) throws -> ()) throws {
61 | try self.getComponent(of: entId) { (ptr: UnsafeMutablePointer) in
62 | try body(&ptr.pointee)
63 | }
64 | }
65 |
66 | /// Kills an entity
67 | ///
68 | /// This means the entity id will be reused for other entities.
69 | public mutating func kill(entity id: EntityId) {
70 | let entity: Entity = self.entityStore.get(entity: id)
71 | self.entityStore.kill(entity: id)
72 | self.archStore.getMut(archetype: entity.archId) { (archPtr: UnsafeMutablePointer) in
73 | archPtr.pointee.removeEntity(row: entity.archRow)
74 | }
75 | }
76 |
77 | /// The amount of entities that are alive
78 | public var entityCount: Int {
79 | self.entityStore.entityCount()
80 | }
81 |
82 | /// Returns whether an entity contains the given component
83 | public func hasComponent(entity: EntityId, _ component: T.Type) -> Bool {
84 | let entity: Entity = self.entityStore.get(entity: entity)
85 | return self.archStore.get(archetype: entity.archId) { (archPtr: UnsafePointer) in
86 | return archPtr.pointee.hasComponent(component: T.id)
87 | }
88 | }
89 |
90 | /// Returns whether the entity has the specified flag
91 | public func hasFlag(entity: EntityId, _ flag: F) -> Bool where F.RawValue == FlagId {
92 | self.entityStore.hasFlag(entity: entity, flag.rawValue)
93 | }
94 |
95 | public func hasFlags(entity: EntityId, _ flags: F...) -> Bool where F.RawValue == FlagId {
96 | flags.allSatisfy { self.entityStore.hasFlag(entity: entity, $0.rawValue) }
97 | }
98 |
99 | /// Sets a flag for an entity
100 | public mutating func setFlag(entity: EntityId, _ flag: F) where F.RawValue == FlagId {
101 | self.entityStore.setFlag(entity: entity, flag.rawValue)
102 | }
103 |
104 | /// Remove a flag from an entity
105 | public mutating func removeFlag(entity: EntityId, _ flag: F) where F.RawValue == FlagId {
106 | self.entityStore.removeFlag(entity: entity, flag.rawValue)
107 | }
108 |
109 | /// Cleans up memory related to the world
110 | public mutating func destroy() {
111 | self.archStore.deallocArchetypes()
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/Tests/kiwi-ecs-tests/kiwi_ecs_swiftTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import Kiwi
3 |
4 | struct Pos: Component, Equatable {
5 | var x: Int8
6 | var y: Int8
7 | }
8 |
9 | struct Name: Component, Equatable {
10 | var name: String
11 | }
12 |
13 | struct Vel: Component, Equatable {
14 | var x: Int
15 | var y: Int
16 | }
17 |
18 | final class kiwi_ecs_swiftTests: XCTestCase {
19 | func testSpawn() throws {
20 | var world = World()
21 |
22 | let expectedPos = Pos(x: 1, y: 4)
23 | let expectedName = Name(name: "Hello world")
24 |
25 | let id = world.spawn(expectedPos, expectedName)
26 |
27 | let pos: Pos = try world.getComponent(of: id)
28 | XCTAssertEqual(expectedPos, pos)
29 |
30 | let name: Name = try world.getComponent(of: id)
31 | XCTAssertEqual(expectedName, name)
32 | }
33 |
34 | func testSpawn2() {
35 | var world = World()
36 | (0..<10000).forEach { i in
37 | world.spawn(Name(name: "Hello world - \(i)"))
38 | }
39 |
40 | (0..<10000).forEach { (i: Int) in
41 | let _ = XCTAssertEqual(try! world.getComponent(of: EntityId(i)), Name(name: "Hello world - \(i)"))
42 | }
43 | }
44 |
45 | func testHasComponent() throws {
46 | var world = World()
47 | let id = world.spawn(Name(name: "Hello world"))
48 | XCTAssertTrue(world.hasComponent(entity: id, Name.self))
49 | XCTAssertFalse(world.hasComponent(entity: id, Pos.self))
50 | }
51 |
52 | func testGetComponentMutPtr() throws {
53 | var world = World()
54 |
55 | let id = world.spawn(Pos(x: 0, y: 1))
56 | try world.getComponent(of: id) { (posPtr: UnsafeMutablePointer) in
57 | posPtr.pointee.x = 10
58 | posPtr.pointee.y = 59
59 | }
60 |
61 | XCTAssertEqual(Pos(x: 10, y: 59), try world.getComponent(of: id))
62 | }
63 |
64 | func testGetComponentMutInout() throws {
65 | var world = World()
66 |
67 | let id = world.spawn(Pos(x: 0, y: 1))
68 |
69 | try world.getComponent(of: id) { (posPtr: inout Pos) in
70 | posPtr.x = 10
71 | posPtr.y = 59
72 | }
73 |
74 | XCTAssertEqual(Pos(x: 10, y: 59), try world.getComponent(of: id))
75 | }
76 |
77 | enum Flags: FlagId {
78 | // typealias RawValue = FlagId
79 | case Player
80 | case Enemy
81 | case Wall
82 | }
83 |
84 | func testFlags() throws {
85 | var world = World()
86 |
87 | let id = world.spawn()
88 |
89 | // print("== Player ==")
90 | world.setFlag(entity: id, Flags.Player)
91 | XCTAssert(world.hasFlag(entity: id, Flags.Player))
92 | // print("== Player && Wall ==")
93 | world.setFlag(entity: id, Flags.Wall)
94 | XCTAssert(world.hasFlag(entity: id, Flags.Player) && world.hasFlag(entity: id, Flags.Wall))
95 | // print("== Wall ==")
96 | world.removeFlag(entity: id, Flags.Player)
97 | XCTAssert(world.hasFlag(entity: id, Flags.Wall))
98 | }
99 |
100 | func testHasFlags() throws {
101 | var world = World()
102 |
103 | let id = world.spawn()
104 | world.setFlag(entity: id, Flags.Player)
105 | world.setFlag(entity: id, Flags.Enemy)
106 | let oid = world.spawn()
107 | world.setFlag(entity: oid, Flags.Player)
108 |
109 | var i = 0
110 | world.queryIds()
111 | .filter { world.hasFlags(entity: $0, Flags.Player, Flags.Enemy) }
112 | .forEach { id2 in
113 | XCTAssertEqual(id, id2)
114 | i += 1
115 | }
116 | XCTAssertEqual(i, 1)
117 | }
118 |
119 | func testQuery() throws {
120 | var world = World()
121 |
122 | world.spawn()
123 | let correctId = world.spawn(Name(name: "Hello"), Pos(x: 0, y: 10), Vel(x: 11, y: 25))
124 | world.spawn(Name(name: "Hello"), Pos(x: 1, y: 12))
125 |
126 | var count = 0
127 | world.query(Name.self, Pos.self, Vel.self).getWithIds()
128 | .forEach { (id: EntityId, name: Name, pos: Pos, vel: Vel) in
129 | XCTAssertEqual(id, correctId)
130 | XCTAssertEqual(Name(name: "Hello"), name)
131 | XCTAssertEqual(Pos(x: 0, y: 10), pos)
132 | XCTAssertEqual(Vel(x: 11, y: 25), vel)
133 | count += 1
134 |
135 | }
136 | XCTAssertEqual(count, 1)
137 | }
138 |
139 | func testQueryMut() throws {
140 | var world = World()
141 |
142 | let id1 = world.spawn(Pos(x: 0, y: 10))
143 | let id2 = world.spawn(Vel(x: 4, y: 11), Pos(x: 9, y: 15))
144 |
145 | world.queryMut(Pos.self)
146 | .mutate { (pos: inout Pos) in
147 | pos.x += 1
148 | }
149 |
150 | world.query(Pos.self).getWithIds()
151 | .forEach { (id: EntityId, pos: Pos) in
152 | if id == id1 {
153 | XCTAssertEqual(pos, Pos(x: 1, y: 10))
154 | } else if id == id2 {
155 | XCTAssertEqual(pos, Pos(x: 10, y: 15))
156 | } else {
157 | XCTFail()
158 | }
159 | }
160 | }
161 |
162 | func testReuseEntityIds() throws {
163 | var world = World()
164 |
165 | let id1 = world.spawn(Pos(x: 0, y: 10))
166 | world.setFlag(entity: id1, Flags.Player)
167 | XCTAssertEqual(world.entityCount, 1)
168 | XCTAssertEqual(world.queryIds().map { $0 }, [0])
169 | var count = 0
170 | world.query(Pos.self).get()
171 | .forEach { (_: Pos) in count += 1 }
172 | XCTAssertEqual(count, 1)
173 | XCTAssert(world.hasFlag(entity: id1, Flags.Player))
174 |
175 | world.kill(entity: id1)
176 |
177 | XCTAssertEqual(world.entityCount, 0)
178 | XCTAssertEqual(world.queryIds().map { $0 }, [])
179 | count = 0
180 | world.query(Pos.self).get()
181 | .forEach { (_: Pos) in count += 1 }
182 | XCTAssertEqual(count, 0)
183 |
184 | let id2 = world.spawn(Pos(x: 0, y: 10))
185 | XCTAssertEqual(id2, id1)
186 | XCTAssertEqual(world.entityCount, 1)
187 | XCTAssertEqual(world.queryIds().map { $0 }, [0])
188 | count = 0
189 | world.query(Pos.self).get()
190 | .forEach { (_: Pos) in count += 1 }
191 | XCTAssertEqual(count, 1)
192 | XCTAssert(!world.hasFlag(entity: id1, Flags.Player))
193 | }
194 | }
195 |
--------------------------------------------------------------------------------
/expand_macros.sh:
--------------------------------------------------------------------------------
1 | SPAWN=${SPAWN:-25}
2 | QGET=${QGET:-25}
3 |
4 | rm Sources/Kiwi/generated/*.swift
5 |
6 | for file in Macros/*.template
7 | do
8 | file_name="${file/Macros/}"
9 | out_loc="Sources/Kiwi/generated${file_name/.template/}"
10 | SPAWN=$SPAWN QGET=$QGET ruby macro.rb $file $out_loc
11 | done
12 |
--------------------------------------------------------------------------------
/macro.rb:
--------------------------------------------------------------------------------
1 | #=========#
2 | # Parsing #
3 | #=========#
4 |
5 | FUNCTION_TOKEN='§'
6 |
7 | class Iterator
8 | def initialize(arr)
9 | @enumerator = arr.to_enum
10 | end
11 |
12 | def next
13 | begin
14 | return @enumerator.next
15 | rescue
16 | return nil
17 | end
18 | end
19 | end
20 |
21 | Source = Struct.new :string
22 | Macro = Struct.new :type, :instruction, :body
23 | module MacroType
24 | FOR = 0
25 | end
26 | ForInstruction = Struct.new :var, :range
27 | MRange = Struct.new :begin, :end
28 |
29 | # Variables
30 | Variable = Struct.new :name
31 | EnvVar = Struct.new :name
32 |
33 | Function = Struct.new :name, :params
34 |
35 | BinaryExpression = Struct.new :left, :operator, :right
36 |
37 | def parse(input)
38 | chars = Iterator.new(input.chars)
39 | code = []
40 | current_source = ""
41 | while c = chars.next
42 | case c
43 | when '<'
44 | c2 = chars.next
45 | if c2 == '<'
46 | while c2 == '<'
47 | current_source << c
48 |
49 | c = c2
50 | c2 = chars.next
51 | if c2.nil? || c2 == '%'
52 | break
53 | end
54 | end
55 | end
56 | if c2 == '%'
57 | if !current_source.empty?
58 | code << Source.new(current_source)
59 | current_source = ""
60 | end
61 | # <%
62 | code << parse_template(chars)
63 | else
64 | current_source << c + c2
65 | end # if c = '%'
66 | when FUNCTION_TOKEN
67 | if !current_source.empty?
68 | code << Source.new(current_source)
69 | current_source = ""
70 | end
71 | code << parse_macro_function(chars)
72 | else
73 | current_source << c
74 | end # case c
75 | end # while
76 | if !current_source.empty?
77 | code << Source.new(current_source)
78 | end
79 |
80 | return code
81 | end
82 |
83 | def parse_template(chars)
84 | unless chars.next == '{'
85 | raise "expected '{'"
86 | end
87 |
88 | cmd = ""
89 | loop do
90 | c = chars.next
91 | break if c == '}'
92 | cmd << c
93 | end
94 |
95 | body = ""
96 | level = 0
97 | loop do
98 | c = chars.next
99 | if c == '%'
100 | c2 = chars.next
101 | if c2 == '>'
102 | if level != 0
103 | level -= 1
104 | body << c + c2
105 | else
106 | break
107 | end
108 | else
109 | body << c + c2
110 | end
111 | elsif c == '<'
112 | c2 = chars.next
113 | if c2 == '<'
114 | while c2 == '<'
115 | body << c
116 |
117 | c = c2
118 | c2 = chars.next
119 | if c2 == '%'
120 | break
121 | elsif c2.nil?
122 | raise "unexpectedly found end of input while parsing template body"
123 | end
124 | end
125 | end
126 | if c2 == '%'
127 | level += 1
128 | end
129 | body << c + c2
130 | else
131 | body << c
132 | end
133 | end
134 |
135 | return Macro.new(MacroType::FOR, parse_instruction(cmd), parse(body))
136 | end
137 |
138 | def parse_instruction(instr)
139 | case instr[...instr.index(" ")]
140 | when "for"
141 | instr = instr[instr.index(" ")..].strip
142 | variable = instr[...instr.index(" ")].strip
143 | instr = instr[instr.index(" ")..].strip
144 | raise "expected `in` in for instruction" if instr[..instr.index(" ")].strip != "in"
145 | instr = instr[instr.index(" ")..].strip
146 | range = instr.split("..")
147 | r_range = range[1]
148 | if r_range[0] == "="
149 | r_range = r_range[1..] + " + 1"
150 | end
151 | return ForInstruction.new(variable, MRange.new(parse_var(range[0]), parse_var(r_range)))
152 | else
153 | throw "Invalid instruction #{instr[0]}"
154 | end
155 | end
156 |
157 | # deprecated
158 | def parse_var(var)
159 | return parse_binary_expression(var)
160 | end
161 |
162 | def parse_macro_function(chars)
163 | current = :name
164 | name = ""
165 | params_s = ""
166 | paren_level = 0
167 | loop do
168 | c = chars.next
169 | if c.nil?
170 | raise "unexpectedly found end of input while parsing macro function"
171 | end
172 |
173 | case c
174 | when '('
175 | if current == :params
176 | paren_level += 1
177 | else
178 | current = :params
179 | c = chars.next
180 | if c.nil?
181 | raise "unexpectedly found end of input while parsing macro function's arguments"
182 | end
183 | if c == ')'
184 | if paren_level > 0
185 | paren_level -= 1
186 | else
187 | break
188 | end
189 | end
190 | end
191 | when ' '
192 | return Source.new(name) if current == :name
193 | when ')'
194 | if paren_level > 0
195 | paren_level -= 1
196 | else
197 | break
198 | end
199 | end
200 |
201 | case current
202 | when :name
203 | name << c
204 | when :params
205 | params_s << c
206 | end
207 | end
208 |
209 | params = []
210 | quote_level = 0
211 | paren_level = 0
212 | cur_param = ""
213 | params_s.chars.each do |c|
214 | if c == "," && quote_level == 0 && paren_level == 0
215 | params << cur_param
216 | cur_param = ""
217 | next
218 | end
219 |
220 | if c == '"' && quote_level == 0
221 | quote_level += 1
222 | elsif c == '"' && quote_level == 1 && cur_param[cur_param.length-2] != '\\'
223 | quote_level -= 1
224 | elsif c == '(' && quote_level == 0
225 | paren_level += 1
226 | elsif c == ')' && quote_level == 0
227 | paren_level -= 1
228 | end
229 |
230 |
231 | cur_param << c
232 | end
233 | params << cur_param
234 |
235 | params = params
236 | .map(&:strip)
237 | .map { |param|
238 | parse_binary_expression(param)
239 | }
240 |
241 | return Function.new(name.gsub(FUNCTION_TOKEN, "").to_sym, params)
242 | end
243 |
244 | def parse_binary_expression(param)
245 | elems = []
246 | cur_elem = ""
247 | quote_level = 0
248 | for c in param.chars
249 | if c == ' ' && quote_level == 0
250 | elems << cur_elem
251 | cur_elem = ""
252 | else
253 | quote_level += 1 if c == '"' && (quote_level == 0 || cur_elem.last == "\\")
254 | quote_level -= 1 if c == '"' && quote_level == 1 && cur_elem.chars.last != "\\"
255 |
256 | cur_elem << c
257 | end
258 | end
259 | elems << cur_elem
260 |
261 | tokens = elems
262 | .map { |e| e.strip }
263 | .map do |elem|
264 | if elem.start_with? '$'
265 | Variable.new(elem)
266 | elsif elem.to_i.to_s == elem
267 | elem.to_i
268 | elsif elem.start_with? "ENV"
269 | EnvVar.new(elem.split(".")[1])
270 | elsif elem.start_with? '"'
271 | elem[1..param.length-2]
272 | elsif elem == "=="
273 | :equal
274 | elsif elem == "+"
275 | :plus
276 | elsif elem == "-"
277 | :minus
278 | elsif elem.start_with? FUNCTION_TOKEN
279 | parse_macro_function(Iterator.new(elem.chars))
280 | else
281 | raise "invalid argument in binary expression: #{elem}"
282 | end
283 | end
284 |
285 | return parse_binary_expression_tokens(tokens)
286 | end
287 |
288 | def parse_binary_expression_tokens(tokens)
289 | [
290 | [:equal],
291 | [:gt, :gte, :lt, :lte],
292 | [:minus, :plus],
293 | [:divide, :multiply],
294 | # [:not]
295 | ].each do |operators|
296 | tokens.each_with_index do |token, i|
297 | if operators.include? token
298 | return BinaryExpression.new(
299 | parse_binary_expression_tokens(tokens[0...i]),
300 | token,
301 | parse_binary_expression_tokens(tokens[i+1..tokens.length-1])
302 | )
303 | end
304 | end
305 | end
306 |
307 | # No binary operators found
308 | if tokens.length > 1
309 | raise "invalid binary expression (#{tokens})"
310 | end
311 |
312 | return tokens[0]
313 | end
314 |
315 | #=================
316 | # Macro expansion
317 | #=================
318 |
319 | def expand(input, functions = Hash.new, variables = Hash.new)
320 | # puts "expand input: #{input}"
321 | return input.flat_map do |val|
322 | if val.is_a? Source
323 | next val.string
324 | elsif val.is_a? Macro
325 | macro = val
326 | case macro.type
327 | when MacroType::FOR
328 | for_instr = macro.instruction
329 | raise "undefined variable #{for_instr.range.begin}" unless range_begin = get_var(for_instr.range.begin, variables, functions).to_i
330 | raise "undefined variable #{for_instr.range.end}" unless range_end = get_var(for_instr.range.end, variables, functions).to_i
331 |
332 | next (range_begin...range_end).flat_map do |var_val|
333 | variables[for_instr.var] = var_val
334 |
335 | next variables.inject(expand(macro.body, functions, variables)) do |src, variable|
336 | src.gsub(variable[0].to_s, variable[1].to_s)
337 | end
338 | end
339 | else
340 | raise "bug"
341 | end
342 | elsif val.is_a? Function
343 | raise "unknown function #{val.name}" unless fn = functions[val.name]
344 | next fn.call(val.params, variables, functions)
345 | else
346 | raise "bug #{val}"
347 | end
348 | end.join " "
349 | end
350 |
351 | def get_var(var, variables, functions)
352 | if var.is_a? Integer
353 | return var
354 | elsif var.is_a? Variable
355 | return variables[var.name]
356 | elsif var.is_a? EnvVar
357 | return ENV[var.name]
358 | elsif var.is_a? BinaryExpression
359 | return eval_binary_expression(var, variables, functions)
360 | elsif var.is_a? Function
361 | return expand([var], functions, variables)
362 | elsif var.is_a? String
363 | return var
364 | else
365 | raise "invalid variable #{var}"
366 | end
367 | end
368 |
369 | def eval_binary_expression(expr, variables, functions)
370 | lhs = get_var(expr.left, variables, functions).to_i
371 | rhs = get_var(expr.right, variables, functions).to_i
372 |
373 | case expr.operator
374 | when :equal
375 | return lhs == rhs
376 | when :plus
377 | return lhs + rhs
378 | when :minus
379 | return lhs - rhs
380 | when :gt
381 | return lhs > rhs
382 | when :gte
383 | return lhs >= rhs
384 | when :lt
385 | return lhs < rhs
386 | when :lte
387 | return lhs <= rhs
388 | when :divide
389 | return lhs / rhs
390 | when :multiply
391 | return lhs * rhs
392 | end
393 | end
394 |
395 | #=========
396 | # Execute
397 | #=========
398 |
399 | def gen_zip(count, total)
400 | if total + 1 == count
401 | return nil
402 | end
403 |
404 | next_zip = gen_zip(count + 1, total)
405 | if !next_zip.nil?
406 | return <"
448 | end
449 |
450 | functions = {
451 | :unless => -> (params, variables, functions) do
452 | unless eval_binary_expression(params[0], variables, functions)
453 | return get_var(params[1], variables, functions)
454 | end
455 | end,
456 | :if => -> (params, variables, functions) do
457 | if eval_binary_expression(params[0], variables, functions)
458 | return params[1]
459 | end
460 | end,
461 | :gen_zip => -> (params, variables, functions) do
462 | gen_zip(1, get_var(params[0], variables, functions))
463 | end,
464 | :zip_flatmap => -> (params, variables, functions) do
465 | i = get_var(params[0], variables, functions)
466 | if i == 1
467 | return ""
468 | else
469 | n = next_zip_flatmap(i, 0, [0])
470 | return ".map { tuple in (#{n})}"
471 | end
472 | end,
473 | :zip2sequence => -> (params, variables, functions) do
474 | total = get_var(params[0], variables, functions)
475 | return zip2sequence(total)
476 | end,
477 | :eval => -> (params, variables, functions) do
478 | return get_var(params[0], variables, functions)
479 | end
480 | }
481 |
482 | raise "no input file given" unless filename = ARGV[0]
483 | raise "no output file given" unless output_file = ARGV[1]
484 |
485 | puts "#{filename} > #{output_file}"
486 |
487 | parsed = parse(File.read(filename))
488 | # p parsed
489 | File.write(output_file, expand(parsed, functions))
490 |
--------------------------------------------------------------------------------