diff --git a/ArkKit.xcodeproj/project.pbxproj b/ArkKit.xcodeproj/project.pbxproj index da129b5..e9b1951 100644 --- a/ArkKit.xcodeproj/project.pbxproj +++ b/ArkKit.xcodeproj/project.pbxproj @@ -146,6 +146,11 @@ 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 */; }; + 02EDED3B2BD5399200FFA756 /* ArkCameraContextTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02EDED3A2BD5399200FFA756 /* ArkCameraContextTests.swift */; }; + 02EDED3D2BD539BC00FFA756 /* MockCanvas.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02EDED3C2BD539BC00FFA756 /* MockCanvas.swift */; }; + 02EDED3F2BD53BC900FFA756 /* MockView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02EDED3E2BD53BC900FFA756 /* MockView.swift */; }; + 02EDED422BD53D9D00FFA756 /* ArkTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02EDED412BD53D9D00FFA756 /* ArkTests.swift */; }; + 02EDED442BD543E000FFA756 /* MockRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02EDED432BD543E000FFA756 /* MockRootView.swift */; }; 280C803B2BD3EEC90007F659 /* ArkSKPhysicsBodyFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 280C803A2BD3EEC90007F659 /* ArkSKPhysicsBodyFactory.swift */; }; 280CD3B72BA7391100372C5D /* ArkPhysicsUpdateSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 280CD3B62BA7391100372C5D /* ArkPhysicsUpdateSystem.swift */; }; 280CD3B92BA7391700372C5D /* PhysicsComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 280CD3B82BA7391700372C5D /* PhysicsComponent.swift */; }; @@ -517,6 +522,11 @@ 02E0E8F32BA283180043E2BA /* UIKitRect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitRect.swift; sourceTree = ""; }; 02E0E8F52BA283940043E2BA /* UIKitPolygon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitPolygon.swift; sourceTree = ""; }; 02E0E8F72BA284540043E2BA /* UIKitBitmap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitBitmap.swift; sourceTree = ""; }; + 02EDED3A2BD5399200FFA756 /* ArkCameraContextTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArkCameraContextTests.swift; sourceTree = ""; }; + 02EDED3C2BD539BC00FFA756 /* MockCanvas.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockCanvas.swift; sourceTree = ""; }; + 02EDED3E2BD53BC900FFA756 /* MockView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockView.swift; sourceTree = ""; }; + 02EDED412BD53D9D00FFA756 /* ArkTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArkTests.swift; sourceTree = ""; }; + 02EDED432BD543E000FFA756 /* MockRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRootView.swift; sourceTree = ""; }; 280C803A2BD3EEC90007F659 /* ArkSKPhysicsBodyFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArkSKPhysicsBodyFactory.swift; sourceTree = ""; }; 280CD3B62BA7391100372C5D /* ArkPhysicsUpdateSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArkPhysicsUpdateSystem.swift; sourceTree = ""; }; 280CD3B82BA7391700372C5D /* PhysicsComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhysicsComponent.swift; sourceTree = ""; }; @@ -841,6 +851,9 @@ 02A4382E2BB00D9C000135BC /* MockDisplayContext.swift */, 02A438302BB00DC0000135BC /* MockAudioContext.swift */, 02B3C6122BCAF33C002331A0 /* MockExternalResource.swift */, + 02EDED3C2BD539BC00FFA756 /* MockCanvas.swift */, + 02EDED3E2BD53BC900FFA756 /* MockView.swift */, + 02EDED432BD543E000FFA756 /* MockRootView.swift */, AD0B9E122BD3C8400095C001 /* MockGameWorldUpdateLoopDelegate.swift */, AD0B9E142BD3C8720095C001 /* MockPhysicsSceneUpdateLoopDelegate.swift */, AD0B9E182BD3C99A0095C001 /* MockNetworkService.swift */, @@ -1335,6 +1348,22 @@ path = bitmap; sourceTree = ""; }; + 02EDED392BD5397B00FFA756 /* ark-camera-kit-tests */ = { + isa = PBXGroup; + children = ( + 02EDED3A2BD5399200FFA756 /* ArkCameraContextTests.swift */, + ); + path = "ark-camera-kit-tests"; + sourceTree = ""; + }; + 02EDED402BD53D9100FFA756 /* ark-tests */ = { + isa = PBXGroup; + children = ( + 02EDED412BD53D9D00FFA756 /* ArkTests.swift */, + ); + path = "ark-tests"; + sourceTree = ""; + }; 280CD3B52BA738EC00372C5D /* ark-physics-kit */ = { isa = PBXGroup; children = ( @@ -2012,6 +2041,8 @@ AD787A662B9C6373003EBBD0 /* ArkKitTests */ = { isa = PBXGroup; children = ( + 02EDED402BD53D9100FFA756 /* ark-tests */, + 02EDED392BD5397B00FFA756 /* ark-camera-kit-tests */, AD0B9E0D2BD3BE570095C001 /* ark-multiplayer-kit-tests */, 281AE1262BD3910A00B79B91 /* data-structures-tests */, 02A438272BB00D20000135BC /* mocks */, @@ -2535,6 +2566,7 @@ AD0B9E1D2BD4427E0095C001 /* ArkParticipantNetworkPublisherTests.swift in Sources */, AD0B9E0F2BD3BEE00095C001 /* ArkNetworkServiceTests.swift in Sources */, AD787A682B9C6373003EBBD0 /* ArkKitTests.swift in Sources */, + 02EDED422BD53D9D00FFA756 /* ArkTests.swift in Sources */, 28A34CF92BD3990000BD3F4C /* ArkSKPhysicsBodyTests.swift in Sources */, 02A438312BB00DC0000135BC /* MockAudioContext.swift in Sources */, 28A34D002BD3A67500BD3F4C /* MockSKPhysicsBodyManager.swift in Sources */, @@ -2542,6 +2574,8 @@ 281AE12E2BD3928D00B79B91 /* OrderedDictionaryTests.swift in Sources */, 28A34D082BD3BCEC00BD3F4C /* AbstractArkPhysicsSceneTests.swift in Sources */, 02A438262BB007C9000135BC /* ArkRuleKitTests.swift in Sources */, + 02EDED3B2BD5399200FFA756 /* ArkCameraContextTests.swift in Sources */, + 02EDED442BD543E000FFA756 /* MockRootView.swift in Sources */, AD0B9E192BD3C99A0095C001 /* MockNetworkService.swift in Sources */, AD0B9E232BD4D26C0095C001 /* MockHostNetworkPublisher.swift in Sources */, 28A34D0C2BD3C16E00BD3F4C /* MockBaseSKScene.swift in Sources */, @@ -2551,6 +2585,9 @@ AD0B9E172BD3C8960095C001 /* ArkMultiplayerGameLoopTests.swift in Sources */, 02A4382D2BB00D75000135BC /* MockEventContext.swift in Sources */, 02A4382B2BB00D4D000135BC /* MockECSContext.swift in Sources */, + 02EDED3D2BD539BC00FFA756 /* MockCanvas.swift in Sources */, + 02EDED3F2BD53BC900FFA756 /* MockView.swift in Sources */, + 28D006612BAFEE3D001B4BD4 /* ArkPhysicsKitTests.swift in Sources */, AD0B9E132BD3C8400095C001 /* MockGameWorldUpdateLoopDelegate.swift in Sources */, AD0B9E1F2BD444010095C001 /* ArkHostNetworkPublisherTests.swift in Sources */, 281AE12C2BD3928700B79B91 /* PriorityQueueTests.swift in Sources */, diff --git a/ArkKit/app/utils/set-up/ArkSetUpIfHostStrategy.swift b/ArkKit/app/utils/set-up/ArkSetUpIfHostStrategy.swift index ae6693b..4c54961 100644 --- a/ArkKit/app/utils/set-up/ArkSetUpIfHostStrategy.swift +++ b/ArkKit/app/utils/set-up/ArkSetUpIfHostStrategy.swift @@ -24,11 +24,12 @@ class ArkSetUpIfHostStrategy: Ark extension ArkSetUpIfHostStrategy: ArkPlayerStateSetupDelegate { func setup(_ playerId: Int) { - let playerSetUpCallbacks = ark?.blueprint.playerSpecificSetupFunctions - guard let specificPlayerSetUp = playerSetUpCallbacks?[playerId], + guard let playerSetUpCallbacks = ark?.blueprint.playerSpecificSetupFunctions, + playerId < playerSetUpCallbacks.count, let ark = ark else { return } + let specificPlayerSetUp = playerSetUpCallbacks[playerId] ark.arkState.setup(specificPlayerSetUp, with: ark.setupContext) } } diff --git a/ArkKitTests/ark-camera-kit-tests/ArkCameraContextTests.swift b/ArkKitTests/ark-camera-kit-tests/ArkCameraContextTests.swift new file mode 100644 index 0000000..c569fae --- /dev/null +++ b/ArkKitTests/ark-camera-kit-tests/ArkCameraContextTests.swift @@ -0,0 +1,77 @@ +import XCTest +@testable import ArkKit + +class ArkCameraContextTests: XCTestCase { + func testEmptyCanvasTransformation() { + // Setup + let mockECS = MockECSContext() + let mockDisplayContext = MockDisplayContext() + let context = ArkCameraContext(ecs: mockECS, displayContext: mockDisplayContext) + let canvas = MockCanvas() + + // Action + let transformedCanvas = context.transform(canvas) + + // Assertion + XCTAssertEqual(canvas.canvasElements.count, 0) + XCTAssertEqual(transformedCanvas.canvasElements.count, 0) + } + + func testCanvasWithCameraEntities() { + let ecs = ArkECS() + let mockPosition = CGPoint(x: 0, y: 0) + ecs.createEntity(with: [PlacedCameraComponent( + camera: Camera(canvasPosition: mockPosition), + screenPosition: mockPosition, size: CGSize(width: 0, height: 0) + )]) + let canvasContext = ArkCanvasContext(ecs: ecs, arkView: MockView()) + let context = ArkCameraContext(ecs: ecs, displayContext: MockDisplayContext()) + let canvasToTransform = canvasContext.getFlatCanvas() + let transformedCanvas = context.transform(canvasToTransform) + // get entity from canvas + XCTAssertEqual(0, canvasToTransform.canvasElements.count) // no renderable component + XCTAssertEqual(1, transformedCanvas.canvasElements.count) // should have 1 CameraContainerRenderableComponent + } + + func testCanvasWithCameraEntitiesThatHasRenderable() { + let ecs = ArkECS() + let mockPosition = CGPoint(x: 0, y: 0) + ecs.createEntity(with: [ + PlacedCameraComponent( + camera: Camera(canvasPosition: mockPosition), + screenPosition: mockPosition, size: CGSize(width: 0, height: 0) + ), + ButtonRenderableComponent(width: 0.0, height: 0.0) + ]) + let canvasContext = ArkCanvasContext(ecs: ecs, arkView: MockView()) + let context = ArkCameraContext(ecs: ecs, displayContext: MockDisplayContext()) + let canvasToTransform = canvasContext.getFlatCanvas() + let transformedCanvas = context.transform(canvasToTransform) + // get entity from canvas + XCTAssertEqual(1, canvasToTransform.canvasElements.count) // no renderable component + XCTAssertEqual(1, transformedCanvas.canvasElements.count) // should have 1 CameraContainerRenderableComponent + } + + func testCameraSystem_shouldUpdatePosition() { + let ecs = ArkECS() + let originalPosition = CGPoint(x: 0, y: 0) + let entity = ecs.createEntity(with: [ + PlacedCameraComponent( + camera: Camera(canvasPosition: originalPosition), + screenPosition: originalPosition, size: CGSize(width: 0, height: 0) + ), + ButtonRenderableComponent(width: 0.0, height: 0.0), + PositionComponent(position: originalPosition) + ]) + // update position of entity + let updatedPosition = CGPoint(x: 1, y: 0) + ecs.upsertComponent(PositionComponent(position: updatedPosition), to: entity) + let cameraSystem = ArkCameraSystem() + cameraSystem.update(deltaTime: 1 / 60, arkECS: ecs) + guard let camComponent = ecs.getComponent(ofType: PlacedCameraComponent.self, for: entity) else { + XCTAssertFalse(true) + return + } + XCTAssertEqual(camComponent.camera.canvasPosition, updatedPosition) + } +} diff --git a/ArkKitTests/ark-tests/ArkTests.swift b/ArkKitTests/ark-tests/ArkTests.swift new file mode 100644 index 0000000..e64b994 --- /dev/null +++ b/ArkKitTests/ark-tests/ArkTests.swift @@ -0,0 +1,75 @@ +import XCTest +@testable import ArkKit + +class ArkTests: XCTestCase { + func testArkInit() { + let mockRootView = MockRootView() + let blueprint = ArkBlueprint(frameWidth: 0, frameHeight: 0) + let ark = Ark(rootView: mockRootView, blueprint: blueprint) + + XCTAssertNotNil(ark) + } + + func testArkStart() { + let mockRootView = MockRootView() + let blueprint = ArkBlueprint(frameWidth: 0, frameHeight: 0) + let ark = Ark(rootView: mockRootView, blueprint: blueprint) + ark.start() + XCTAssertNotNil(ark.gameLoop) + } + + func testArkFinish() { + let mockRootView = MockRootView() + let blueprint = ArkBlueprint(frameWidth: 0, frameHeight: 0) + let ark = Ark(rootView: mockRootView, blueprint: blueprint) + ark.finish() + XCTAssertNil(ark.gameLoop) + } + + func testArkStartWithNetworkableBlueprintHost() { + let mockRootView = MockRootView() + let blueprint = ArkBlueprint(frameWidth: 0, frameHeight: 0) + .supportNetworkMultiPlayer(roomName: "Test", numberOfPlayers: 1) + .setRole(.host) + let ark = Ark(rootView: mockRootView, blueprint: blueprint) + ark.start() + XCTAssertNotNil(ark.gameLoop) + XCTAssertNotNil(ark.networkService) + ark.finish() + } + + func testArkStartWithNetworkableBlueprintParticipant() { + let mockRootView = MockRootView() + let blueprint = ArkBlueprint(frameWidth: 0, frameHeight: 0) + .supportNetworkMultiPlayer(roomName: "Test", numberOfPlayers: 1) + .setRole(.participant) + let ark = Ark(rootView: mockRootView, blueprint: blueprint) + ark.start() + XCTAssertNotNil(ark.gameLoop) + XCTAssertNotNil(ark.networkService) + ark.finish() + } + + func testArk_withBlueprintContextSetup_shouldStart() { + let mockRootView = MockRootView() + let blueprint = ArkBlueprint(frameWidth: 0, frameHeight: 0) + .setup { context in + context.ecs.createEntity(with: [ + ButtonRenderableComponent(width: 0, height: 0), + JoystickRenderableComponent(radius: 0), + CircleRenderableComponent(radius: 0), + RectRenderableComponent(width: 0, height: 0), + PolygonRenderableComponent( + points: [CGPoint(x: 0, y: 0)], + frame: CGRect(x: 0, y: 0, width: 0, height: 0) + ), + BitmapImageRenderableComponent(imageResourcePath: "test", width: 0, height: 0) + ]) + } + let ark = Ark(rootView: mockRootView, blueprint: blueprint) + ark.start() + XCTAssertNotNil(ark.gameLoop) + ark.finish() + } + +} diff --git a/ArkKitTests/mocks/MockCanvas.swift b/ArkKitTests/mocks/MockCanvas.swift new file mode 100644 index 0000000..11c9468 --- /dev/null +++ b/ArkKitTests/mocks/MockCanvas.swift @@ -0,0 +1,11 @@ +@testable import ArkKit + +class MockCanvas: Canvas { + var canvasElements: CanvasElements = [:] + + func addEntityRenderableToCanvas(entityId: ArkKit.EntityID, + componentType: RenderableComponentType, + renderableComponent: any ArkKit.RenderableComponent) { + // mock + } +} diff --git a/ArkKitTests/mocks/MockRootView.swift b/ArkKitTests/mocks/MockRootView.swift new file mode 100644 index 0000000..6bff76e --- /dev/null +++ b/ArkKitTests/mocks/MockRootView.swift @@ -0,0 +1,16 @@ +@testable import ArkKit +import Foundation + +class MockRootView: AbstractRootView { + var abstractView: View + init() { + self.abstractView = 1 + self.size = CGSize(width: 0, height: 0) + } + func pushView(_ view: any ArkKit.AbstractView, animated: Bool) { + } + + var size: CGSize + + typealias View = Any +} diff --git a/ArkKitTests/mocks/MockView.swift b/ArkKitTests/mocks/MockView.swift new file mode 100644 index 0000000..147fd3a --- /dev/null +++ b/ArkKitTests/mocks/MockView.swift @@ -0,0 +1,34 @@ +@testable import ArkKit + +class MockView: ArkView { + typealias View = Any + init() { + self.viewModel = nil + self.renderableBuilder = nil + self.gameLoop = nil + self.abstractView = 1.0 + } + var viewModel: ArkKit.ArkViewModel? + + var renderableBuilder: (any ArkKit.RenderableBuilder)? + + var abstractView: View + + func didMove(to parent: any ArkKit.AbstractParentView) { + } + + func render(_ canvas: any ArkKit.Canvas, with canvasContext: any ArkKit.CanvasContext) { + } + + func onScreenResize(_ delegate: @escaping ScreenResizeDelegate) { + } + + var gameLoop: (any ArkKit.GameLoop)? + + func handleGameProgress(dt: Double) { + } + + func update(for dt: Double) { + } + +}