diff --git a/ArkGameExample/games/TankRaceGame/CustomViews/TankWinCustomView.swift b/ArkGameExample/games/TankRaceGame/CustomViews/TankWinCustomView.swift new file mode 100644 index 0000000..f023535 --- /dev/null +++ b/ArkGameExample/games/TankRaceGame/CustomViews/TankWinCustomView.swift @@ -0,0 +1,58 @@ +import UIKit + +class TankWinCustomView: UIViewController { + + var winner: String? + + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = UIColor(white: 0, alpha: 0.5) + setupSubviews() + } + + func setupSubviews() { + // Container view + let containerView = UIView() + containerView.backgroundColor = .white + containerView.layer.cornerRadius = 10 + containerView.clipsToBounds = true + view.addSubview(containerView) + + // Winner label + let winnerLabel = UILabel() + winnerLabel.text = "Winner: \(winner ?? "Unknown")" + winnerLabel.textAlignment = .center + winnerLabel.font = UIFont.boldSystemFont(ofSize: 24) + containerView.addSubview(winnerLabel) + + // Back button + let backButton = UIButton(type: .system) + backButton.setTitle("Back", for: .normal) + backButton.addTarget(self, action: #selector(backButtonTapped), for: .touchUpInside) + containerView.addSubview(backButton) + + // Layout + containerView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + containerView.centerXAnchor.constraint(equalTo: view.centerXAnchor), + containerView.centerYAnchor.constraint(equalTo: view.centerYAnchor), + containerView.widthAnchor.constraint(equalToConstant: 300), + containerView.heightAnchor.constraint(equalToConstant: 200) + ]) + + winnerLabel.translatesAutoresizingMaskIntoConstraints = false + backButton.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + winnerLabel.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 20), + winnerLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 20), + winnerLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -20), + + backButton.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -20), + backButton.centerXAnchor.constraint(equalTo: containerView.centerXAnchor) + ]) + } + + @objc func backButtonTapped() { + dismiss(animated: true, completion: nil) + } +} diff --git a/ArkGameExample/games/TankRaceGame/Events/TankWinEvent.swift b/ArkGameExample/games/TankRaceGame/Events/TankWinEvent.swift new file mode 100644 index 0000000..66caded --- /dev/null +++ b/ArkGameExample/games/TankRaceGame/Events/TankWinEvent.swift @@ -0,0 +1,17 @@ +import Foundation + +struct TankWinEventData: ArkSerializableEventData { + var name: String + var tankId: Int +} + +struct TankWinEvent: ArkSerializableEvent { + static var id = UUID() + var eventData: TankWinEventData + var priority: Int? + + init(eventData: TankWinEventData, priority: Int? = nil) { + self.eventData = eventData + self.priority = priority + } +} diff --git a/ArkGameExample/games/TankRaceGame/TankRaceEventHandler.swift b/ArkGameExample/games/TankRaceGame/TankRaceEventHandler.swift index 3788a0c..1dec9f2 100644 --- a/ArkGameExample/games/TankRaceGame/TankRaceEventHandler.swift +++ b/ArkGameExample/games/TankRaceGame/TankRaceEventHandler.swift @@ -3,9 +3,11 @@ import Foundation class TankRaceEventHandler { let tankIdEntityMap: [Int: Entity] var collisionStrategyManager = TankRaceGameCollisionStrategyManager() + let finishLineEntities: [Entity] - init(tankIdEntityMap: [Int: Entity]) { + init(tankIdEntityMap: [Int: Entity], finishLineEntities: [Entity]) { self.tankIdEntityMap = tankIdEntityMap + self.finishLineEntities = finishLineEntities } func handleTankSteer(_ event: TankRaceSteeringEvent, @@ -42,7 +44,7 @@ class TankRaceEventHandler { return } - let magnitude = 100.0 + let magnitude = 1_000.0 let velocityX = magnitude * cos((tankRotationComponent.angleInRadians ?? 0.0) - Double.pi / 2) let velocityY = magnitude * sin((tankRotationComponent.angleInRadians ?? 0.0) - Double.pi / 2) @@ -73,7 +75,7 @@ class TankRaceEventHandler { } func handleContactBegan(_ event: ArkCollisionBeganEvent, - in context: TankRaceGameActionContext) { + in context: TankRaceGameActionContext) { let eventData = event.eventData let entityA = eventData.entityA @@ -84,6 +86,30 @@ class TankRaceEventHandler { collisionStrategyManager.handleCollisionBegan(between: entityA, and: entityB, bitMaskA: bitMaskA, bitMaskB: bitMaskB, in: context) + + let tankEntities = Set(tankIdEntityMap.compactMap { _, value in value }) + let finishLineSet = Set(finishLineEntities) + if (tankEntities.contains(entityA) || tankEntities.contains(entityB)) && + (finishLineSet.contains(entityA) || finishLineSet.contains(entityB)) { + // win condition reached + // retrieve tank id + guard let tankId = tankIdEntityMap.filter { _, entity in + entity == entityA || entity == entityB + }.map { tankId, _ in tankId }.first else { + return + } + let tankWinEventData = TankWinEventData(name: "TankWon", tankId: tankId) + let tankWinEvent = TankWinEvent(eventData: tankWinEventData) + context.events.emit(tankWinEvent) + } + } + + func handleWin(_ event: TankWinEvent, view: AbstractDemoGameHostingPage) { + let tankWinEventData = event.eventData + let winner = tankWinEventData.tankId + let tankWinView = TankWinCustomView() + tankWinView.winner = String(winner) + view.present(tankWinView, animated: true) } func handleTankShoot(_ event: TankShootEvent, in context: TankRaceGameActionContext) { diff --git a/ArkGameExample/games/TankRaceGame/TankRaceGame.swift b/ArkGameExample/games/TankRaceGame/TankRaceGame.swift index f023632..263e233 100644 --- a/ArkGameExample/games/TankRaceGame/TankRaceGame.swift +++ b/ArkGameExample/games/TankRaceGame/TankRaceGame.swift @@ -18,10 +18,14 @@ class TankRaceGame { var camera2: Entity? var camera3: Entity? + var finishLineEntities: [Entity] = [] + private var tankIdEntityMap = [Int: Entity]() var handlerManager: TankRaceEventHandler? + var rootView: AbstractDemoGameHostingPage - init() { + init(rootView: AbstractDemoGameHostingPage) { + self.rootView = rootView self.blueprint = ArkBlueprint(frameWidth: 900, frameHeight: 10_000) load() } @@ -41,7 +45,7 @@ class TankRaceGame { width: canvasWidth, height: canvasHeight, zPosition: 0, background: [[1, 2, 3], [1, 2, 3], [1, 2, 3]]), in: ecs) - TankRaceGameEntityCreator.createFinishLine( + self.finishLineEntities = TankRaceGameEntityCreator.createFinishLine( canvasWidth: canvasWidth, canvasHeight: canvasHeight, zPosition: 1, in: ecs, eventContext: events) TankGameEntityCreator.createBoundaries(width: canvasWidth, height: canvasHeight, in: ecs) self.createTankTerrainEntities(ecs: ecs, canvasWidth: canvasWidth, canvasHeight: canvasHeight) @@ -89,7 +93,8 @@ class TankRaceGame { self.camera1 = tank1 self.camera2 = tank2 self.camera3 = tank3 - self.handlerManager = TankRaceEventHandler(tankIdEntityMap: self.tankIdEntityMap) + self.handlerManager = TankRaceEventHandler(tankIdEntityMap: self.tankIdEntityMap, + finishLineEntities: self.finishLineEntities) } .on(TankRacePedalEvent.self) { event, context in self.handlerManager?.handleTankPedal(event, in: context) @@ -102,6 +107,7 @@ class TankRaceGame { } .on(ArkCollisionBeganEvent.self) { event, context in self.handlerManager?.handleContactBegan(event, in: context) + } .on(TankShootEvent.self) { event, context in self.handlerManager?.handleTankShoot(event, in: context) @@ -115,6 +121,9 @@ class TankRaceGame { .on(TankRaceSteeringEvent.self) { event, context in self.handlerManager?.handleTankSteer(event, in: context) } + .on(TankWinEvent.self) { event, _ in + self.handlerManager?.handleWin(event, view: self.rootView) + } } private func setupButtons(screenWidth: CGFloat, screenHeight: CGFloat, diff --git a/ArkGameExample/games/TankRaceGame/TankRaceGameEntityCreator.swift b/ArkGameExample/games/TankRaceGame/TankRaceGameEntityCreator.swift index 46d26cc..2de0ec2 100644 --- a/ArkGameExample/games/TankRaceGame/TankRaceGameEntityCreator.swift +++ b/ArkGameExample/games/TankRaceGame/TankRaceGameEntityCreator.swift @@ -139,7 +139,7 @@ enum TankRaceGameEntityCreator { canvasHeight: CGFloat, zPosition: Double, in ecsContext: ArkECSContext, - eventContext: ArkEventContext) { + eventContext: ArkEventContext) -> [Entity] { let positions: [CGPoint] = [CGPoint(x: canvasWidth * 1 / 6, y: canvasWidth / 5), CGPoint(x: canvasWidth * 1 / 2, y: canvasWidth / 5), CGPoint(x: canvasWidth * 5 / 6, y: canvasWidth / 5)] @@ -164,6 +164,7 @@ enum TankRaceGameEntityCreator { ) ]) } + return entities } } diff --git a/ArkGameExample/utils/GameHostingPageFactory.swift b/ArkGameExample/utils/GameHostingPageFactory.swift index b033c01..87ae968 100644 --- a/ArkGameExample/utils/GameHostingPageFactory.swift +++ b/ArkGameExample/utils/GameHostingPageFactory.swift @@ -13,8 +13,8 @@ class GameHostingPageFactory { vc.arkBlueprint = blueprint return vc case .TankRaceGame: - let blueprint: ArkBlueprint = TankRaceGame().blueprint let vc: ArkDemoGameHostingPage = ArkDemoGameHostingPage() + let blueprint: ArkBlueprint = TankRaceGame(rootView: vc).blueprint vc.arkBlueprint = blueprint return vc } diff --git a/ArkKit.xcodeproj/project.pbxproj b/ArkKit.xcodeproj/project.pbxproj index 42fd211..154cf38 100644 --- a/ArkKit.xcodeproj/project.pbxproj +++ b/ArkKit.xcodeproj/project.pbxproj @@ -44,6 +44,8 @@ 02B3C61E2BCBDFD2002331A0 /* TankRaceSteeringEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02B3C61D2BCBDFD2002331A0 /* TankRaceSteeringEvent.swift */; }; 02B3C6202BCBDFEC002331A0 /* TankRaceEndPedalEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02B3C61F2BCBDFEC002331A0 /* TankRaceEndPedalEvent.swift */; }; 02B3C6222BCBE226002331A0 /* TankRaceEventHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02B3C6212BCBE226002331A0 /* TankRaceEventHandler.swift */; }; + 02B3C6242BCBF6C1002331A0 /* TankWinEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02B3C6232BCBF6C1002331A0 /* TankWinEvent.swift */; }; + 02B3C6272BCBF8F9002331A0 /* TankWinCustomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02B3C6262BCBF8F9002331A0 /* TankWinCustomView.swift */; }; 02C394DC2BA402060075F1CA /* GameStateRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02C394DB2BA402060075F1CA /* GameStateRenderer.swift */; }; 02C394DE2BA4053E0075F1CA /* RenderableBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02C394DD2BA4053E0075F1CA /* RenderableBuilder.swift */; }; 02C394E02BA405C10075F1CA /* Canvas.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02C394DF2BA405C10075F1CA /* Canvas.swift */; }; @@ -301,6 +303,8 @@ 02B3C61D2BCBDFD2002331A0 /* TankRaceSteeringEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TankRaceSteeringEvent.swift; sourceTree = ""; }; 02B3C61F2BCBDFEC002331A0 /* TankRaceEndPedalEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TankRaceEndPedalEvent.swift; sourceTree = ""; }; 02B3C6212BCBE226002331A0 /* TankRaceEventHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TankRaceEventHandler.swift; sourceTree = ""; }; + 02B3C6232BCBF6C1002331A0 /* TankWinEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TankWinEvent.swift; sourceTree = ""; }; + 02B3C6262BCBF8F9002331A0 /* TankWinCustomView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TankWinCustomView.swift; sourceTree = ""; }; 02C394DB2BA402060075F1CA /* GameStateRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameStateRenderer.swift; sourceTree = ""; }; 02C394DD2BA4053E0075F1CA /* RenderableBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenderableBuilder.swift; sourceTree = ""; }; 02C394DF2BA405C10075F1CA /* Canvas.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Canvas.swift; sourceTree = ""; }; @@ -562,6 +566,7 @@ 0266151B2BC1402400B56A85 /* TankRaceGame */ = { isa = PBXGroup; children = ( + 02B3C6252BCBF8EF002331A0 /* CustomViews */, 02B3C61C2BCBDFC1002331A0 /* Events */, 94D053962BC7E788000280C6 /* ExternalAssets */, 0266151C2BC1405C00B56A85 /* TankRaceGame.swift */, @@ -677,10 +682,19 @@ 2812FCBE2BC3DE2F00A0FE24 /* TankRacePedalEvent.swift */, 02B3C61D2BCBDFD2002331A0 /* TankRaceSteeringEvent.swift */, 02B3C61F2BCBDFEC002331A0 /* TankRaceEndPedalEvent.swift */, + 02B3C6232BCBF6C1002331A0 /* TankWinEvent.swift */, ); path = Events; sourceTree = ""; }; + 02B3C6252BCBF8EF002331A0 /* CustomViews */ = { + isa = PBXGroup; + children = ( + 02B3C6262BCBF8F9002331A0 /* TankWinCustomView.swift */, + ); + path = CustomViews; + sourceTree = ""; + }; 02C394DA2BA401F00075F1CA /* ark-render-kit */ = { isa = PBXGroup; children = ( @@ -1768,6 +1782,7 @@ 02C394E92BA41B480075F1CA /* ArkViewModel.swift in Sources */, AD36A7562BAC0D36003E938B /* TankGameManager.swift in Sources */, 02B3C60D2BCAEFB3002331A0 /* GameHostingPageFactory.swift in Sources */, + 02B3C6242BCBF6C1002331A0 /* TankWinEvent.swift in Sources */, AD6E036B2BA1A71800974EBF /* ArkEventData.swift in Sources */, AD6E03722BA2DB2700974EBF /* Heap.swift in Sources */, 02D8E94E2BAC9A0E00BF3A07 /* GameLoop.swift in Sources */, @@ -1799,6 +1814,7 @@ 02C394EC2BA41DF00075F1CA /* ArkUIKitView.swift in Sources */, 02E0E8F42BA283180043E2BA /* UIKitRect.swift in Sources */, AD787A552B9C636F003EBBD0 /* RootViewController.swift in Sources */, + 02B3C6272BCBF8F9002331A0 /* TankWinCustomView.swift in Sources */, 02C394E32BA407420075F1CA /* Ark.swift in Sources */, 0267BA2D2BBD2AB90010F729 /* Camera.swift in Sources */, 02C9CC382BBF30180098E849 /* AbstractLetterboxable.swift in Sources */,