Skip to content

Commit

Permalink
[ark-physics-kit] feat: separate sync to and update systems
Browse files Browse the repository at this point in the history
  • Loading branch information
markusyeo committed Apr 15, 2024
1 parent 715e1e6 commit f6277a7
Show file tree
Hide file tree
Showing 4 changed files with 208 additions and 151 deletions.
12 changes: 8 additions & 4 deletions ArkKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@
02E0E8F42BA283180043E2BA /* UIKitRect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02E0E8F32BA283180043E2BA /* UIKitRect.swift */; };
02E0E8F62BA283940043E2BA /* UIKitPolygon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02E0E8F52BA283940043E2BA /* UIKitPolygon.swift */; };
02E0E8F82BA284540043E2BA /* UIKitBitmap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02E0E8F72BA284540043E2BA /* UIKitBitmap.swift */; };
280CD3B72BA7391100372C5D /* ArkPhysicsSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 280CD3B62BA7391100372C5D /* ArkPhysicsSystem.swift */; };
280CD3B72BA7391100372C5D /* ArkPhysicsUpdateSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 280CD3B62BA7391100372C5D /* ArkPhysicsUpdateSystem.swift */; };
280CD3B92BA7391700372C5D /* PhysicsComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 280CD3B82BA7391700372C5D /* PhysicsComponent.swift */; };
280CD3BC2BA73CF900372C5D /* ArkSKPhysicsBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = 280CD3BB2BA73CF900372C5D /* ArkSKPhysicsBody.swift */; };
280CD3C42BA7419400372C5D /* ArkPhysicsContactUpdateDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 280CD3C32BA7419400372C5D /* ArkPhysicsContactUpdateDelegate.swift */; };
Expand All @@ -165,6 +165,7 @@
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 */; };
28548F542BCD9EA000C49404 /* ArkPhysicsSyncSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28548F532BCD9EA000C49404 /* ArkPhysicsSyncSystem.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 */; };
Expand Down Expand Up @@ -434,7 +435,7 @@
02E0E8F32BA283180043E2BA /* UIKitRect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitRect.swift; sourceTree = "<group>"; };
02E0E8F52BA283940043E2BA /* UIKitPolygon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitPolygon.swift; sourceTree = "<group>"; };
02E0E8F72BA284540043E2BA /* UIKitBitmap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitBitmap.swift; sourceTree = "<group>"; };
280CD3B62BA7391100372C5D /* ArkPhysicsSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArkPhysicsSystem.swift; sourceTree = "<group>"; };
280CD3B62BA7391100372C5D /* ArkPhysicsUpdateSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArkPhysicsUpdateSystem.swift; sourceTree = "<group>"; };
280CD3B82BA7391700372C5D /* PhysicsComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhysicsComponent.swift; sourceTree = "<group>"; };
280CD3BB2BA73CF900372C5D /* ArkSKPhysicsBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArkSKPhysicsBody.swift; sourceTree = "<group>"; };
280CD3C32BA7419400372C5D /* ArkPhysicsContactUpdateDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArkPhysicsContactUpdateDelegate.swift; sourceTree = "<group>"; };
Expand All @@ -453,6 +454,7 @@
282248522BA82E5800850D7F /* SKPhysicsBodyManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SKPhysicsBodyManager.swift; sourceTree = "<group>"; };
28548F4F2BCD9CA700C49404 /* ArkEntityRemovalSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArkEntityRemovalSystem.swift; sourceTree = "<group>"; };
28548F512BCD9CB900C49404 /* ToRemoveComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToRemoveComponent.swift; sourceTree = "<group>"; };
28548F532BCD9EA000C49404 /* ArkPhysicsSyncSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArkPhysicsSyncSystem.swift; sourceTree = "<group>"; };
286C09BF2BADD0BB000343B1 /* TankGameMapBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TankGameMapBuilder.swift; sourceTree = "<group>"; };
286C09C12BADD5FB000343B1 /* TankGameTerrainObjectBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TankGameTerrainObjectBuilder.swift; sourceTree = "<group>"; };
287B00542BC16E6C002F0114 /* TankHPComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TankHPComponent.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1151,7 +1153,8 @@
children = (
280CD3D72BA7FA9A00372C5D /* ark-physics-facade */,
280CD3CC2BA74E3100372C5D /* sprite-kit-physics-facade */,
280CD3B62BA7391100372C5D /* ArkPhysicsSystem.swift */,
280CD3B62BA7391100372C5D /* ArkPhysicsUpdateSystem.swift */,
28548F532BCD9EA000C49404 /* ArkPhysicsSyncSystem.swift */,
280CD3B82BA7391700372C5D /* PhysicsComponent.swift */,
28844EA02BA881E60037A7F6 /* DemoPhysicsComponent.swift */,
);
Expand Down Expand Up @@ -1946,6 +1949,7 @@
02B3C64E2BCD8507002331A0 /* ArkPlayerStateSetupDelegate.swift in Sources */,
945441042BC9178400E90ECE /* SnakeGame.swift in Sources */,
ADA847FD2BBC4B5500B19378 /* AbstractNetworkService.swift in Sources */,
28548F542BCD9EA000C49404 /* ArkPhysicsSyncSystem.swift in Sources */,
02C3951E2BA849490075F1CA /* CircleRenderableComponent.swift in Sources */,
9479A3AF2BA952E300F99013 /* AbstractShape.swift in Sources */,
02E0E8EE2BA280BD0043E2BA /* UIKitShape.swift in Sources */,
Expand Down Expand Up @@ -2091,7 +2095,7 @@
AD787A842B9C6C78003EBBD0 /* Component.swift in Sources */,
945441252BC99D8800E90ECE /* SnakeGridPositionComponent.swift in Sources */,
8F5573BF2BA425E4007030C8 /* ShapeRenderable.swift in Sources */,
280CD3B72BA7391100372C5D /* ArkPhysicsSystem.swift in Sources */,
280CD3B72BA7391100372C5D /* ArkPhysicsUpdateSystem.swift in Sources */,
28EFCB252BCCA7CC0059A908 /* ArkECSWrapper.swift in Sources */,
280CD3D62BA7F8CC00372C5D /* SKPhysicsScene.swift in Sources */,
941BE21E2BC0EC1900707997 /* ArkProtocol.swift in Sources */,
Expand Down
18 changes: 11 additions & 7 deletions ArkKit/app/utils/set-up/ArkSetUpStrategy.swift
Original file line number Diff line number Diff line change
Expand Up @@ -132,25 +132,29 @@ extension ArkSetUpStrategy {

let simulator = SKSimulator(size: CGSize(width: worldWidth, height: worldHeight))
ark.gameLoop = simulator
let physicsSystem = ArkPhysicsSystem(simulator: simulator,
eventManager: ark.arkState.eventManager,
arkECS: ark.arkState.arkECS)
let physicsUpdateSystem = ArkPhysicsUpdateSystem(simulator: simulator,
eventManager: ark.arkState.eventManager,
arkECS: ark.arkState.arkECS)
let animationSystem = ArkAnimationSystem()
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(physicsUpdateSystem)
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
simulator.physicsScene?.sceneUpdateLoopDelegate = physicsSystem
ark.gameLoop?.updatePhysicsSceneDelegate = physicsSystem
let physicsSyncSystem = ArkPhysicsSyncSystem(simulator: simulator,
eventManager: ark.arkState.eventManager,
arkECS: ark.arkState.arkECS)
ark.arkState.arkECS.addSystem(physicsSyncSystem)
simulator.physicsScene?.sceneContactUpdateDelegate = physicsSyncSystem
simulator.physicsScene?.sceneUpdateLoopDelegate = physicsSyncSystem
ark.gameLoop?.updatePhysicsSceneDelegate = physicsSyncSystem
}

func setupMultiplayerGameLoop() {
Expand Down
185 changes: 185 additions & 0 deletions ArkKit/ark-physics-kit/ArkPhysicsSyncSystem.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
import Foundation

/**
* A system which syncs from the physics world.
*/
class ArkPhysicsSyncSystem: UpdateSystem {
var active: Bool
var scene: AbstractArkPhysicsScene?
var eventManager: ArkEventManager
var arkECS: ArkECS
weak var sceneUpdateDelegate: ArkPhysicsContactUpdateDelegate?

init(simulator: AbstractPhysicsArkSimulator,
eventManager: ArkEventManager,
arkECS: ArkECS,
active: Bool = true) {
self.active = active
self.scene = simulator.physicsScene
self.eventManager = eventManager
self.arkECS = arkECS
}

func update(deltaTime: TimeInterval, arkECS: ArkECS) {
}

private func getPhysicsComponents(_ arkECS: ArkECS) -> [(Entity, PhysicsComponent)] {
arkECS.getEntities(with: [PhysicsComponent.self]).compactMap { entity in
guard let physicsComponent = arkECS.getComponent(ofType: PhysicsComponent.self, for: entity) else {
return nil
}
return (entity, physicsComponent)
}
}

func syncFromPhysicsEngine() {
let syncStrategies: [ComponentSyncing] = [PhysicsComponentSync(),
PositionComponentSync(),
RotationComponentSync()]

scene?.forEachEntity(perform: { entity, physicsBody in
syncStrategies.forEach { strategy in
strategy.sync(entity: entity, with: physicsBody, using: self.arkECS)
}
})
}

// MARK: Impulse application
func apply(impulse: CGVector, to entity: Entity, arkECS: ArkECS) {
guard var physicsComponent: PhysicsComponent =
arkECS.getComponent(ofType: PhysicsComponent.self, for: entity) else {
return
}
physicsComponent.impulse = impulse
arkECS.upsertComponent(physicsComponent, to: entity)
}

func apply(angularImpulse: CGFloat, to entity: Entity, arkECS: ArkECS) {
guard var physicsComponent: PhysicsComponent =
arkECS.getComponent(ofType: PhysicsComponent.self, for: entity) else {
return
}
physicsComponent.angularImpulse = angularImpulse
arkECS.upsertComponent(physicsComponent, to: entity)
}

// MARK: Handle Collision
func handleCollisionBegan(between entityA: Entity, and entityB: Entity) {
let arkCollisionEvent = makeCollisionEvent(ArkCollisionBeganEvent.self, entityA, entityB)
self.eventManager.emit(arkCollisionEvent)
}

func handleCollisionEnd(between entityA: Entity, and entityB: Entity) {
let arkCollisionEvent = makeCollisionEvent(ArkCollisionEndedEvent.self, entityA, entityB)
self.eventManager.emit(arkCollisionEvent)
}

private func makeCollisionEvent<T: ArkCollisionEventProtocol> (_ eventType: T.Type,
_ entityA: Entity,
_ entityB: Entity) -> T {
let entityACategoryBitMask = scene?.getPhysicsBody(for: entityA)?.categoryBitMask ?? 0
let entityBCategoryBitMask = scene?.getPhysicsBody(for: entityB)?.categoryBitMask ?? 0
let arkCollisionEvent = T(eventData: ArkCollisionEventData(
entityA: entityA,
entityACategoryBitMask: entityACategoryBitMask,
entityB: entityB,
entityBCategoryBitMask: entityBCategoryBitMask),
priority: 0)
return arkCollisionEvent
}
}

extension ArkPhysicsSyncSystem: ArkPhysicsContactUpdateDelegate {
func didContactBegin(between entityA: Entity, and entityB: Entity) {
handleCollisionBegan(between: entityA, and: entityB)
}

func didContactEnd(between entityA: Entity, and entityB: Entity) {
handleCollisionEnd(between: entityA, and: entityB)
}
}

extension ArkPhysicsSyncSystem: ArkPhysicsSceneUpdateLoopDelegate {
func update(_ deltaTime: TimeInterval) {
syncFromPhysicsEngine()
}
}

protocol ArkCollisionEventProtocol: ArkEvent {
init(eventData: ArkCollisionEventData, priority: Int?)
}

struct ArkCollisionEventData: ArkEventData {
var name = ""
var entityA: Entity
var entityACategoryBitMask: UInt32
var entityB: Entity
var entityBCategoryBitMask: UInt32
}

struct ArkCollisionEndedEvent: ArkCollisionEventProtocol {
static var id = UUID()
var eventData: ArkCollisionEventData
var priority: Int?

init(eventData: ArkCollisionEventData, priority: Int? = 10) {
self.eventData = eventData
}
}

struct ArkCollisionBeganEvent: ArkCollisionEventProtocol {
static var id = UUID()
var eventData: ArkCollisionEventData
var priority: Int?

init(eventData: ArkCollisionEventData, priority: Int? = 10) {
self.eventData = eventData
self.priority = priority
}
}

protocol ComponentSyncing {
func sync(entity: Entity, with physicsBody: AbstractArkPhysicsBody, using arkECS: ArkECS)
}

struct PhysicsComponentSync: ComponentSyncing {
func sync(entity: Entity, with physicsBody: AbstractArkPhysicsBody, using arkECS: ArkECS) {
guard var physicsComponent: PhysicsComponent =
arkECS.getComponent(ofType: PhysicsComponent.self, for: entity) else {
return }
physicsComponent.velocity = physicsBody.velocity
physicsComponent.mass = physicsBody.mass
physicsComponent.affectedByGravity = physicsBody.affectedByGravity
physicsComponent.linearDamping = physicsBody.linearDamping
physicsComponent.isDynamic = physicsBody.isDynamic
physicsComponent.allowsRotation = physicsBody.allowsRotation
physicsComponent.restitution = physicsBody.restitution
physicsComponent.friction = physicsBody.friction

physicsComponent.categoryBitMask = physicsBody.categoryBitMask
physicsComponent.contactTestBitMask = physicsBody.contactTestBitMask
physicsComponent.collisionBitMask = physicsBody.collisionBitMask
arkECS.upsertComponent(physicsComponent, to: entity)
}
}

struct PositionComponentSync: ComponentSyncing {
func sync(entity: Entity, with physicsBody: AbstractArkPhysicsBody, using arkECS: ArkECS) {
guard let positionComponent: PositionComponent =
arkECS.getComponent(ofType: PositionComponent.self, for: entity) else {
return }
var newPositionComp = positionComponent
newPositionComp.position = physicsBody.position
arkECS.upsertComponent(newPositionComp, to: entity)
}
}

struct RotationComponentSync: ComponentSyncing {
func sync(entity: Entity, with physicsBody: AbstractArkPhysicsBody, using arkECS: ArkECS) {
guard var rotationComponent: RotationComponent =
arkECS.getComponent(ofType: RotationComponent.self, for: entity) else {
return }
rotationComponent.angleInRadians = physicsBody.zRotation
arkECS.upsertComponent(rotationComponent, to: entity)
}
}
Loading

0 comments on commit f6277a7

Please sign in to comment.