├── .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 | --------------------------------------------------------------------------------