From 8ce68f21d62d76b076b0b8d4fc308bf78617dbfc Mon Sep 17 00:00:00 2001 From: rrbox <87851278+rrbox@users.noreply.github.com> Date: Thu, 18 Jan 2024 01:23:19 +0900 Subject: [PATCH] =?UTF-8?q?change:=20=E9=9D=9E=E5=90=8C=E6=9C=9F=E5=8C=96?= =?UTF-8?q?=E3=81=AE=E5=A4=89=E6=9B=B4=E3=82=92=E8=BF=BD=E5=8A=A0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xcschemes/ECS_Swift-Package.xcscheme | 250 ++++++++++++++++++ .../xcshareddata/xcschemes/PlugIns.xcscheme | 66 +++++ Sources/ECS/Chunk/Chunk.swift | 8 +- Sources/ECS/Chunk/ChunkEntityInterface.swift | 46 +++- Sources/ECS/Chunk/ChunkStorage+.swift | 20 +- Sources/ECS/Chunk/ChunkStorage.swift | 4 - Sources/ECS/Commands/Command.swift | 2 +- Sources/ECS/Commands/Commands.swift | 6 +- Sources/ECS/Commands/World+Commands.swift | 8 +- Sources/ECS/Commons/WorldStorage.swift | 2 +- .../Commands/AddComponentCommand.swift | 4 +- .../Commands/DespawnCommand.swift | 4 +- .../Commands/RemoveComponentCommand.swift | 4 +- .../Commands/SpawnCommand.swift | 4 +- .../EntityCommands/EntityCommands.swift | 14 +- Sources/ECS/Event/EventStreaming/Event.swift | 20 +- .../Event/EventStreaming/EventResponder.swift | 4 +- Sources/ECS/Event/World+EventStreamer.swift | 29 +- Sources/ECS/FilterdQuery/FIlteredQuery.swift | 22 +- Sources/ECS/FilterdQuery/QueryProtocol.swift | 26 +- Sources/ECS/Query/Query.swift | 14 +- Sources/ECS/Query/Query2.swift | 14 +- Sources/ECS/Query/Query3.swift | 14 +- Sources/ECS/Query/Query4.swift | 14 +- Sources/ECS/Query/Query5.swift | 14 +- Sources/ECS/Resource/Commands+Resource.swift | 2 +- Sources/ECS/States/State.swift | 70 +++-- Sources/ECS/States/StateControll.swift | 20 +- Sources/ECS/System/SystemBuffer.swift | 2 +- .../ECS/SystemParameter/SystemParameter.swift | 4 +- Sources/ECS/Systems/System.swift | 16 +- Sources/ECS/Systems/System2.swift | 20 +- Sources/ECS/Systems/System3.swift | 32 +-- Sources/ECS/Systems/System4.swift | 38 +-- Sources/ECS/Systems/System5.swift | 44 +-- Sources/ECS/WorldMethods/FirstFrame.swift | 6 +- .../WorldMethods/World+EntityCommands.swift | 8 +- Sources/ECS/WorldMethods/World+SetUp.swift | 11 +- Sources/ECS/WorldMethods/World+Spawn.swift | 8 +- Sources/ECS/WorldMethods/World+Update.swift | 35 ++- .../Commands/EntityCommands+Graphic.swift | 12 +- .../Graphic2D/Commands/RemoveGraphic.swift | 2 +- .../Graphic2D/Commands/SetGraphic.swift | 2 +- Sources/PlugIns/Scene/Scene.swift | 12 +- .../ObjectLinkPlugInTests.swift | 2 +- Tests/ecs-swiftTests/ChunkTests.swift | 32 ++- Tests/ecs-swiftTests/CommandsTests.swift | 34 ++- .../ecs-swiftTests/EntityCommandsTests.swift | 18 +- Tests/ecs-swiftTests/EventTests.swift | 28 +- Tests/ecs-swiftTests/FilteredQueryTests.swift | 31 +++ Tests/ecs-swiftTests/QueryTests.swift | 162 ++++++++---- Tests/ecs-swiftTests/SystemTests.swift | 30 +-- Tests/ecs-swiftTests/UpdateSystemTests.swift | 19 +- Tests/ecs-swiftTests/ecs_swiftTests.swift | 107 ++++++-- 54 files changed, 1009 insertions(+), 411 deletions(-) create mode 100644 .swiftpm/xcode/xcshareddata/xcschemes/ECS_Swift-Package.xcscheme create mode 100644 .swiftpm/xcode/xcshareddata/xcschemes/PlugIns.xcscheme create mode 100644 Tests/ecs-swiftTests/FilteredQueryTests.swift diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/ECS_Swift-Package.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/ECS_Swift-Package.xcscheme new file mode 100644 index 0000000..5840b87 --- /dev/null +++ b/.swiftpm/xcode/xcshareddata/xcschemes/ECS_Swift-Package.xcscheme @@ -0,0 +1,250 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/PlugIns.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/PlugIns.xcscheme new file mode 100644 index 0000000..e2f85dc --- /dev/null +++ b/.swiftpm/xcode/xcshareddata/xcschemes/PlugIns.xcscheme @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sources/ECS/Chunk/Chunk.swift b/Sources/ECS/Chunk/Chunk.swift index f5e4ec6..b01fa0f 100644 --- a/Sources/ECS/Chunk/Chunk.swift +++ b/Sources/ECS/Chunk/Chunk.swift @@ -5,8 +5,8 @@ // Created by rrbox on 2023/08/11. // -public class Chunk { - func spawn(entity: Entity, entityRecord: EntityRecordRef) {} - func despawn(entity: Entity) {} - func applyCurrentState(_ entityRecord: EntityRecordRef, forEntity entity: Entity) {} +public protocol Chunk: WorldStorageElement { + func spawn(entity: Entity, entityRecord: EntityRecordRef) async + func despawn(entity: Entity) async + func applyCurrentState(_ entityRecord: EntityRecordRef, forEntity entity: Entity) async } diff --git a/Sources/ECS/Chunk/ChunkEntityInterface.swift b/Sources/ECS/Chunk/ChunkEntityInterface.swift index b88a6ed..2db9ca6 100644 --- a/Sources/ECS/Chunk/ChunkEntityInterface.swift +++ b/Sources/ECS/Chunk/ChunkEntityInterface.swift @@ -5,19 +5,45 @@ // Created by rrbox on 2023/08/11. // +class _ChunkBox { + func spawn(entity: Entity, entityRecord: EntityRecordRef) async {} + func despawn(entity: Entity) async {} + func applyCurrentState(_ entityRecord: EntityRecordRef, forEntity entity: Entity) async {} +} + +class ChunkBox: _ChunkBox { + let chunk: T + + init(chunk: T) { + self.chunk = chunk + } + + override func spawn(entity: Entity, entityRecord: EntityRecordRef) async { + await self.chunk.spawn(entity: entity, entityRecord: entityRecord) + } + + override func despawn(entity: Entity) async { + await self.chunk.despawn(entity: entity) + } + + override func applyCurrentState(_ entityRecord: EntityRecordRef, forEntity entity: Entity) async { + await self.chunk.applyCurrentState(entityRecord, forEntity: entity) + } +} + /// Chunk を種類関係なく格納するためのコンポーネントです. /// /// Entity の変更を全ての Chunk に反映させる目的で使用されます. -class ChunkEntityInterface: WorldStorageElement { +actor ChunkEntityInterface: WorldStorageElement { /// entity が spawn されてから component が完全に挿入されるまでの間, entity を queue に保管します. /// /// Entity が ``Commands/spawn()`` され, ``EntityCommands/addComponent(_:)`` されるまでの間, Entity は実際には Chunk に反映されず, var prespawnedEntityQueue = [(Entity, EntityRecordRef)]() - var chunks = [Chunk]() + var chunks = [_ChunkBox]() /// chunk を追加します - func add(chunk: Chunk) { - self.chunks.append(chunk) + func add(chunk: T) { + self.chunks.append(ChunkBox(chunk: chunk)) } /// World に entity が追加された時に実行します. @@ -30,10 +56,10 @@ class ChunkEntityInterface: WorldStorageElement { /// Spawn 処理された entity を, 実際に chunk に追加します. /// /// Component が完全に追加された後にこの処理を呼び出すことで, Entity の Component の有無が Chunk に反映されるようになります. - func applyEntityQueue() { + func applyEntityQueue() async { for (entity, entityRecord) in prespawnedEntityQueue { for chunk in self.chunks { - chunk.spawn(entity: entity, entityRecord: entityRecord) + await chunk.spawn(entity: entity, entityRecord: entityRecord) } } self.prespawnedEntityQueue = [] @@ -42,15 +68,15 @@ class ChunkEntityInterface: WorldStorageElement { /// World から entity が削除される時に実行します. /// /// フレームの終わりに全ての chunk から entity を削除します. - func despawn(entity: Entity) { + func despawn(entity: Entity) async { for chunk in self.chunks { - chunk.despawn(entity: entity) + await chunk.despawn(entity: entity) } } - func applyCurrentState(_ entityRecord: EntityRecordRef, forEntity entity: Entity) { + func applyCurrentState(_ entityRecord: EntityRecordRef, forEntity entity: Entity) async { for chunk in self.chunks { - chunk.applyCurrentState(entityRecord, forEntity: entity) + await chunk.applyCurrentState(entityRecord, forEntity: entity) } } diff --git a/Sources/ECS/Chunk/ChunkStorage+.swift b/Sources/ECS/Chunk/ChunkStorage+.swift index 0f9c1bb..19ea18d 100644 --- a/Sources/ECS/Chunk/ChunkStorage+.swift +++ b/Sources/ECS/Chunk/ChunkStorage+.swift @@ -10,26 +10,26 @@ extension ChunkStorage { self.buffer.map.push(ChunkEntityInterface()) } - func addChunk(_ chunk: ChunkType) { + func addChunk(_ chunk: ChunkType) async { self.buffer.map.push(chunk) - self.buffer.map.valueRef(ofType: ChunkEntityInterface.self)!.body.add(chunk: chunk) + await self.buffer.map.valueRef(ofType: ChunkEntityInterface.self)!.body.add(chunk: chunk) } - func push(entity: Entity, entityRecord: EntityRecordRef) { - self.buffer.map.valueRef(ofType: ChunkEntityInterface.self)!.body.push(entity: entity, entityRecord: entityRecord) + func push(entity: Entity, entityRecord: EntityRecordRef) async { + await self.buffer.map.valueRef(ofType: ChunkEntityInterface.self)!.body.push(entity: entity, entityRecord: entityRecord) } - func applyEntityQueue() { - self.buffer.map.valueRef(ofType: ChunkEntityInterface.self)!.body.applyEntityQueue() + func applyEntityQueue() async { + await self.buffer.map.valueRef(ofType: ChunkEntityInterface.self)!.body.applyEntityQueue() } - func despawn(entity: Entity) { - self.buffer.map.valueRef(ofType: ChunkEntityInterface.self)!.body.despawn(entity: entity) + func despawn(entity: Entity) async { + await self.buffer.map.valueRef(ofType: ChunkEntityInterface.self)!.body.despawn(entity: entity) } // entity を最新の状態に更新します. - func applyCurrentState(_ entityRecord: EntityRecordRef, forEntity entity: Entity) { - self.buffer.map.valueRef(ofType: ChunkEntityInterface.self)!.body.applyCurrentState(entityRecord, forEntity: entity) + func applyCurrentState(_ entityRecord: EntityRecordRef, forEntity entity: Entity) async { + await self.buffer.map.valueRef(ofType: ChunkEntityInterface.self)!.body.applyCurrentState(entityRecord, forEntity: entity) } } diff --git a/Sources/ECS/Chunk/ChunkStorage.swift b/Sources/ECS/Chunk/ChunkStorage.swift index d80771f..4816cb5 100644 --- a/Sources/ECS/Chunk/ChunkStorage.swift +++ b/Sources/ECS/Chunk/ChunkStorage.swift @@ -5,10 +5,6 @@ // Created by rrbox on 2023/08/11. // -extension Chunk: WorldStorageElement { - -} - /// Chunk を種類別で格納します final public class ChunkStorage { let buffer: WorldStorageRef diff --git a/Sources/ECS/Commands/Command.swift b/Sources/ECS/Commands/Command.swift index 71702bf..8df037e 100644 --- a/Sources/ECS/Commands/Command.swift +++ b/Sources/ECS/Commands/Command.swift @@ -7,7 +7,7 @@ open class Command { public init() {} - open func runCommand(in world: World) { + open func runCommand(in world: World) async { } } diff --git a/Sources/ECS/Commands/Commands.swift b/Sources/ECS/Commands/Commands.swift index 49f1f90..12a3a69 100644 --- a/Sources/ECS/Commands/Commands.swift +++ b/Sources/ECS/Commands/Commands.swift @@ -20,7 +20,7 @@ } ``` */ -final public class Commands: SystemParameter { +public actor Commands: SystemParameter { var commandQueue = [Command]() /// Commands では, World への登録時には何もしません. @@ -36,4 +36,8 @@ final public class Commands: SystemParameter { public func push(command: Command) { self.commandQueue.append(command) } + + func clearAllCommand() { + self.commandQueue = [] + } } diff --git a/Sources/ECS/Commands/World+Commands.swift b/Sources/ECS/Commands/World+Commands.swift index 6fad8d0..715b889 100644 --- a/Sources/ECS/Commands/World+Commands.swift +++ b/Sources/ECS/Commands/World+Commands.swift @@ -6,11 +6,11 @@ // extension World { - func applyCommands() { + func applyCommands() async { let commands = self.worldStorage.commandsStorage.commands()! - for command in commands.commandQueue { - command.runCommand(in: self) + for command in await commands.commandQueue { + await command.runCommand(in: self) } - commands.commandQueue = [] + await commands.clearAllCommand() } } diff --git a/Sources/ECS/Commons/WorldStorage.swift b/Sources/ECS/Commons/WorldStorage.swift index 275b13b..5657c61 100644 --- a/Sources/ECS/Commons/WorldStorage.swift +++ b/Sources/ECS/Commons/WorldStorage.swift @@ -7,7 +7,7 @@ enum WorldStorage {} -protocol WorldStorageElement {} +public protocol WorldStorageElement {} class Box: Item { var body: T diff --git a/Sources/ECS/EntityCommands/Commands/AddComponentCommand.swift b/Sources/ECS/EntityCommands/Commands/AddComponentCommand.swift index a51c701..efdbdf2 100644 --- a/Sources/ECS/EntityCommands/Commands/AddComponentCommand.swift +++ b/Sources/ECS/EntityCommands/Commands/AddComponentCommand.swift @@ -13,7 +13,7 @@ class AddComponent: EntityCommand { super.init(entity: entity) } - override func runCommand(in world: World) { - world.addComponent(self.componnet, forEntity: self.entity) + override func runCommand(in world: World) async { + await world.addComponent(self.componnet, forEntity: self.entity) } } diff --git a/Sources/ECS/EntityCommands/Commands/DespawnCommand.swift b/Sources/ECS/EntityCommands/Commands/DespawnCommand.swift index e986818..68e5b24 100644 --- a/Sources/ECS/EntityCommands/Commands/DespawnCommand.swift +++ b/Sources/ECS/EntityCommands/Commands/DespawnCommand.swift @@ -6,7 +6,7 @@ // class DespawnCommand: EntityCommand { - override func runCommand(in world: World) { - world.despawn(entity: self.entity) + override func runCommand(in world: World) async { + await world.despawn(entity: self.entity) } } diff --git a/Sources/ECS/EntityCommands/Commands/RemoveComponentCommand.swift b/Sources/ECS/EntityCommands/Commands/RemoveComponentCommand.swift index 7bd9427..ae8f7b0 100644 --- a/Sources/ECS/EntityCommands/Commands/RemoveComponentCommand.swift +++ b/Sources/ECS/EntityCommands/Commands/RemoveComponentCommand.swift @@ -10,7 +10,7 @@ class RemoveComponent: EntityCommand { super.init(entity: entity) } - override func runCommand(in world: World) { - world.removeComponent(ofType: ComponentType.self, fromEntity: self.entity) + override func runCommand(in world: World) async { + await world.removeComponent(ofType: ComponentType.self, fromEntity: self.entity) } } diff --git a/Sources/ECS/EntityCommands/Commands/SpawnCommand.swift b/Sources/ECS/EntityCommands/Commands/SpawnCommand.swift index 374500d..43fc9f7 100644 --- a/Sources/ECS/EntityCommands/Commands/SpawnCommand.swift +++ b/Sources/ECS/EntityCommands/Commands/SpawnCommand.swift @@ -13,8 +13,8 @@ class SpawnCommand: EntityCommand { super.init(entity: id) } - override func runCommand(in world: World) { - world.push(entity: self.entity, entityRecord: self.entityRecord) + override func runCommand(in world: World) async { + await world.push(entity: self.entity, entityRecord: self.entityRecord) } } diff --git a/Sources/ECS/EntityCommands/EntityCommands/EntityCommands.swift b/Sources/ECS/EntityCommands/EntityCommands/EntityCommands.swift index 614fb87..573cdf5 100644 --- a/Sources/ECS/EntityCommands/EntityCommands/EntityCommands.swift +++ b/Sources/ECS/EntityCommands/EntityCommands/EntityCommands.swift @@ -14,8 +14,9 @@ final public class EntityCommands { self.commands = commands } - public func pushCommand(_ command: EntityCommand) { - self.commands.commandQueue.append(command) + public func pushCommand(_ command: EntityCommand) async { +// self.commands.commandQueue.append(command) + await self.commands.push(command: command) } /// Commands で操作した Entity を受け取ります. @@ -27,16 +28,17 @@ final public class EntityCommands { /// Entity に Component を追加します. /// - Parameter component: 追加するコンポーネントを指定します. /// - Returns: Entity component のビルダーです. - @discardableResult public func addComponent(_ component: ComponentType) -> EntityCommands { - self.commands.commandQueue.append(AddComponent(entity: self.entity, componnet: component)) + @discardableResult public func addComponent(_ component: ComponentType) async-> EntityCommands { +// self.commands.commandQueue.append(AddComponent(entity: self.entity, componnet: component)) + await self.commands.push(command: AddComponent(entity: self.entity, componnet: component)) return self } /// Entity から Component を削除します. /// - Parameter type: 削除する Component の型を指定します. /// - Returns: Entity component のビルダーです. - @discardableResult public func removeComponent(ofType type: ComponentType.Type) -> EntityCommands { - self.commands.commandQueue.append(RemoveComponent(entity: self.entity, componentType: ComponentType.self)) + @discardableResult public func removeComponent(ofType type: ComponentType.Type) async -> EntityCommands { + await self.commands.push(command: RemoveComponent(entity: self.entity, componentType: ComponentType.self)) return self } diff --git a/Sources/ECS/Event/EventStreaming/Event.swift b/Sources/ECS/Event/EventStreaming/Event.swift index 9b8cf0f..9a73f6b 100644 --- a/Sources/ECS/Event/EventStreaming/Event.swift +++ b/Sources/ECS/Event/EventStreaming/Event.swift @@ -10,7 +10,7 @@ public protocol EventProtocol { } class AnyEvent { - func runEventReceiver(worldStorage: WorldStorageRef) { + func runEventReceiver(worldStorage: WorldStorageRef) async { } } @@ -21,19 +21,27 @@ final class Event: AnyEvent { self.value = value } - override func runEventReceiver(worldStorage: WorldStorageRef) { + override func runEventReceiver(worldStorage: WorldStorageRef) async { worldStorage.map.push(EventReader(value: self.value)) if let systems = worldStorage.eventStorage.eventResponder(eventOfType: T.self)!.systems[.update] { - for system in systems { - system.execute(worldStorage) + await withTaskGroup(of: Void.self) { group in + for system in systems { + group.addTask { + await system.execute(worldStorage) + } + } } } for schedule in worldStorage.stateStorage.currentSchedulesWhichAssociatedStates() { guard let systems = worldStorage.eventStorage.eventResponder(eventOfType: T.self)!.systems[schedule] else { continue } - for system in systems { - system.execute(worldStorage) + await withTaskGroup(of: Void.self) { group in + for system in systems { + group.addTask { + await system.execute(worldStorage) + } + } } } diff --git a/Sources/ECS/Event/EventStreaming/EventResponder.swift b/Sources/ECS/Event/EventStreaming/EventResponder.swift index faeba39..7eb3fa0 100644 --- a/Sources/ECS/Event/EventStreaming/EventResponder.swift +++ b/Sources/ECS/Event/EventStreaming/EventResponder.swift @@ -21,9 +21,9 @@ final public class EventResponder: WorldStorageElement { } public extension World { - @discardableResult func buildEventResponder(_ eventType: T.Type, _ build: (EventResponderBuilder) -> ()) -> World { + @discardableResult func buildEventResponder(_ eventType: T.Type, _ build: (EventResponderBuilder) async -> ()) async -> World { let builder = EventResponderBuilder(worldStorage: self.worldStorage) - build(builder) + await build(builder) self.worldStorage.eventStorage.eventResponder(eventOfType: T.self)! .systems diff --git a/Sources/ECS/Event/World+EventStreamer.swift b/Sources/ECS/Event/World+EventStreamer.swift index c2c4154..495bd3c 100644 --- a/Sources/ECS/Event/World+EventStreamer.swift +++ b/Sources/ECS/Event/World+EventStreamer.swift @@ -29,17 +29,22 @@ extension World { } extension World { - func applyEventQueue() { + func applyEventQueue() async { let eventQueue = self.worldStorage.eventStorage.eventQueue()! eventQueue.sendingEvents = eventQueue.eventQueue eventQueue.eventQueue = [] - for event in eventQueue.sendingEvents { - event.runEventReceiver(worldStorage: self.worldStorage) + await withTaskGroup(of: Void.self) { group in + for event in eventQueue.sendingEvents { + group.addTask { + await event.runEventReceiver(worldStorage: self.worldStorage) + } + } } + eventQueue.sendingEvents = [] } - func applyCommandsEventQueue(eventOfType: T.Type) { + func applyCommandsEventQueue(eventOfType: T.Type) async { let eventStorage = self.worldStorage.eventStorage let eventQueue = eventStorage.commandsEventQueue(eventOfType: T.self)! eventQueue.sendingEvents = eventQueue.eventQueue @@ -48,15 +53,23 @@ extension World { self.worldStorage.map.push(EventReader(value: event)) if let systems = eventStorage.commandsEventResponder(eventOfType: T.self)!.systems[.update] { - for system in systems { - system.execute(self.worldStorage) + await withTaskGroup(of: Void.self) { group in + for system in systems { + group.addTask { + await system.execute(self.worldStorage) + } + } } } for schedule in self.worldStorage.stateStorage.currentSchedulesWhichAssociatedStates() { guard let systems = eventStorage.commandsEventResponder(eventOfType: T.self)!.systems[schedule] else { continue } - for system in systems { - system.execute(self.worldStorage) + await withTaskGroup(of: Void.self) { group in + for system in systems { + group.addTask { + await system.execute(self.worldStorage) + } + } } } diff --git a/Sources/ECS/FilterdQuery/FIlteredQuery.swift b/Sources/ECS/FilterdQuery/FIlteredQuery.swift index 75e68f7..ca50793 100644 --- a/Sources/ECS/FilterdQuery/FIlteredQuery.swift +++ b/Sources/ECS/FilterdQuery/FIlteredQuery.swift @@ -5,28 +5,36 @@ // Created by rrbox on 2023/09/17. // -final public class Filtered: Chunk, SystemParameter { +public actor Filtered: Chunk, SystemParameter { public let query: Q = Q() - override func spawn(entity: Entity, entityRecord: EntityRecordRef) { + public func spawn(entity: Entity, entityRecord: EntityRecordRef) async { guard F.condition(forEntityRecord: entityRecord) else { return } - self.query.spawn(entity: entity, entityRecord: entityRecord) + await self.query.spawn(entity: entity, entityRecord: entityRecord) } - override func despawn(entity: Entity) { - self.query.despawn(entity: entity) + public func despawn(entity: Entity) async { + await self.query.despawn(entity: entity) + } + + public func applyCurrentState(_ entityRecord: EntityRecordRef, forEntity entity: Entity) async { + await self.query.applyCurrentState(entityRecord, forEntity: entity) } public static func getParameter(from worldStorage: WorldStorageRef) -> Filtered? { worldStorage.chunkStorage.chunk(ofType: Filtered.self) } - public static func register(to worldStorage: WorldStorageRef) { + public static func register(to worldStorage: WorldStorageRef) async { guard worldStorage.chunkStorage.chunk(ofType: Self.self) == nil else { return } - worldStorage.chunkStorage.addChunk(Filtered()) + await worldStorage.chunkStorage.addChunk(Filtered()) + } + + public func update(entity: Entity, _ execute: Q.Update) async { + await self.query.update(entity, execute) } } diff --git a/Sources/ECS/FilterdQuery/QueryProtocol.swift b/Sources/ECS/FilterdQuery/QueryProtocol.swift index 6172ea2..2f06544 100644 --- a/Sources/ECS/FilterdQuery/QueryProtocol.swift +++ b/Sources/ECS/FilterdQuery/QueryProtocol.swift @@ -5,14 +5,24 @@ // Created by rrbox on 2023/09/17. // -public protocol QueryProtocol { - func spawn(entity: Entity, entityRecord: EntityRecordRef) - func despawn(entity: Entity) +public protocol QueryProtocol: Chunk { init() + associatedtype Update + func update(_ entity: Entity, _ execute: Update) async } -extension Query: QueryProtocol {} -extension Query2: QueryProtocol {} -extension Query3: QueryProtocol {} -extension Query4: QueryProtocol {} -extension Query5: QueryProtocol {} +extension Query: QueryProtocol { + public typealias Update = (inout ComponentType) -> () +} +extension Query2: QueryProtocol { + public typealias Update = (inout C0, inout C1) -> () +} +extension Query3: QueryProtocol { + public typealias Update = (inout C0, inout C1, inout C2) -> () +} +extension Query4: QueryProtocol { + public typealias Update = (inout C0, inout C1, inout C2, inout C3) -> () +} +extension Query5: QueryProtocol { + public typealias Update = (inout C0, inout C1, inout C2, inout C3, inout C4) -> () +} diff --git a/Sources/ECS/Query/Query.swift b/Sources/ECS/Query/Query.swift index 72d5219..0fdacab 100644 --- a/Sources/ECS/Query/Query.swift +++ b/Sources/ECS/Query/Query.swift @@ -5,21 +5,21 @@ // Created by rrbox on 2023/08/12. // -final public class Query: Chunk, SystemParameter { +public actor Query: Chunk, SystemParameter { var components = [Entity: ComponentRef]() - public override init() {} + public init() {} - public override func spawn(entity: Entity, entityRecord: EntityRecordRef) { + public func spawn(entity: Entity, entityRecord: EntityRecordRef) async { guard let componentRef = entityRecord.componentRef(ComponentType.self) else { return } self.components[entity] = componentRef } - public override func despawn(entity: Entity) { + public func despawn(entity: Entity) async { self.components.removeValue(forKey: entity) } - override func applyCurrentState(_ entityRecord: EntityRecordRef, forEntity entity: Entity) { + public func applyCurrentState(_ entityRecord: EntityRecordRef, forEntity entity: Entity) { guard let componentRef = entityRecord.componentRef(ComponentType.self) else { self.components.removeValue(forKey: entity) return @@ -43,14 +43,14 @@ final public class Query: Chunk, SystemParameter { self.components[entity]?.value } - public static func register(to worldStorage: WorldStorageRef) { + public static func register(to worldStorage: WorldStorageRef) async { guard worldStorage.chunkStorage.chunk(ofType: Self.self) == nil else { return } let queryRegistory = Self() - worldStorage.chunkStorage.addChunk(queryRegistory) + await worldStorage.chunkStorage.addChunk(queryRegistory) } diff --git a/Sources/ECS/Query/Query2.swift b/Sources/ECS/Query/Query2.swift index 731e2d1..859b54c 100644 --- a/Sources/ECS/Query/Query2.swift +++ b/Sources/ECS/Query/Query2.swift @@ -5,23 +5,23 @@ // Created by rrbox on 2023/08/12. // -final public class Query2: Chunk, SystemParameter { +public actor Query2: Chunk, SystemParameter { var components = [Entity: (ComponentRef, ComponentRef)]() var executes: [(Entity, inout C0, inout C1) -> ()] = [] - public override init() {} + public init() {} - public override func spawn(entity: Entity, entityRecord: EntityRecordRef) { + public func spawn(entity: Entity, entityRecord: EntityRecordRef) { guard let c0 = entityRecord.componentRef(C0.self), let c1 = entityRecord.componentRef(C1.self) else { return } self.components[entity] = (c0, c1) } - public override func despawn(entity: Entity) { + public func despawn(entity: Entity) { self.components.removeValue(forKey: entity) } - override func applyCurrentState(_ entityRecord: EntityRecordRef, forEntity entity: Entity) { + public func applyCurrentState(_ entityRecord: EntityRecordRef, forEntity entity: Entity) { guard let c0 = entityRecord.componentRef(C0.self), let c1 = entityRecord.componentRef(C1.self) else { self.components.removeValue(forKey: entity) @@ -49,14 +49,14 @@ final public class Query2: Chunk, SystemParameter } - public static func register(to worldStorage: WorldStorageRef) { + public static func register(to worldStorage: WorldStorageRef) async { guard worldStorage.chunkStorage.chunk(ofType: Self.self) == nil else { return } let queryRegistory = Self() - worldStorage.chunkStorage.addChunk(queryRegistory) + await worldStorage.chunkStorage.addChunk(queryRegistory) } diff --git a/Sources/ECS/Query/Query3.swift b/Sources/ECS/Query/Query3.swift index 8cf46ac..f4ea5be 100644 --- a/Sources/ECS/Query/Query3.swift +++ b/Sources/ECS/Query/Query3.swift @@ -5,23 +5,23 @@ // Created by rrbox on 2023/08/12. // -final public class Query3: Chunk, SystemParameter { +public actor Query3: Chunk, SystemParameter { var components = [Entity: (ComponentRef, ComponentRef, ComponentRef)]() - public override init() {} + public init() {} - public override func spawn(entity: Entity, entityRecord: EntityRecordRef) { + public func spawn(entity: Entity, entityRecord: EntityRecordRef) { guard let c0 = entityRecord.componentRef(C0.self), let c1 = entityRecord.componentRef(C1.self), let c2 = entityRecord.componentRef(C2.self) else { return } self.components[entity] = (c0, c1, c2) } - public override func despawn(entity: Entity) { + public func despawn(entity: Entity) { self.components.removeValue(forKey: entity) } - override func applyCurrentState(_ entityRecord: EntityRecordRef, forEntity entity: Entity) { + public func applyCurrentState(_ entityRecord: EntityRecordRef, forEntity entity: Entity) { guard let c0 = entityRecord.componentRef(C0.self), let c1 = entityRecord.componentRef(C1.self), let c2 = entityRecord.componentRef(C2.self) else { @@ -48,14 +48,14 @@ final public class Query3: Chunk, S return (references.0.value, references.1.value, references.2.value) } - public static func register(to worldStorage: WorldStorageRef) { + public static func register(to worldStorage: WorldStorageRef) async { guard worldStorage.chunkStorage.chunk(ofType: Self.self) == nil else { return } let queryRegistory = Self() - worldStorage.chunkStorage.addChunk(queryRegistory) + await worldStorage.chunkStorage.addChunk(queryRegistory) } diff --git a/Sources/ECS/Query/Query4.swift b/Sources/ECS/Query/Query4.swift index d954a74..2e2ad50 100644 --- a/Sources/ECS/Query/Query4.swift +++ b/Sources/ECS/Query/Query4.swift @@ -5,12 +5,12 @@ // Created by rrbox on 2023/08/12. // -final public class Query4: Chunk, SystemParameter { +public actor Query4: Chunk, SystemParameter { var components = [Entity: (ComponentRef, ComponentRef, ComponentRef, ComponentRef)]() - public override init() {} + public init() {} - public override func spawn(entity: Entity, entityRecord: EntityRecordRef) { + public func spawn(entity: Entity, entityRecord: EntityRecordRef) { guard let c0 = entityRecord.componentRef(C0.self), let c1 = entityRecord.componentRef(C1.self), let c2 = entityRecord.componentRef(C2.self), @@ -18,11 +18,11 @@ final public class Query4: Chunk, SystemParameter { +public actor Query5: Chunk, SystemParameter { var components = [Entity: (ComponentRef, ComponentRef, ComponentRef, ComponentRef, ComponentRef)]() - public override init() {} + public init() {} - public override func spawn(entity: Entity, entityRecord: EntityRecordRef) { + public func spawn(entity: Entity, entityRecord: EntityRecordRef) { guard let c0 = entityRecord.componentRef(C0.self), let c1 = entityRecord.componentRef(C1.self), let c2 = entityRecord.componentRef(C2.self), @@ -19,11 +19,11 @@ final public class Query5: Command { self.resrouce = resrouce } - override func runCommand(in world: World) { + override func runCommand(in world: World) async { world.addResource(self.resrouce) } } diff --git a/Sources/ECS/States/State.swift b/Sources/ECS/States/State.swift index e6c2aff..a9367f4 100644 --- a/Sources/ECS/States/State.swift +++ b/Sources/ECS/States/State.swift @@ -9,8 +9,8 @@ public protocol StateProtocol: Hashable { } -public class StateStorage { - class StateRegistry: WorldStorageElement { +public final class StateStorage { + actor StateRegistry: WorldStorageElement { var currentState: T var inactiveStates = [T]() @@ -18,6 +18,17 @@ public class StateStorage { self.currentState = currentState } + func setState(_ state: T) { + self.currentState = state + } + + func push(_ state: T) { + self.inactiveStates.append(state) + } + + func pop() -> T { + self.inactiveStates.removeLast() + } } class StateAssociatedSchedules: WorldStorageElement { @@ -48,82 +59,83 @@ public class StateStorage { self.storageRef.map.valueRef(ofType: StatesDidEnterInStartUp.self)!.body.schedules.insert(.didEnter(initialState)) } - func currentState(ofType type: T.Type) -> T? { - self.storageRef.map.valueRef(ofType: StateRegistry.self)?.body.currentState + func currentState(ofType type: T.Type) async -> T? { + await self.storageRef.map.valueRef(ofType: StateRegistry.self)?.body.currentState } func currentSchedulesWhichAssociatedStates() -> Set { self.storageRef.map.valueRef(ofType: StateAssociatedSchedules.self)!.body.schedules } - func enter(_ state: T) { + func enter(_ state: T) async { let stateRegistry = self.storageRef.map.valueRef(ofType: StateRegistry.self)!.body let schedulesManager = self.storageRef.map.valueRef(ofType: StateAssociatedSchedules.self)!.body - schedulesManager.schedules.remove(.onUpdate(stateRegistry.currentState)) - schedulesManager.schedules.remove(.onStackUpdate(stateRegistry.currentState)) + await schedulesManager.schedules.remove(.onUpdate(stateRegistry.currentState)) + await schedulesManager.schedules.remove(.onStackUpdate(stateRegistry.currentState)) // will exit - for system in self.storageRef.systemStorage.systems(.willExit(stateRegistry.currentState)) { - system.execute(self.storageRef) + for system in await self.storageRef.systemStorage.systems(.willExit(stateRegistry.currentState)) { + await system.execute(self.storageRef) } - self.storageRef.map.valueRef(ofType: StateRegistry.self)!.body.currentState = state + await self.storageRef.map.valueRef(ofType: StateRegistry.self)!.body.setState(state) // did enter for system in self.storageRef.systemStorage.systems(.didEnter(state)) { - system.execute(self.storageRef) + await system.execute(self.storageRef) } schedulesManager.schedules.insert(.onUpdate(state)) schedulesManager.schedules.insert(.onStackUpdate(state)) } - func push(_ state: T) { + func push(_ state: T) async { let registry = self.storageRef.map.valueRef(ofType: StateRegistry.self)!.body let schedulesManager = self.storageRef.map.valueRef(ofType: StateAssociatedSchedules.self)!.body // on pause - for system in self.storageRef.systemStorage.systems(.onPause(registry.currentState)) { - system.execute(self.storageRef) + for system in await self.storageRef.systemStorage.systems(.onPause(registry.currentState)) { + await system.execute(self.storageRef) } - schedulesManager.schedules.remove(.onUpdate(registry.currentState)) - schedulesManager.schedules.insert(.onInactiveUpdate(registry.currentState)) + await schedulesManager.schedules.remove(.onUpdate(registry.currentState)) + await schedulesManager.schedules.insert(.onInactiveUpdate(registry.currentState)) - registry.inactiveStates.append(registry.currentState) - registry.currentState = state + await registry.push(registry.currentState) + await registry.setState(state) schedulesManager.schedules.insert(.onUpdate(state)) schedulesManager.schedules.insert(.onStackUpdate(state)) // did enter for system in self.storageRef.systemStorage.systems(.didEnter(state)) { - system.execute(self.storageRef) + await system.execute(self.storageRef) } } - func pop(_ stateType: T.Type) { + func pop(_ stateType: T.Type) async { let registry = self.storageRef.map.valueRef(ofType: StateRegistry.self)!.body let schedulesManager = self.storageRef.map.valueRef(ofType: StateAssociatedSchedules.self)!.body // will exit - for system in self.storageRef.systemStorage.systems(.willExit(registry.currentState)) { - system.execute(self.storageRef) + for system in await self.storageRef.systemStorage.systems(.willExit(registry.currentState)) { + await system.execute(self.storageRef) } - schedulesManager.schedules.remove(.onUpdate(registry.currentState)) - schedulesManager.schedules.remove(.onStackUpdate(registry.currentState)) + await schedulesManager.schedules.remove(.onUpdate(registry.currentState)) + await schedulesManager.schedules.remove(.onStackUpdate(registry.currentState)) - registry.currentState = registry.inactiveStates.removeLast() +// registry.currentState = registry.inactiveStates.removeLast() + await registry.setState(registry.pop()) // on resume - for system in self.storageRef.systemStorage.systems(.onResume(registry.currentState)) { - system.execute(self.storageRef) + for system in await self.storageRef.systemStorage.systems(.onResume(registry.currentState)) { + await system.execute(self.storageRef) } - schedulesManager.schedules.remove(.onInactiveUpdate(registry.currentState)) - schedulesManager.schedules.insert(.onUpdate(registry.currentState)) + await schedulesManager.schedules.remove(.onInactiveUpdate(registry.currentState)) + await schedulesManager.schedules.insert(.onUpdate(registry.currentState)) } } diff --git a/Sources/ECS/States/StateControll.swift b/Sources/ECS/States/StateControll.swift index 9d821e0..bc29f4d 100644 --- a/Sources/ECS/States/StateControll.swift +++ b/Sources/ECS/States/StateControll.swift @@ -11,7 +11,7 @@ - note: 詳細は を参照してください. */ -final public class State: SystemParameter { +final public actor State: SystemParameter { let stateStrageRef: StateStorage let currentState: T @@ -25,17 +25,17 @@ final public class State: SystemParameter { } - public static func getParameter(from worldStorage: WorldStorageRef) -> State? { - State(stateStrageRef: worldStorage.stateStorage, - currentStaete: worldStorage.stateStorage.currentState(ofType: T.self)) + public static func getParameter(from worldStorage: WorldStorageRef) async -> State? { + await State(stateStrageRef: worldStorage.stateStorage, + currentStaete: worldStorage.stateStorage.currentState(ofType: T.self)) } /** 別の状態へ遷移します. - Parameter state: 遷移先の `State` を指定します. */ - public func enter(_ state: T) { - self.stateStrageRef.enter(state) + public func enter(_ state: T) async { + await self.stateStrageRef.enter(state) } /** @@ -48,8 +48,8 @@ final public class State: SystemParameter { - 以前の状態の ``Schedule/onInactiveUpdate(_:)`` と関連づけられたシステムが常時実行されます. - 以前の状態および `state` の ``Schedule/onStackUpdate(_:)`` と関連づけられたシステムが常時実行されます. */ - public func push(_ state: T) { - self.stateStrageRef.push(state) + public func push(_ state: T) async { + await self.stateStrageRef.push(state) } /** @@ -61,8 +61,8 @@ final public class State: SystemParameter { - 直前の状態の ``Schedule/onUpdate(_:)`` と関連したシステムが実行しなくなり, 戻り先の状態の ``Schedule/onUpdate(_:)`` のシステムが常時実行されます. - 直前の状態の ``Schedule/onStackUpdate(_:)`` のシステムが実行しなくなります. 戻り先の状態の ``Schedule/onStackUpdate(_:)`` のシステムは引き続き常時実行されます. */ - public func pop() { - self.stateStrageRef.pop(T.self) + public func pop() async { + await self.stateStrageRef.pop(T.self) } } diff --git a/Sources/ECS/System/SystemBuffer.swift b/Sources/ECS/System/SystemBuffer.swift index 39ab29a..3daef29 100644 --- a/Sources/ECS/System/SystemBuffer.swift +++ b/Sources/ECS/System/SystemBuffer.swift @@ -7,7 +7,7 @@ public class SystemExecute { init() {} - public func execute(_ worldStorage: WorldStorageRef) {} + public func execute(_ worldStorage: WorldStorageRef) async {} } final public class SystemStorage { diff --git a/Sources/ECS/SystemParameter/SystemParameter.swift b/Sources/ECS/SystemParameter/SystemParameter.swift index a80fecf..64481a9 100644 --- a/Sources/ECS/SystemParameter/SystemParameter.swift +++ b/Sources/ECS/SystemParameter/SystemParameter.swift @@ -9,7 +9,7 @@ /// /// - note: カスタムのパラメータを定義する場合にこのプロトコルを使用してください. public protocol SystemParameter: AnyObject { - static func register(to worldStorage: WorldStorageRef) - static func getParameter(from worldStorage: WorldStorageRef) -> Self? + static func register(to worldStorage: WorldStorageRef) async + static func getParameter(from worldStorage: WorldStorageRef) async -> Self? } diff --git a/Sources/ECS/Systems/System.swift b/Sources/ECS/Systems/System.swift index a41d45a..b42a74f 100644 --- a/Sources/ECS/Systems/System.swift +++ b/Sources/ECS/Systems/System.swift @@ -6,33 +6,33 @@ // class System: SystemExecute { - let execute: (P) -> () + let execute: (P) async -> () - init(_ execute: @escaping (P) -> ()) { + init(_ execute: @escaping (P) async -> ()) { self.execute = execute } - override func execute(_ worldStorageRef: WorldStorageRef) { - self.execute(P.getParameter(from: worldStorageRef)!) + override func execute(_ worldStorageRef: WorldStorageRef) async { + await self.execute(P.getParameter(from: worldStorageRef)!) } } public extension World { - @discardableResult func addSystem(_ schedule: Schedule, _ system: @escaping (P) -> ()) -> World { + @discardableResult func addSystem(_ schedule: Schedule, _ system: @escaping (P) async -> ()) async -> World { self.worldStorage.systemStorage.addSystem(schedule, System

(system)) - P.register(to: self.worldStorage) + await P.register(to: self.worldStorage) return self } } public extension EventResponderBuilder { - @discardableResult func addSystem(_ schedule: Schedule, _ system: @escaping (P) -> ()) -> EventResponderBuilder { + @discardableResult func addSystem(_ schedule: Schedule, _ system: @escaping (P) async -> ()) async -> EventResponderBuilder { if !self.systems.keys.contains(schedule) { self.systems[schedule] = [] } self.systems[schedule]!.append(System

(system)) - P.register(to: self.worldStorage) + await P.register(to: self.worldStorage) return self } } diff --git a/Sources/ECS/Systems/System2.swift b/Sources/ECS/Systems/System2.swift index b2e8df5..9481658 100644 --- a/Sources/ECS/Systems/System2.swift +++ b/Sources/ECS/Systems/System2.swift @@ -6,35 +6,35 @@ // class System2: SystemExecute { - let execute: (P0, P1) -> () + let execute: (P0, P1) async -> () - init(_ execute: @escaping (P0, P1) -> ()) { + init(_ execute: @escaping (P0, P1) async -> ()) { self.execute = execute } - override func execute(_ worldStorageRef: WorldStorageRef) { - self.execute(P0.getParameter(from: worldStorageRef)!, P1.getParameter(from: worldStorageRef)!) + override func execute(_ worldStorageRef: WorldStorageRef) async { + await self.execute(P0.getParameter(from: worldStorageRef)!, P1.getParameter(from: worldStorageRef)!) } } public extension World { - @discardableResult func addSystem(_ schedule: Schedule, _ system: @escaping (P0, P1) -> ()) -> World { + @discardableResult func addSystem(_ schedule: Schedule, _ system: @escaping (P0, P1) async -> ()) async -> World { self.worldStorage.systemStorage.addSystem(schedule, System2(system)) - P0.register(to: self.worldStorage) - P1.register(to: self.worldStorage) + await P0.register(to: self.worldStorage) + await P1.register(to: self.worldStorage) return self } } public extension EventResponderBuilder { - @discardableResult func addSystem(_ schedule: Schedule, _ system: @escaping (P0, P1) -> ()) -> EventResponderBuilder { + @discardableResult func addSystem(_ schedule: Schedule, _ system: @escaping (P0, P1) async -> ()) async -> EventResponderBuilder { if !self.systems.keys.contains(schedule) { self.systems[schedule] = [] } self.systems[schedule]?.append(System2(system)) - P0.register(to: self.worldStorage) - P1.register(to: self.worldStorage) + await P0.register(to: self.worldStorage) + await P1.register(to: self.worldStorage) return self } } diff --git a/Sources/ECS/Systems/System3.swift b/Sources/ECS/Systems/System3.swift index c76f470..f389aaf 100644 --- a/Sources/ECS/Systems/System3.swift +++ b/Sources/ECS/Systems/System3.swift @@ -6,28 +6,28 @@ // class System3: SystemExecute { - let execute: (P0, P1, P2) -> () + let execute: (P0, P1, P2) async -> () - init(_ execute: @escaping (P0, P1, P2) -> ()) { + init(_ execute: @escaping (P0, P1, P2) async -> ()) { self.execute = execute } - override func execute(_ worlfBuffer: WorldStorageRef) { - self.execute(P0.getParameter(from: worlfBuffer)!, - P1.getParameter(from: worlfBuffer)!, - P2.getParameter(from: worlfBuffer)!) + override func execute(_ worlfBuffer: WorldStorageRef) async { + await self.execute(P0.getParameter(from: worlfBuffer)!, + P1.getParameter(from: worlfBuffer)!, + P2.getParameter(from: worlfBuffer)!) } } public extension World { @discardableResult func addSystem( _ schedule: Schedule, - _ system: @escaping (P0, P1, P2) -> () - ) -> World { + _ system: @escaping (P0, P1, P2) async -> () + ) async -> World { self.worldStorage.systemStorage.addSystem(schedule, System3(system)) - P0.register(to: self.worldStorage) - P1.register(to: self.worldStorage) - P2.register(to: self.worldStorage) + await P0.register(to: self.worldStorage) + await P1.register(to: self.worldStorage) + await P2.register(to: self.worldStorage) return self } } @@ -35,16 +35,16 @@ public extension World { public extension EventResponderBuilder { @discardableResult func addSystem( _ schedule: Schedule, - _ system: @escaping (P0, P1, P2) -> () - ) -> EventResponderBuilder { + _ system: @escaping (P0, P1, P2) async -> () + ) async -> EventResponderBuilder { if !self.systems.keys.contains(schedule) { self.systems[schedule] = [] } self.systems[schedule]?.append(System3(system)) - P0.register(to: self.worldStorage) - P1.register(to: self.worldStorage) - P2.register(to: self.worldStorage) + await P0.register(to: self.worldStorage) + await P1.register(to: self.worldStorage) + await P2.register(to: self.worldStorage) return self } } diff --git a/Sources/ECS/Systems/System4.swift b/Sources/ECS/Systems/System4.swift index ee8ff2a..558e632 100644 --- a/Sources/ECS/Systems/System4.swift +++ b/Sources/ECS/Systems/System4.swift @@ -6,30 +6,30 @@ // class System4: SystemExecute { - let execute: (P0, P1, P2, P3) -> () + let execute: (P0, P1, P2, P3) async -> () - init(_ execute: @escaping (P0, P1, P2, P3) -> ()) { + init(_ execute: @escaping (P0, P1, P2, P3) async -> ()) { self.execute = execute } - override func execute(_ worldStorageRef: WorldStorageRef) { - self.execute(P0.getParameter(from: worldStorageRef)!, - P1.getParameter(from: worldStorageRef)!, - P2.getParameter(from: worldStorageRef)!, - P3.getParameter(from: worldStorageRef)!) + override func execute(_ worldStorageRef: WorldStorageRef) async { + await self.execute(P0.getParameter(from: worldStorageRef)!, + P1.getParameter(from: worldStorageRef)!, + P2.getParameter(from: worldStorageRef)!, + P3.getParameter(from: worldStorageRef)!) } } public extension World { @discardableResult func addSystem( _ schedule: Schedule, - _ system: @escaping (P0, P1, P2, P3) -> () - ) -> World { + _ system: @escaping (P0, P1, P2, P3) async -> () + ) async -> World { self.worldStorage.systemStorage.addSystem(schedule, System4(system)) - P0.register(to: self.worldStorage) - P1.register(to: self.worldStorage) - P2.register(to: self.worldStorage) - P3.register(to: self.worldStorage) + await P0.register(to: self.worldStorage) + await P1.register(to: self.worldStorage) + await P2.register(to: self.worldStorage) + await P3.register(to: self.worldStorage) return self } } @@ -37,17 +37,17 @@ public extension World { public extension EventResponderBuilder { @discardableResult func addSystem( _ schedule: Schedule, - _ system: @escaping (P0, P1, P2, P3) -> () - ) -> EventResponderBuilder { + _ system: @escaping (P0, P1, P2, P3) async -> () + ) async -> EventResponderBuilder { if !self.systems.keys.contains(schedule) { self.systems[schedule] = [] } self.systems[schedule]?.append(System4(system)) - P0.register(to: self.worldStorage) - P1.register(to: self.worldStorage) - P2.register(to: self.worldStorage) - P3.register(to: self.worldStorage) + await P0.register(to: self.worldStorage) + await P1.register(to: self.worldStorage) + await P2.register(to: self.worldStorage) + await P3.register(to: self.worldStorage) return self } } diff --git a/Sources/ECS/Systems/System5.swift b/Sources/ECS/Systems/System5.swift index cbb2bb5..407405d 100644 --- a/Sources/ECS/Systems/System5.swift +++ b/Sources/ECS/Systems/System5.swift @@ -6,32 +6,32 @@ // class System5: SystemExecute { - let execute: (P0, P1, P2, P3, P4) -> () + let execute: (P0, P1, P2, P3, P4) async -> () - init(_ execute: @escaping (P0, P1, P2, P3, P4) -> ()) { + init(_ execute: @escaping (P0, P1, P2, P3, P4) async -> ()) { self.execute = execute } - override func execute(_ worldStorageRef: WorldStorageRef) { - self.execute(P0.getParameter(from: worldStorageRef)!, - P1.getParameter(from: worldStorageRef)!, - P2.getParameter(from: worldStorageRef)!, - P3.getParameter(from: worldStorageRef)!, - P4.getParameter(from: worldStorageRef)!) + override func execute(_ worldStorageRef: WorldStorageRef) async { + await self.execute(P0.getParameter(from: worldStorageRef)!, + P1.getParameter(from: worldStorageRef)!, + P2.getParameter(from: worldStorageRef)!, + P3.getParameter(from: worldStorageRef)!, + P4.getParameter(from: worldStorageRef)!) } } public extension World { @discardableResult func addSystem( _ schedule: Schedule, - _ system: @escaping (P0, P1, P2, P3, P4) -> () - ) -> World { + _ system: @escaping (P0, P1, P2, P3, P4) async -> () + ) async -> World { self.worldStorage.systemStorage.addSystem(schedule, System5(system)) - P0.register(to: self.worldStorage) - P1.register(to: self.worldStorage) - P2.register(to: self.worldStorage) - P3.register(to: self.worldStorage) - P4.register(to: self.worldStorage) + await P0.register(to: self.worldStorage) + await P1.register(to: self.worldStorage) + await P2.register(to: self.worldStorage) + await P3.register(to: self.worldStorage) + await P4.register(to: self.worldStorage) return self } } @@ -39,18 +39,18 @@ public extension World { public extension EventResponderBuilder { @discardableResult func addSystem( _ schedule: Schedule, - _ system: @escaping (P0, P1, P2, P3, P4) -> () - ) -> EventResponderBuilder { + _ system: @escaping (P0, P1, P2, P3, P4) async -> () + ) async -> EventResponderBuilder { if !self.systems.keys.contains(schedule) { self.systems[schedule] = [] } self.systems[schedule]?.append(System5(system)) - P0.register(to: self.worldStorage) - P1.register(to: self.worldStorage) - P2.register(to: self.worldStorage) - P3.register(to: self.worldStorage) - P4.register(to: self.worldStorage) + await P0.register(to: self.worldStorage) + await P1.register(to: self.worldStorage) + await P2.register(to: self.worldStorage) + await P3.register(to: self.worldStorage) + await P4.register(to: self.worldStorage) return self } } diff --git a/Sources/ECS/WorldMethods/FirstFrame.swift b/Sources/ECS/WorldMethods/FirstFrame.swift index 2a1138b..4597131 100644 --- a/Sources/ECS/WorldMethods/FirstFrame.swift +++ b/Sources/ECS/WorldMethods/FirstFrame.swift @@ -6,12 +6,12 @@ // class FirstFrameCommand: Command { - override func runCommand(in world: World) { + override func runCommand(in world: World) async { world.updateSchedule = .update } } // Delta time resource の設定のため, 一番最初のフレームはスキップします. -func firstFrameSystem(commands: Commands) { - commands.push(command: FirstFrameCommand()) +func firstFrameSystem(commands: Commands) async { + await commands.push(command: FirstFrameCommand()) } diff --git a/Sources/ECS/WorldMethods/World+EntityCommands.swift b/Sources/ECS/WorldMethods/World+EntityCommands.swift index e0c9a76..b65a4cc 100644 --- a/Sources/ECS/WorldMethods/World+EntityCommands.swift +++ b/Sources/ECS/WorldMethods/World+EntityCommands.swift @@ -7,20 +7,20 @@ extension World { /// Entity に Component を追加します. - func addComponent(_ component: ComponentType, forEntity entity: Entity) { + func addComponent(_ component: ComponentType, forEntity entity: Entity) async { let archetype = self.entityRecord(forEntity: entity)! if let componentRef = archetype.componentRef(ComponentType.self) { componentRef.value = component } archetype.addComponent(component) - self.worldStorage.chunkStorage.applyCurrentState(archetype, forEntity: entity) + await self.worldStorage.chunkStorage.applyCurrentState(archetype, forEntity: entity) } /// Entity から Component を削除します. - func removeComponent(ofType type: ComponentType.Type, fromEntity entity: Entity) { + func removeComponent(ofType type: ComponentType.Type, fromEntity entity: Entity) async { let archetype = self.entityRecord(forEntity: entity)! archetype.removeComponent(ofType: ComponentType.self) - self.worldStorage.chunkStorage.applyCurrentState(archetype, forEntity: entity) + await self.worldStorage.chunkStorage.applyCurrentState(archetype, forEntity: entity) } } diff --git a/Sources/ECS/WorldMethods/World+SetUp.swift b/Sources/ECS/WorldMethods/World+SetUp.swift index 0521549..afb3850 100644 --- a/Sources/ECS/WorldMethods/World+SetUp.swift +++ b/Sources/ECS/WorldMethods/World+SetUp.swift @@ -6,17 +6,18 @@ // public extension World { - func setUpWorld() { + @MainActor + func setUpWorld() async { for system in self.worldStorage.systemStorage.systems(.startUp) { - system.execute(self.worldStorage) + await system.execute(self.worldStorage) } // 初期値として設定された state に対して did move schedule で関連づけられた system を実行します. for schedule in self.worldStorage.map.valueRef(ofType: StateStorage.StatesDidEnterInStartUp.self)!.body.schedules { for system in self.worldStorage.systemStorage.systems(schedule) { - system.execute(self.worldStorage) + await system.execute(self.worldStorage) } } - self.applyCommands() - self.worldStorage.chunkStorage.applyEntityQueue() + await self.applyCommands() + await self.worldStorage.chunkStorage.applyEntityQueue() } } diff --git a/Sources/ECS/WorldMethods/World+Spawn.swift b/Sources/ECS/WorldMethods/World+Spawn.swift index f5d7837..74e538f 100644 --- a/Sources/ECS/WorldMethods/World+Spawn.swift +++ b/Sources/ECS/WorldMethods/World+Spawn.swift @@ -23,9 +23,9 @@ extension World { /// /// ``Commands/spawn()`` が実行された後, フレームが終了するタイミングでこの関数が実行されます. /// entity へのコンポーネントの登録などは, push の後に行われます. - func push(entity: Entity, entityRecord: EntityRecordRef) { + func push(entity: Entity, entityRecord: EntityRecordRef) async { self.insert(entity: entity, entityRecord: entityRecord) - self.worldStorage + await self.worldStorage .chunkStorage .push(entity: entity, entityRecord: entityRecord) @@ -38,9 +38,9 @@ extension World { /// Entity を削除します. /// /// ``Commands/despawn()`` が実行された後, フレームが終了するタイミングでこの関数が実行されます. - func despawn(entity: Entity) { + func despawn(entity: Entity) async { self.remove(entity: entity) - self.worldStorage + await self.worldStorage .chunkStorage .despawn(entity: entity) diff --git a/Sources/ECS/WorldMethods/World+Update.swift b/Sources/ECS/WorldMethods/World+Update.swift index 18a6820..ecf2db2 100644 --- a/Sources/ECS/WorldMethods/World+Update.swift +++ b/Sources/ECS/WorldMethods/World+Update.swift @@ -21,37 +21,44 @@ public extension World { - note: 最初のフレーム (current time = 0) は準備用フレームとして実行されるため, システムが実行されません. */ - func update(currentTime: TimeInterval) { + func update(currentTime: TimeInterval) async { let currentTimeResource = self.worldStorage.resourceBuffer.resource(ofType: CurrentTime.self)! self.worldStorage.resourceBuffer.resource(ofType: DeltaTime.self)?.resource = DeltaTime(value: currentTime - currentTimeResource.resource.value) currentTimeResource.resource = CurrentTime(value: currentTime) - for system in self.worldStorage.systemStorage.systems(self.updateSchedule) { - system.execute(self.worldStorage) - } - - // activate な state を shcedule によって紐づけられた system を実行します. - for schedule in self.worldStorage.stateStorage.currentSchedulesWhichAssociatedStates() { - for system in self.worldStorage.systemStorage.systems(schedule) { - system.execute(self.worldStorage) + await withTaskGroup(of: Void.self) { group in + for system in self.worldStorage.systemStorage.systems(self.updateSchedule) { + group.addTask { + await system.execute(self.worldStorage) + } + } + + // activate な state を shcedule によって紐づけられた system を実行します. + for schedule in self.worldStorage.stateStorage.currentSchedulesWhichAssociatedStates() { + for system in self.worldStorage.systemStorage.systems(schedule) { + group.addTask { + await system.execute(self.worldStorage) + } + } + } } // world が受信した event を event system に発信します. - self.applyEventQueue() + await self.applyEventQueue() - self.applyCommands() + await self.applyCommands() // will despawn event を配信します. - self.applyCommandsEventQueue(eventOfType: WillDespawnEvent.self) + await self.applyCommandsEventQueue(eventOfType: WillDespawnEvent.self) // apply commands の際に push された entity を chunk に割り振ります. - self.worldStorage.chunkStorage.applyEntityQueue() + await self.worldStorage.chunkStorage.applyEntityQueue() // Did Spawn event を event system に発信します. - self.applyCommandsEventQueue(eventOfType: DidSpawnEvent.self) + await self.applyCommandsEventQueue(eventOfType: DidSpawnEvent.self) } } diff --git a/Sources/PlugIns/Graphic2D/Commands/EntityCommands+Graphic.swift b/Sources/PlugIns/Graphic2D/Commands/EntityCommands+Graphic.swift index d2ae07e..5ec0c8c 100644 --- a/Sources/PlugIns/Graphic2D/Commands/EntityCommands+Graphic.swift +++ b/Sources/PlugIns/Graphic2D/Commands/EntityCommands+Graphic.swift @@ -9,14 +9,14 @@ import SpriteKit import ECS public extension EntityCommands { - @discardableResult func setGraphic(_ node: Node) -> EntityCommands { - self.pushCommand(SetGraphic(node: node, entity: self.id())) - return self.addComponent(Graphic(node: node)).addComponent(Graphic(node: node)) + @discardableResult func setGraphic(_ node: Node) async -> EntityCommands { + await self.pushCommand(SetGraphic(node: node, entity: self.id())) + return await self.addComponent(Graphic(node: node)).addComponent(Graphic(node: node)) } - @discardableResult func removeGraphic() -> EntityCommands { - self.pushCommand(RemoveGraphic(entity: self.id())) - return self.removeComponent(ofType: Graphic.self).removeComponent(ofType: Graphic.self) + @discardableResult func removeGraphic() async -> EntityCommands { + await self.pushCommand(RemoveGraphic(entity: self.id())) + return await self.removeComponent(ofType: Graphic.self).removeComponent(ofType: Graphic.self) } } diff --git a/Sources/PlugIns/Graphic2D/Commands/RemoveGraphic.swift b/Sources/PlugIns/Graphic2D/Commands/RemoveGraphic.swift index 131b6f0..8031895 100644 --- a/Sources/PlugIns/Graphic2D/Commands/RemoveGraphic.swift +++ b/Sources/PlugIns/Graphic2D/Commands/RemoveGraphic.swift @@ -8,7 +8,7 @@ import ECS class RemoveGraphic: EntityCommand { - override func runCommand(in world: World) { + func runCommand(in world: World) { world.removeGraphic(fromEntity: self.entity) } } diff --git a/Sources/PlugIns/Graphic2D/Commands/SetGraphic.swift b/Sources/PlugIns/Graphic2D/Commands/SetGraphic.swift index 420881d..aa4e92a 100644 --- a/Sources/PlugIns/Graphic2D/Commands/SetGraphic.swift +++ b/Sources/PlugIns/Graphic2D/Commands/SetGraphic.swift @@ -16,7 +16,7 @@ class SetGraphic: EntityCommand { super.init(entity: entity) } - override func runCommand(in world: World) { + func runCommand(in world: World) { world.setGraphic(self.node, forEntity: self.entity) } } diff --git a/Sources/PlugIns/Scene/Scene.swift b/Sources/PlugIns/Scene/Scene.swift index aa5fa25..dd9f266 100644 --- a/Sources/PlugIns/Scene/Scene.swift +++ b/Sources/PlugIns/Scene/Scene.swift @@ -21,16 +21,20 @@ open class Scene: SKScene { final public override func didMove(to view: SKView) { self._world = self.buildWorld() - self._world?.setUpWorld() + Task { + await self._world?.setUpWorld() + } } final public override func update(_ currentTime: TimeInterval) { - self._world?.update(currentTime: currentTime) + Task { + await self._world?.update(currentTime: currentTime) + } } - public func restartWorld() { + public func restartWorld() async { self._world = self.buildWorld() - self._world.setUpWorld() + await self._world.setUpWorld() } } diff --git a/Tests/ObjectLinkPlugInTests/ObjectLinkPlugInTests.swift b/Tests/ObjectLinkPlugInTests/ObjectLinkPlugInTests.swift index 4a1fca7..63b7d49 100644 --- a/Tests/ObjectLinkPlugInTests/ObjectLinkPlugInTests.swift +++ b/Tests/ObjectLinkPlugInTests/ObjectLinkPlugInTests.swift @@ -6,7 +6,7 @@ // import XCTest -@testable import ECS_ObjectLink +import ECS_ObjectLink final class ObjectLinkTests: XCTestCase { diff --git a/Tests/ecs-swiftTests/ChunkTests.swift b/Tests/ecs-swiftTests/ChunkTests.swift index 810e189..ba8cf00 100644 --- a/Tests/ecs-swiftTests/ChunkTests.swift +++ b/Tests/ecs-swiftTests/ChunkTests.swift @@ -5,55 +5,65 @@ // Created by rrbox on 2023/08/11. // +#if DEBUG + import XCTest @testable import ECS class TestChunk: Chunk { var entities = [Entity: EntityRecordRef]() - override func spawn(entity: Entity, entityRecord: EntityRecordRef) { + func spawn(entity: Entity, entityRecord: EntityRecordRef) async { self.entities[entity] = entityRecord } - override func despawn(entity: Entity) { + func despawn(entity: Entity) async { self.entities.removeValue(forKey: entity) } + + func applyCurrentState(_ entityRecord: EntityRecordRef, forEntity entity: Entity) async { + + } } class TestChunk_2: Chunk { var entities = [Entity: EntityRecordRef]() - override func spawn(entity: Entity, entityRecord: EntityRecordRef) { + func spawn(entity: Entity, entityRecord: EntityRecordRef) { self.entities[entity] = entityRecord } - override func despawn(entity: Entity) { + func despawn(entity: Entity) { self.entities.removeValue(forKey: entity) } + + func applyCurrentState(_ entityRecord: EntityRecordRef, forEntity entity: Entity) async { + + } } final class ChunkTests: XCTestCase { - func testInterface() { + func testInterface() async { let mockEntities = [Entity(), Entity(), Entity(), Entity(), Entity()] let world = World() // Spawn された entity を単に蓄積するだけの test 用の chunk です. let testChunk = TestChunk() let testChunk_2 = TestChunk_2() - world.worldStorage.chunkStorage.addChunk(testChunk) - world.worldStorage.chunkStorage.addChunk(testChunk_2) + await world.worldStorage.chunkStorage.addChunk(testChunk) + await world.worldStorage.chunkStorage.addChunk(testChunk_2) // chunk interface を介して chunk に entity を push します(回数: 5回). for entity in mockEntities { - world.push(entity: entity, entityRecord: EntityRecordRef()) + await world.push(entity: entity, entityRecord: EntityRecordRef()) } - world.worldStorage.chunkStorage.applyEntityQueue() + await world.worldStorage.chunkStorage.applyEntityQueue() XCTAssertEqual(testChunk.entities.count, 5) XCTAssertEqual(testChunk_2.entities.count, 5) // chunk interface を介して chunk から entity を削除します. for entity in mockEntities { - world.despawn(entity: entity) + await world.despawn(entity: entity) } XCTAssertEqual(testChunk.entities.count, 0) @@ -61,3 +71,5 @@ final class ChunkTests: XCTestCase { } } + +#endif diff --git a/Tests/ecs-swiftTests/CommandsTests.swift b/Tests/ecs-swiftTests/CommandsTests.swift index 9c5658f..caa60dd 100644 --- a/Tests/ecs-swiftTests/CommandsTests.swift +++ b/Tests/ecs-swiftTests/CommandsTests.swift @@ -5,6 +5,8 @@ // Created by rrbox on 2023/08/11. // +#if DEBUG + import XCTest @testable import ECS @@ -13,8 +15,8 @@ class TestCommand_Spawn: Command { init(entity: Entity) { self.entity = entity } - override func runCommand(in world: World) { - world.push(entity: self.entity, entityRecord: EntityRecordRef()) + override func runCommand(in world: World) async { + await world.push(entity: self.entity, entityRecord: EntityRecordRef()) } } @@ -23,13 +25,13 @@ class TestCommand_Despawn: Command { init(entity: Entity) { self.entity = entity } - override func runCommand(in world: World) { - world.despawn(entity: self.entity) + override func runCommand(in world: World) async { + await world.despawn(entity: self.entity) } } final class CommandsTests: XCTestCase { - func testCommands() { + func testCommands() async { let world = World() world.worldStorage.commandsStorage.setCommands(Commands()) let commands = world.worldStorage.commandsStorage.commands()! @@ -37,23 +39,29 @@ final class CommandsTests: XCTestCase { let testEntities = [Entity(), Entity(), Entity()] for testEntity in testEntities { - commands.push(command: TestCommand_Spawn(entity: testEntity)) + await commands.push(command: TestCommand_Spawn(entity: testEntity)) } - XCTAssertEqual(commands.commandQueue.count, 3) - world.applyCommands() + var commandsCount = await commands.commandQueue.count + XCTAssertEqual(commandsCount, 3) + await world.applyCommands() - XCTAssertEqual(commands.commandQueue.count, 0) + commandsCount = await commands.commandQueue.count + XCTAssertEqual(commandsCount, 0) XCTAssertEqual(world.entities.sequence.count, 3) for testEntity in testEntities { - commands.push(command: TestCommand_Despawn(entity: testEntity)) + await commands.push(command: TestCommand_Despawn(entity: testEntity)) } - XCTAssertEqual(commands.commandQueue.count, 3) - world.applyCommands() + commandsCount = await commands.commandQueue.count + XCTAssertEqual(commandsCount, 3) + await world.applyCommands() - XCTAssertEqual(commands.commandQueue.count, 0) + commandsCount = await commands.commandQueue.count + XCTAssertEqual(commandsCount, 0) XCTAssertEqual(world.entities.sequence.count, 0) } } + +#endif diff --git a/Tests/ecs-swiftTests/EntityCommandsTests.swift b/Tests/ecs-swiftTests/EntityCommandsTests.swift index 85a0b4d..a49e829 100644 --- a/Tests/ecs-swiftTests/EntityCommandsTests.swift +++ b/Tests/ecs-swiftTests/EntityCommandsTests.swift @@ -5,34 +5,38 @@ // Created by rrbox on 2023/08/11. // +#if DEBUG + import XCTest @testable import ECS final class EntityCommandsTests: XCTestCase { - func testEntityCommands() { + func testEntityCommands() async { let commands = Commands() let world = World() world.worldStorage.commandsStorage.setCommands(commands) // world に entity を生成し, component を追加し, id(id としての entity) を受け取ります. - let entity = commands.spawn() + let entity = await commands.spawn() .addComponent(TestComponent(content: "test")) .id() - world.applyCommands() + await world.applyCommands() XCTAssertEqual(world.entityRecord(forEntity: entity)!.componentRef(TestComponent.self)!.value.content, "test") - commands.entity(entity)?.removeComponent(ofType: TestComponent.self) - world.applyCommands() + await commands.entity(entity)?.removeComponent(ofType: TestComponent.self) + await world.applyCommands() // world 内に entity が存在し, component が削除されていることをテストします. XCTAssertNotNil(world.entityRecord(forEntity: entity)) XCTAssertNil(world.entityRecord(forEntity: entity)!.componentRef(TestComponent.self)) - commands.despawn(entity: entity) - world.applyCommands() + await commands.despawn(entity: entity) + await world.applyCommands() // world から entity が削除されたことをテストします. XCTAssertNil(world.entityRecord(forEntity: entity)) } } + +#endif diff --git a/Tests/ecs-swiftTests/EventTests.swift b/Tests/ecs-swiftTests/EventTests.swift index b68ddd9..7b03b3b 100644 --- a/Tests/ecs-swiftTests/EventTests.swift +++ b/Tests/ecs-swiftTests/EventTests.swift @@ -6,17 +6,17 @@ // import XCTest -@testable import ECS +import ECS struct TestEvent: EventProtocol { let name: String } -func testEvent(event: EventReader, eventWriter: EventWriter, commands: Commands, currentTime: Resource) { +func testEvent(event: EventReader, eventWriter: EventWriter, commands: Commands, currentTime: Resource) async { print("---test event read---") print("frame:", currentTime.resource.value) print("<- read event:", event.value.name) - let spawned = commands.spawn().addComponent(TestComponent(content: event.value.name)).id() + let spawned = await commands.spawn().addComponent(TestComponent(content: event.value.name)).id() print("-> spawn:", spawned) print("-> event send:", "\"link\"") eventWriter.send(value: TestEvent(name: "[\(currentTime.resource.value)]: link")) @@ -32,12 +32,12 @@ func setUp(eventWriter: EventWriter) { print() } -func spawnedEntitySystem(eventReader: EventReader, commands: Commands, currentTime: Resource) { +func spawnedEntitySystem(eventReader: EventReader, commands: Commands, currentTime: Resource) async { print("---spawned entity event read---") print("frame:", currentTime.resource.value) print("<- spawned(receive):", eventReader.value.spawnedEntity) print("-> despawn:", eventReader.value.spawnedEntity) - commands.despawn(entity: eventReader.value.spawnedEntity) + await commands.despawn(entity: eventReader.value.spawnedEntity) print("---") print() } @@ -51,23 +51,25 @@ func despanedEntitySystem(eventReader: EventReader, commands: } final class EventTests: XCTestCase { - func testEvent() { + func testEvent() async { print() - let world = World() + let world = await World() .addEventStreamer(eventType: TestEvent.self) .buildEventResponder(TestEvent.self, { responder in - responder.addSystem(.update, testEvent(event:eventWriter:commands:currentTime:)) + await responder.addSystem(.update, testEvent(event:eventWriter:commands:currentTime:)) }) .addSystem(.startUp, setUp(eventWriter:)) .addSystem(.didSpawn, spawnedEntitySystem(eventReader:commands:currentTime:)) .addSystem(.willDespawn, despanedEntitySystem(eventReader:commands:currentTime:)) - world.setUpWorld() + await world.setUpWorld() + + await world.update(currentTime: 0) + await world.update(currentTime: 1) + await world.update(currentTime: 2) + await world.update(currentTime: 3) - world.update(currentTime: 0) - world.update(currentTime: 1) - world.update(currentTime: 2) - world.update(currentTime: 3) } } + diff --git a/Tests/ecs-swiftTests/FilteredQueryTests.swift b/Tests/ecs-swiftTests/FilteredQueryTests.swift new file mode 100644 index 0000000..9c63ea9 --- /dev/null +++ b/Tests/ecs-swiftTests/FilteredQueryTests.swift @@ -0,0 +1,31 @@ +// +// FilteredQueryTests.swift +// +// +// Created by rrbox on 2024/01/16. +// + +#if DEBUG + +import XCTest +@testable import ECS + + + +final class FilteredQueryTests: XCTestCase { + func testFilteredQuery() async { + let testQuery = Query() + let filtered1 = Filtered, With>() + let filtered2 = Filtered, And, With>>() + + let world = World() + + await world.worldStorage.chunkStorage.addChunk(testQuery) + await world.worldStorage.chunkStorage.addChunk(filtered1) + await world.worldStorage.chunkStorage.addChunk(filtered2) + + } +} + +#endif + diff --git a/Tests/ecs-swiftTests/QueryTests.swift b/Tests/ecs-swiftTests/QueryTests.swift index dc66b38..45d2540 100644 --- a/Tests/ecs-swiftTests/QueryTests.swift +++ b/Tests/ecs-swiftTests/QueryTests.swift @@ -5,11 +5,53 @@ // Created by rrbox on 2023/08/11. // +#if DEBUG + import XCTest @testable import ECS +extension Query { + var count: Int { + get async { + return self.components.count + } + } +} +extension Query2 { + var count: Int { + get async { + return self.components.count + } + } +} + +extension Query3 { + var count: Int { + get async { + return self.components.count + } + } +} + +extension Query4 { + var count: Int { + get async { + return self.components.count + } + } +} + +extension Query5 { + var count: Int { + get async { + return self.components.count + } + } +} + + final class QueryTests: XCTestCase { - func testQuery() { + func testQuery() async { let testQuery = Query() let testQuery2 = Query2() let testQuery3 = Query3() @@ -18,73 +60,97 @@ final class QueryTests: XCTestCase { let world = World() - world.worldStorage.chunkStorage.addChunk(testQuery) - world.worldStorage.chunkStorage.addChunk(testQuery2) - world.worldStorage.chunkStorage.addChunk(testQuery3) - world.worldStorage.chunkStorage.addChunk(testQuery4) - world.worldStorage.chunkStorage.addChunk(testQuery5) + await world.worldStorage.chunkStorage.addChunk(testQuery) + await world.worldStorage.chunkStorage.addChunk(testQuery2) + await world.worldStorage.chunkStorage.addChunk(testQuery3) + await world.worldStorage.chunkStorage.addChunk(testQuery4) + await world.worldStorage.chunkStorage.addChunk(testQuery5) let commands = world.worldStorage.commandsStorage.commands()! - let testEntity = commands.spawn().addComponent(TestComponent(content: "test")).id() + let testEntity = await commands.spawn().addComponent(TestComponent(content: "test")).id() + + await world.applyCommands() + + let test = { () async in + await ( + testQuery.count, + testQuery2.count, + testQuery3.count, + testQuery4.count, + testQuery5.count + ) + } - world.applyCommands() + let test_0 = await test() - XCTAssertEqual(testQuery.components.count, 1) - XCTAssertEqual(testQuery2.components.count, 0) - XCTAssertEqual(testQuery3.components.count, 0) - XCTAssertEqual(testQuery4.components.count, 0) - XCTAssertEqual(testQuery5.components.count, 0) + XCTAssertEqual(test_0.0, 1) + XCTAssertEqual(test_0.1, 0) + XCTAssertEqual(test_0.2, 0) + XCTAssertEqual(test_0.3, 0) + XCTAssertEqual(test_0.4, 0) - commands.entity(testEntity)?.addComponent(TestComponent2(content: "test2")) + await commands.entity(testEntity)?.addComponent(TestComponent2(content: "test2")) - world.applyCommands() + await world.applyCommands() - XCTAssertEqual(testQuery.components.count, 1) - XCTAssertEqual(testQuery2.components.count, 1) - XCTAssertEqual(testQuery3.components.count, 0) - XCTAssertEqual(testQuery4.components.count, 0) - XCTAssertEqual(testQuery5.components.count, 0) + let test_1 = await test() - commands.entity(testEntity)?.addComponent(TestComponent3(content: "test2")) + XCTAssertEqual(test_1.0, 1) + XCTAssertEqual(test_1.1, 1) + XCTAssertEqual(test_1.2, 0) + XCTAssertEqual(test_1.3, 0) + XCTAssertEqual(test_1.4, 0) - world.applyCommands() + await commands.entity(testEntity)?.addComponent(TestComponent3(content: "test2")) - XCTAssertEqual(testQuery.components.count, 1) - XCTAssertEqual(testQuery2.components.count, 1) - XCTAssertEqual(testQuery3.components.count, 1) - XCTAssertEqual(testQuery4.components.count, 0) - XCTAssertEqual(testQuery5.components.count, 0) + await world.applyCommands() - commands.entity(testEntity)?.addComponent(TestComponent4(content: "test2")) + let test_2 = await test() - world.applyCommands() + XCTAssertEqual(test_2.0, 1) + XCTAssertEqual(test_2.1, 1) + XCTAssertEqual(test_2.2, 1) + XCTAssertEqual(test_2.3, 0) + XCTAssertEqual(test_2.4, 0) - XCTAssertEqual(testQuery.components.count, 1) - XCTAssertEqual(testQuery2.components.count, 1) - XCTAssertEqual(testQuery3.components.count, 1) - XCTAssertEqual(testQuery4.components.count, 1) - XCTAssertEqual(testQuery5.components.count, 0) + await commands.entity(testEntity)?.addComponent(TestComponent4(content: "test2")) - commands.entity(testEntity)?.addComponent(TestComponent5(content: "test2")) + await world.applyCommands() - world.applyCommands() + let test_3 = await test() - XCTAssertEqual(testQuery.components.count, 1) - XCTAssertEqual(testQuery2.components.count, 1) - XCTAssertEqual(testQuery3.components.count, 1) - XCTAssertEqual(testQuery4.components.count, 1) - XCTAssertEqual(testQuery5.components.count, 1) + XCTAssertEqual(test_3.0, 1) + XCTAssertEqual(test_3.1, 1) + XCTAssertEqual(test_3.2, 1) + XCTAssertEqual(test_3.3, 1) + XCTAssertEqual(test_3.4, 0) - commands.entity(testEntity)?.removeComponent(ofType: TestComponent.self) + await commands.entity(testEntity)?.addComponent(TestComponent5(content: "test2")) - world.applyCommands() + await world.applyCommands() - XCTAssertEqual(testQuery.components.count, 0) - XCTAssertEqual(testQuery2.components.count, 0) - XCTAssertEqual(testQuery3.components.count, 0) - XCTAssertEqual(testQuery4.components.count, 0) - XCTAssertEqual(testQuery5.components.count, 0) + let test_4 = await test() + + XCTAssertEqual(test_4.0, 1) + XCTAssertEqual(test_4.1, 1) + XCTAssertEqual(test_4.2, 1) + XCTAssertEqual(test_4.3, 1) + XCTAssertEqual(test_4.4, 1) + + await commands.entity(testEntity)?.removeComponent(ofType: TestComponent.self) + + await world.applyCommands() + + let test_5 = await test() + + XCTAssertEqual(test_5.0, 0) + XCTAssertEqual(test_5.1, 0) + XCTAssertEqual(test_5.2, 0) + XCTAssertEqual(test_5.3, 0) + XCTAssertEqual(test_5.4, 0) } } + +#endif diff --git a/Tests/ecs-swiftTests/SystemTests.swift b/Tests/ecs-swiftTests/SystemTests.swift index eda37dd..87499f4 100644 --- a/Tests/ecs-swiftTests/SystemTests.swift +++ b/Tests/ecs-swiftTests/SystemTests.swift @@ -8,12 +8,12 @@ import XCTest import ECS -func testSetUp(commands: Commands) { +func testSetUp(commands: Commands) async { print("set up") - commands.spawn() + await commands.spawn() .addComponent(TestComponent(content: "sample_1010")) - commands.spawn() + await commands.spawn() .addComponent(TestComponent(content: "sample_120391-2")) } @@ -27,8 +27,8 @@ func deltaTimeTestSystem(deltaTime: Resource) { func apiTestSystem( q0: Query -) { - q0.update { _, component in +) async { + await q0.update { _, component in print(component) } } @@ -63,15 +63,15 @@ func apiTestSystem( q2: Query3, q3: Query4, q4: Query5 -) { - q0.update { _, component in +) async { + await q0.update { _, component in print(component) } } final class SystemTests: XCTestCase { - func testUpdateSystem() { - let world = World() + func testUpdateSystem() async { + let world = await World() .addSystem(.startUp, testSetUp(commands:)) .addSystem(.update, currentTimeTestSystem(time:)) .addSystem(.update, deltaTimeTestSystem(deltaTime:)) @@ -81,12 +81,12 @@ final class SystemTests: XCTestCase { .addSystem(.update, apiTestSystem(q0:q1:q2:q3:)) .addSystem(.update, apiTestSystem(q0:q1:q2:q3:q4:)) - world.setUpWorld() + await world.setUpWorld() - world.update(currentTime: 0) - world.update(currentTime: 1) - world.update(currentTime: 2) - world.update(currentTime: 3) - world.update(currentTime: 4) + await world.update(currentTime: 0) + await world.update(currentTime: 1) + await world.update(currentTime: 2) + await world.update(currentTime: 3) + await world.update(currentTime: 4) } } diff --git a/Tests/ecs-swiftTests/UpdateSystemTests.swift b/Tests/ecs-swiftTests/UpdateSystemTests.swift index 0bfec74..32699ec 100644 --- a/Tests/ecs-swiftTests/UpdateSystemTests.swift +++ b/Tests/ecs-swiftTests/UpdateSystemTests.swift @@ -6,26 +6,27 @@ // import XCTest -@testable import ECS +import ECS -func mySystem(commands: Commands) { - commands.spawn() +func mySystem(commands: Commands) async { + await commands.spawn() .addComponent(TestComponent(content: "sample")) } -func mySystem2(query: Query) { - query.update { _, component in +func mySystem2(query: Query) async { + await query.update { _, component in XCTAssertEqual(component.content, "sample") } } final class UpdateSystemTests: XCTestCase { - func testUpdate() { - let world = World() + func testUpdate() async { + let world = await World() .addSystem(.update, mySystem(commands:)) .addSystem(.update, mySystem2(query:)) - world.update(currentTime: 0) - world.update(currentTime: 0) + + await world.update(currentTime: 0) + await world.update(currentTime: 0) } } diff --git a/Tests/ecs-swiftTests/ecs_swiftTests.swift b/Tests/ecs-swiftTests/ecs_swiftTests.swift index 0465a78..831c753 100644 --- a/Tests/ecs-swiftTests/ecs_swiftTests.swift +++ b/Tests/ecs-swiftTests/ecs_swiftTests.swift @@ -1,45 +1,50 @@ import XCTest + +#if DEBUG @testable import ECS +#else +import ECS +#endif struct Text: Component { let v: String } -func entitycreate(commands: Commands) { +func entitycreate(commands: Commands) async { for i in 1...20000 { - commands.spawn() + await commands.spawn() .addComponent(Text(v: "\(i)")) } } -func entitycreate2(commands: Commands) { +func entitycreate2(commands: Commands) async { for i in 1...20000 { - commands.spawn() + await commands.spawn() .addComponent(Text(v: "\(i)")) } } -func update(query: Query) { - query.update { _, _ in +func update(query: Query) async { + await query.update { _, _ in } } -func update2(query: Query) { - query.update { _, _ in +func update2(query: Query) async { + await query.update { _, _ in } } -func update3(query: Query) { - query.update { _, _ in +func update3(query: Query) async { + await query.update { _, _ in } } -func update4(query: Query) { - query.update { _, _ in +func update4(query: Query) async { + await query.update { _, _ in } } @@ -49,36 +54,98 @@ final class ecs_swiftTests: XCTestCase { // set up: 1 // update: 1 // 0.00478 s - func testPerformance() { - let world = World() + + // 100 times update + // 0.424 sec + // 0.282 sec (release) + func testPerformance() async { + let world = await World() .addSystem(.startUp, entitycreate(commands:)) .addSystem(.update, update(query:)) - world.setUpWorld() + await world.setUpWorld() + + #if DEBUG print(world.entities.sequence.count) + #endif + + await world.update(currentTime: 0) + measure { - world.update(currentTime: 0) + let exp = expectation(description: "Finished") + Task { + for i in 1...100 { + await world.update(currentTime: TimeInterval(i)) + } + exp.fulfill() + } + wait(for: [exp], timeout: 200.0) } + } // entity: 20000 // set up: 1 // update: 4 // 0.0158 s -> およそ 4 倍 - func testUpdate4Performance() { - let world = World() + + // 100 times update + // 1.713 sec + // 1.139 sec (release) + func testUpdate4Performance() async { + let world = await World() .addSystem(.startUp, entitycreate(commands:)) .addSystem(.update, update(query:)) .addSystem(.update, update2(query:)) .addSystem(.update, update3(query:)) .addSystem(.update, update4(query:)) - world.setUpWorld() + + await world.setUpWorld() + + #if DEBUG print(world.entities.sequence.count) + #endif + + await world.update(currentTime: 0) + + measure { + let exp = expectation(description: "Finished") + Task { + for i in 1...100 { + await world.update(currentTime: TimeInterval(i)) + } + exp.fulfill() + } + wait(for: [exp], timeout: 200.0) + } + + } + + // 0.286 sec (release) + // 2.821 sec (release, 1000 systems) + // 0.286 sec (contiguous array + release) + // 2.849 sec (contiguous array + release, 1000 systems) + func testLargeAmountSystemPerformance() async { + assert(true) + let world = await World().addSystem(.startUp, entitycreate(commands:)) + for _ in 0...1000 { + await world.addSystem(.update, update(query:)) + } + + await world.setUpWorld() + await world.update(currentTime: 0) + measure { - world.update(currentTime: 0) + let exp = expectation(description: "Finished") + Task { + await world.update(currentTime: TimeInterval(0)) + exp.fulfill() + } + wait(for: [exp], timeout: 200.0) } + } }