diff --git a/ArkGameExample/games/TankGame/TankGameCollisionStrategyManager.swift b/ArkGameExample/games/TankGame/TankGameCollisionStrategyManager.swift index 6e43dda..8dd7e87 100644 --- a/ArkGameExample/games/TankGame/TankGameCollisionStrategyManager.swift +++ b/ArkGameExample/games/TankGame/TankGameCollisionStrategyManager.swift @@ -31,11 +31,7 @@ class TankGameCollisionStrategyManager: CollisionStrategyManager(_ entity: Entity, in context: ArkActionContext) { - guard var physicsComponent = context.ecs.getComponent(ofType: PhysicsComponent.self, for: entity) else { - return - } - physicsComponent.toBeRemoved = true - context.ecs.upsertComponent(physicsComponent, to: entity) + context.ecs.upsertComponent(ToRemoveComponent(toBeRemoved: true), to: entity) } func markBallForRemoval(_ entity: Entity, diff --git a/ArkGameExample/games/TankGame/TankGameManager.swift b/ArkGameExample/games/TankGame/TankGameManager.swift index 2b5a0ee..ac40de9 100644 --- a/ArkGameExample/games/TankGame/TankGameManager.swift +++ b/ArkGameExample/games/TankGame/TankGameManager.swift @@ -427,12 +427,10 @@ extension TankGameManager { let eventData = event.eventData let tankEntity = eventData.tankEntity guard let tankHpComponent = context.ecs.getComponent(ofType: TankHpComponent.self, for: tankEntity), - tankHpComponent.hp <= 0, - var physicsComponent = context.ecs.getComponent(ofType: PhysicsComponent.self, for: tankEntity) else { + tankHpComponent.hp <= 0 else { return } - physicsComponent.toBeRemoved = true - context.ecs.upsertComponent(physicsComponent, to: tankEntity) + context.ecs.upsertComponent(ToRemoveComponent(toBeRemoved: true), to: tankEntity) if let positionComponent = context.ecs.getComponent(ofType: PositionComponent.self, for: tankEntity) { ImpactExplosionAnimation(perFrameDuration: 0.1).create(in: ecs, at: positionComponent.position) } diff --git a/ArkGameExample/games/TankRaceGame/TankRaceEventHandler.swift b/ArkGameExample/games/TankRaceGame/TankRaceEventHandler.swift index 1dec9f2..d332315 100644 --- a/ArkGameExample/games/TankRaceGame/TankRaceEventHandler.swift +++ b/ArkGameExample/games/TankRaceGame/TankRaceEventHandler.swift @@ -169,12 +169,10 @@ class TankRaceEventHandler { let eventData = event.eventData let tankEntity = eventData.tankEntity guard let tankHpComponent = context.ecs.getComponent(ofType: TankHpComponent.self, for: tankEntity), - tankHpComponent.hp <= 0, - var physicsComponent = context.ecs.getComponent(ofType: PhysicsComponent.self, for: tankEntity) else { + tankHpComponent.hp <= 0 else { return } - physicsComponent.toBeRemoved = true - context.ecs.upsertComponent(physicsComponent, to: tankEntity) + context.ecs.upsertComponent(ToRemoveComponent(toBeRemoved: true), to: tankEntity) if let positionComponent = context.ecs.getComponent(ofType: PositionComponent.self, for: tankEntity) { ImpactExplosionAnimation(perFrameDuration: 0.1, width: 256.0, diff --git a/ArkKit.xcodeproj/project.pbxproj b/ArkKit.xcodeproj/project.pbxproj index 7d4050c..82ca43d 100644 --- a/ArkKit.xcodeproj/project.pbxproj +++ b/ArkKit.xcodeproj/project.pbxproj @@ -163,6 +163,8 @@ 2812FCC52BC4424200A0FE24 /* TankRaceGameEntityCreator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2812FCC42BC4424200A0FE24 /* TankRaceGameEntityCreator.swift */; }; 2812FCC72BC442BD00A0FE24 /* TankRaceGameTerrainObjectBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2812FCC62BC442BD00A0FE24 /* TankRaceGameTerrainObjectBuilder.swift */; }; 282248532BA82E5800850D7F /* SKPhysicsBodyManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 282248522BA82E5800850D7F /* SKPhysicsBodyManager.swift */; }; + 28548F502BCD9CA700C49404 /* ArkEntityRemovalSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28548F4F2BCD9CA700C49404 /* ArkEntityRemovalSystem.swift */; }; + 28548F522BCD9CB900C49404 /* ToRemoveComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28548F512BCD9CB900C49404 /* ToRemoveComponent.swift */; }; 286C09C02BADD0BB000343B1 /* TankGameMapBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 286C09BF2BADD0BB000343B1 /* TankGameMapBuilder.swift */; }; 286C09C22BADD5FB000343B1 /* TankGameTerrainObjectBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 286C09C12BADD5FB000343B1 /* TankGameTerrainObjectBuilder.swift */; }; 287B00552BC16E6C002F0114 /* TankHPComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 287B00542BC16E6C002F0114 /* TankHPComponent.swift */; }; @@ -449,6 +451,8 @@ 2812FCC42BC4424200A0FE24 /* TankRaceGameEntityCreator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TankRaceGameEntityCreator.swift; sourceTree = ""; }; 2812FCC62BC442BD00A0FE24 /* TankRaceGameTerrainObjectBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TankRaceGameTerrainObjectBuilder.swift; sourceTree = ""; }; 282248522BA82E5800850D7F /* SKPhysicsBodyManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SKPhysicsBodyManager.swift; sourceTree = ""; }; + 28548F4F2BCD9CA700C49404 /* ArkEntityRemovalSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArkEntityRemovalSystem.swift; sourceTree = ""; }; + 28548F512BCD9CB900C49404 /* ToRemoveComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToRemoveComponent.swift; sourceTree = ""; }; 286C09BF2BADD0BB000343B1 /* TankGameMapBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TankGameMapBuilder.swift; sourceTree = ""; }; 286C09C12BADD5FB000343B1 /* TankGameTerrainObjectBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TankGameTerrainObjectBuilder.swift; sourceTree = ""; }; 287B00542BC16E6C002F0114 /* TankHPComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TankHPComponent.swift; sourceTree = ""; }; @@ -1193,6 +1197,7 @@ 280CD3DD2BA8045200372C5D /* RotationComponent.swift */, 02C395332BAAF0EC0075F1CA /* WorldComponent.swift */, 28A032DB2BAD4E8200851BFF /* StopWatchComponent.swift */, + 28548F512BCD9CB900C49404 /* ToRemoveComponent.swift */, ); path = InternalComponents; sourceTree = ""; @@ -1213,6 +1218,7 @@ children = ( 28A032DE2BAD4F2A00851BFF /* ArkTimeSystem.swift */, 0267BA1D2BBB07D80010F729 /* ArkUpdateSystem.swift */, + 28548F4F2BCD9CA700C49404 /* ArkEntityRemovalSystem.swift */, ); path = InternalSystems; sourceTree = ""; @@ -1974,6 +1980,7 @@ 8FEB21782BADE30F00788E20 /* RenderLayer.swift in Sources */, 945441212BC9639E00E90ECE /* SnakeGameTick.swift in Sources */, 941BE2202BC104D000707997 /* TankGameSounds.swift in Sources */, + 28548F502BCD9CA700C49404 /* ArkEntityRemovalSystem.swift in Sources */, 94D053A12BC80E0C000280C6 /* TankGameExplosionAnimationKeyframes.swift in Sources */, 8FEB217C2BADE8A500788E20 /* AbstractRootView.swift in Sources */, 0267BA1C2BBB05B70010F729 /* RuleTrigger.swift in Sources */, @@ -1996,7 +2003,7 @@ 02C395182BA83FC40075F1CA /* ButtonRenderableComponent.swift in Sources */, AD6E03652BA1949000974EBF /* ArkEvent.swift in Sources */, 943D418C2BAEBF2E00F9E88F /* ArkSetupContext.swift in Sources */, - ADA847FF2BBC4EA800B19378 /* ArkDataSerializer.swift in Sources */, + ADA847FF2BBC4EA800B19378 /* ArkEventDataSerializer.swift in Sources */, 02B3C6202BCBDFEC002331A0 /* TankRaceEndPedalEvent.swift in Sources */, ADA847FF2BBC4EA800B19378 /* ArkEventDataSerializer.swift in Sources */, AD36A7582BAC3223003E938B /* TankGameEntityCreator.swift in Sources */, @@ -2058,6 +2065,7 @@ AD2B59B62BB958E400198E99 /* DataWrapper.swift in Sources */, 02B3C62C2BCD19AC002331A0 /* ArkNetworkPublisherDelegate.swift in Sources */, 0230A48E2BB950AF001CFDAF /* OrderedDictionary.swift in Sources */, + 28548F522BCD9CB900C49404 /* ToRemoveComponent.swift in Sources */, 9479A3AC2BA951F300F99013 /* AbstractBitmap.swift in Sources */, 945F7F9B2BA8912200933629 /* AbstractPannable.swift in Sources */, AD6E03682BA1A61200974EBF /* ArkEventManager.swift in Sources */, diff --git a/ArkKit/app/utils/set-up/ArkSetUpStrategy.swift b/ArkKit/app/utils/set-up/ArkSetUpStrategy.swift index edc4546..cef150e 100644 --- a/ArkKit/app/utils/set-up/ArkSetUpStrategy.swift +++ b/ArkKit/app/utils/set-up/ArkSetUpStrategy.swift @@ -139,11 +139,13 @@ extension ArkSetUpStrategy { let canvasSystem = ArkCanvasSystem() let timeSystem = ArkTimeSystem() let cameraSystem = ArkCameraSystem() + let entityRemovalSystem = ArkEntityRemovalSystem() ark.arkState.arkECS.addSystem(timeSystem) ark.arkState.arkECS.addSystem(physicsSystem) ark.arkState.arkECS.addSystem(animationSystem) ark.arkState.arkECS.addSystem(canvasSystem) ark.arkState.arkECS.addSystem(cameraSystem) + ark.arkState.arkECS.addSystem(entityRemovalSystem) // inject dependency into game loop simulator.physicsScene?.sceneContactUpdateDelegate = physicsSystem diff --git a/ArkKit/ark-animation-kit/ArkAnimationSystem.swift b/ArkKit/ark-animation-kit/ArkAnimationSystem.swift index 70124f1..67b213f 100644 --- a/ArkKit/ark-animation-kit/ArkAnimationSystem.swift +++ b/ArkKit/ark-animation-kit/ArkAnimationSystem.swift @@ -26,6 +26,7 @@ class ArkAnimationSystem: UpdateSystem { animationInstance.advance(by: deltaTime) if animationInstance.shouldDestroy { animationsComponent.removeAnimation(animationInstance) + arkECS.upsertComponent(ToRemoveComponent(toBeRemoved: true), to: entity) } } diff --git a/ArkKit/ark-physics-kit/ArkPhysicsSystem.swift b/ArkKit/ark-physics-kit/ArkPhysicsSystem.swift index f0766a8..3668f32 100644 --- a/ArkKit/ark-physics-kit/ArkPhysicsSystem.swift +++ b/ArkKit/ark-physics-kit/ArkPhysicsSystem.swift @@ -52,8 +52,9 @@ class ArkPhysicsSystem: UpdateSystem { for (entity, physics) in physicsComponents { handlePhysicsComponentRemovalIfNeeded(for: entity, using: physics, arkECS: arkECS) - guard !physics.toBeRemoved else { - continue } + if let toRemoveComponent = arkECS.getComponent(ofType: ToRemoveComponent.self, for: entity), + toRemoveComponent.toBeRemoved { + return } guard let positionComponent = arkECS.getComponent(ofType: PositionComponent.self, for: entity), let rotationComponent = arkECS.getComponent(ofType: RotationComponent.self, for: entity) else { @@ -70,8 +71,9 @@ class ArkPhysicsSystem: UpdateSystem { private func handlePhysicsComponentRemovalIfNeeded(for entity: Entity, using physics: PhysicsComponent, arkECS: ArkECS) { - guard physics.toBeRemoved else { - return } + guard let toRemoveComponent = arkECS.getComponent(ofType: ToRemoveComponent.self, for: entity), + toRemoveComponent.toBeRemoved else { + return } scene?.removePhysicsBody(for: entity) arkECS.removeEntity(entity) diff --git a/ArkKit/ark-physics-kit/PhysicsComponent.swift b/ArkKit/ark-physics-kit/PhysicsComponent.swift index dec02b0..f580c95 100644 --- a/ArkKit/ark-physics-kit/PhysicsComponent.swift +++ b/ArkKit/ark-physics-kit/PhysicsComponent.swift @@ -28,6 +28,4 @@ struct PhysicsComponent: SendableComponent { var categoryBitMask: UInt32 var collisionBitMask: UInt32 var contactTestBitMask: UInt32 - - var toBeRemoved = false } diff --git a/ArkKit/ark-state-kit/ark-ecs-kit/InternalComponents/ToRemoveComponent.swift b/ArkKit/ark-state-kit/ark-ecs-kit/InternalComponents/ToRemoveComponent.swift new file mode 100644 index 0000000..d2a625f --- /dev/null +++ b/ArkKit/ark-state-kit/ark-ecs-kit/InternalComponents/ToRemoveComponent.swift @@ -0,0 +1,5 @@ +import Foundation + +struct ToRemoveComponent: Component { + var toBeRemoved: Bool +} diff --git a/ArkKit/ark-state-kit/ark-ecs-kit/InternalSystems/ArkEntityRemovalSystem.swift b/ArkKit/ark-state-kit/ark-ecs-kit/InternalSystems/ArkEntityRemovalSystem.swift new file mode 100644 index 0000000..b86d184 --- /dev/null +++ b/ArkKit/ark-state-kit/ark-ecs-kit/InternalSystems/ArkEntityRemovalSystem.swift @@ -0,0 +1,21 @@ +import Foundation + +class ArkEntityRemovalSystem: UpdateSystem { + var active: Bool + + init(active: Bool = true) { + self.active = active + } + + func update(deltaTime: TimeInterval, arkECS: ArkECS) { + let toRemoveEntities = arkECS.getEntities(with: [ToRemoveComponent.self]) + for entity in toRemoveEntities { + guard let toRemoveComponent = arkECS.getComponent(ofType: ToRemoveComponent.self, + for: entity), + toRemoveComponent.toBeRemoved else { + continue + } + arkECS.removeEntity(entity) + } + } +}