From 116810b927439a6294b8714d9a18a91d282b5864 Mon Sep 17 00:00:00 2001 From: Aaron Christopher Tanhar Date: Sun, 3 Dec 2023 10:05:19 +0700 Subject: [PATCH 1/6] feat: new maze movement system --- Moco/MocoApp.swift | 2 +- Moco/View/User/Maze/MazeScene.swift | 163 ++++++++++++++++----------- Moco/View/User/Maze/MazeView.swift | 1 + Moco/ViewModel/MotionViewModel.swift | 8 ++ 4 files changed, 109 insertions(+), 65 deletions(-) diff --git a/Moco/MocoApp.swift b/Moco/MocoApp.swift index 0106e81..cdeb2d9 100644 --- a/Moco/MocoApp.swift +++ b/Moco/MocoApp.swift @@ -35,7 +35,7 @@ struct MocoApp: App { @StateObject private var objectDetectionViewModel = ObjectDetectionViewModel.shared @StateObject private var arViewModel = ARViewModel() - @StateObject private var motionViewModel = MotionViewModel() + @StateObject private var motionViewModel = MotionViewModel.shared @StateObject private var orientationInfo = OrientationInfo.shared private static let sharedModelContainer: ModelContainer = ModelGenerator.generator(false) diff --git a/Moco/View/User/Maze/MazeScene.swift b/Moco/View/User/Maze/MazeScene.swift index eb7cd5b..58da8b8 100644 --- a/Moco/View/User/Maze/MazeScene.swift +++ b/Moco/View/User/Maze/MazeScene.swift @@ -8,6 +8,17 @@ import SpriteKit import SwiftUI +enum CollisionTypes: UInt32 { + case player = 1 + case wall = 2 + case finish = 4 +} + +enum FinishType: String { + case wrong + case correct +} + @propertyWrapper struct MazeAnswerAssets { private var answerAssets: [String] = [] @@ -22,12 +33,16 @@ struct MazeAnswerAssets { } } -class MazeScene: SKScene, ObservableObject { +class MazeScene: SKScene, SKPhysicsContactDelegate, ObservableObject { + let motionViewModel = MotionViewModel.shared + var moco: SKSpriteNode! var obj01: SKSpriteNode! var obj02: SKSpriteNode! var obj03: SKSpriteNode! + var lastTouchPosition: CGPoint? + var touched: Bool = false var score: Int = 0 @@ -49,9 +64,12 @@ class MazeScene: SKScene, ObservableObject { var mazeModel = MazeModel() override func didMove(to _: SKView) { + physicsWorld.gravity = CGVector(dx: 0, dy: 0) + physicsWorld.contactDelegate = self createMap() createPlayer() createObjective() + motionViewModel.startUpdates() } func createMap() { @@ -78,9 +96,26 @@ class MazeScene: SKScene, ObservableObject { if mazeModel.arrayPoint[index][jIndex] == 0 { ground.name = "0" ground.texture = SKTexture(imageNamed: "Maze/floor") + if index == mazeModel.arrayPoint.count - 1 { // last tile + ground.name = FinishType.wrong.rawValue + if jIndex == mazeModel.correctPoint.xPos { + ground.name = FinishType.correct.rawValue + } + ground.physicsBody = SKPhysicsBody(rectangleOf: CGSize( + width: ground.size.width * 0.5, + height: ground.size.height * 0.5) + ) + ground.physicsBody?.categoryBitMask = CollisionTypes.finish.rawValue + ground.physicsBody?.contactTestBitMask = CollisionTypes.player.rawValue + ground.physicsBody?.isDynamic = false + ground.physicsBody?.collisionBitMask = 0 + } } else if mazeModel.arrayPoint[index][jIndex] == 1 { ground.name = "1" ground.texture = SKTexture(imageNamed: "Maze/wall") + ground.physicsBody = SKPhysicsBody(rectangleOf: ground.size) + ground.physicsBody?.categoryBitMask = CollisionTypes.wall.rawValue + ground.physicsBody?.isDynamic = false } ground.position = CGPoint(x: xRenderPos, y: yRenderPos) @@ -104,13 +139,6 @@ class MazeScene: SKScene, ObservableObject { if mazeModel.characterLocationPoint.yPos == mazeModel.correctPoint.yPos && mazeModel.characterLocationPoint.xPos != mazeModel.correctPoint.xPos { -// let move = SKAction.move(to: position, duration: 0.3) -// let scale = SKAction.scale(to: 0.0001, duration: 0.3) -// let remove = SKAction.removeFromParent() -// let sequence = SKAction.sequence([move, scale, remove]) -// moco.run(sequence) { [unowned self] in -// createPlayer() -// } print("char", mazeModel.characterLocationPoint, "goal", mazeModel.correctPoint) correctAnswer = false wrongAnswer = true @@ -133,6 +161,14 @@ class MazeScene: SKScene, ObservableObject { ) ) + moco.physicsBody = SKPhysicsBody(circleOfRadius: (moco.size.width * 0.9) / 2) + moco.physicsBody?.allowsRotation = false + moco.physicsBody?.linearDamping = 0.5 + + moco.physicsBody?.categoryBitMask = CollisionTypes.player.rawValue + moco.physicsBody?.contactTestBitMask = CollisionTypes.finish.rawValue + moco.physicsBody?.collisionBitMask = CollisionTypes.wall.rawValue + correctAnswer = nil wrongAnswer = nil @@ -188,62 +224,61 @@ class MazeScene: SKScene, ObservableObject { obj03?.texture = SKTexture(imageNamed: wrongAnswerAsset[1]) } - // MARK: - Not used - - /* - func actionMovePlayer(to: SKNode, xPos: CGFloat, yPos: CGFloat) { - let move = SKAction.move(to: to.position, duration: 0.15) - let void = SKAction.run { [self] in - movePacman(xPos: xPos, yPos: yPos) - } - let sequence = SKAction.sequence([move, void]) - moco.run(sequence) - } - - func movePacman(xPos: CGFloat, yPos: CGFloat) { - let next = nodes(at: CGPoint(x: moco.position.x + xPos, y: moco.position.y + yPos)).last - if next?.name == "0" { - if let nextChildNode = next?.childNode(withName: "0") { - nextChildNode.removeFromParent() - } - actionMovePlayer(to: next!, xPos: xPos, yPos: yPos) - } - } - - override func touchesBegan(_: Set, with _: UIEvent?) { - // let touch = touches.first! - // let location = touch.location(in: self) - // if atPoint(location).name == "left" { - // touched = true - // childNode(withName: "left")?.alpha = 1 - // movePacman(x: -size.width / CGFloat(arrayPoint.count), y: 0) - // } - // if atPoint(location).name == "right" { - // touched = true - // childNode(withName: "right")?.alpha = 1 - // movePacman(x: size.width / CGFloat(arrayPoint.count), y: 0) - // } - // if atPoint(location).name == "up" { - // touched = true - // childNode(withName: "up")?.alpha = 1 - // movePacman(x: 0, y: size.width / CGFloat(arrayPoint.count)) - // } - // if atPoint(location).name == "down" { - // touched = true - // childNode(withName: "down")?.alpha = 1 - // movePacman(x: 0, y: -size.width / CGFloat(arrayPoint.count)) - // } - } - - override func touchesEnded(_: Set, with _: UIEvent?) { - // for child in children { - // if child.name == "left" || child.name == "right" || child.name == "up" || child.name == "down" { - // child.alpha = 0.5 - // } - // } - // touched = false - } - */ + override func touchesBegan(_ touches: Set, with event: UIEvent?) { + if let touch = touches.first { + let location = touch.location(in: self) + lastTouchPosition = location + } + } + + override func touchesMoved(_ touches: Set, with event: UIEvent?) { + if let touch = touches.first { + let location = touch.location(in: self) + lastTouchPosition = location + } + } + + override func touchesEnded(_ touches: Set, with event: UIEvent?) { + lastTouchPosition = nil + } + + override func touchesCancelled(_ touches: Set, with event: UIEvent?) { + lastTouchPosition = nil + } + + func didBegin(_ contact: SKPhysicsContact) { + if contact.bodyA.node == moco { + playerCollided(with: contact.bodyB.node!) + } else if contact.bodyB.node == moco { + playerCollided(with: contact.bodyA.node!) + } + } + + func playerCollided(with node: SKNode) { + if node.name == FinishType.correct.rawValue { + correctAnswer = true + wrongAnswer = false + } else if node.name == FinishType.wrong.rawValue { + correctAnswer = false + wrongAnswer = true + } + } + + override func update(_ currentTime: TimeInterval) { +#if targetEnvironment(simulator) + if let currentTouch = lastTouchPosition { + let diff = CGPoint(x: currentTouch.x - player.position.x, y: currentTouch.y - player.position.y) + physicsWorld.gravity = CGVector(dx: diff.x / 100, dy: diff.y / 100) + } +#else + if let accelerometerData = motionViewModel.accelerometerData { + physicsWorld.gravity = CGVector( + dx: accelerometerData.acceleration.y * -2, + dy: accelerometerData.acceleration.x * 2 + ) + } +#endif + } } #Preview { diff --git a/Moco/View/User/Maze/MazeView.swift b/Moco/View/User/Maze/MazeView.swift index a315022..ec85ca4 100644 --- a/Moco/View/User/Maze/MazeView.swift +++ b/Moco/View/User/Maze/MazeView.swift @@ -115,6 +115,7 @@ struct MazeView: View { scene.wrongAnswerAsset = answersAsset timerViewModel.stopTimer("mazeTimer\(correctAnswerAsset)") timerViewModel.setTimer(key: "mazeTimer\(correctAnswerAsset)", withInterval: 0.02) { + return guard mazePromptViewModel.isTutorialDone else { return } motionViewModel.updateMotion() if orientationInfo.orientation == .landscapeLeft { diff --git a/Moco/ViewModel/MotionViewModel.swift b/Moco/ViewModel/MotionViewModel.swift index ef46bdb..546fd57 100644 --- a/Moco/ViewModel/MotionViewModel.swift +++ b/Moco/ViewModel/MotionViewModel.swift @@ -9,6 +9,8 @@ import CoreMotion import Foundation class MotionViewModel: ObservableObject { + static var shared = MotionViewModel() + @Published var accelerationValue: String = "" @Published var gravityValue: String = "" @Published var rotationValue: String = "" @@ -31,6 +33,10 @@ class MotionViewModel: ObservableObject { private var rotationRate: CMRotationRate = .init() private var attitude: CMAttitude? + var accelerometerData: CMAccelerometerData? { + motionManager.accelerometerData + } + init() { // Set the update interval to any time that you want motionManager.deviceMotionUpdateInterval = 1.0 / 60.0 // 60 Hz @@ -63,6 +69,8 @@ class MotionViewModel: ObservableObject { guard let self = self else { return } getRotation(gyroData: gyroData) } + + motionManager.startAccelerometerUpdates() } } From ccaa7f84c718f3ff152aab7d3775c755dda0e300 Mon Sep 17 00:00:00 2001 From: Aaron Christopher Tanhar Date: Sun, 3 Dec 2023 10:53:20 +0700 Subject: [PATCH 2/6] feat: add outer wall --- Moco/View/User/Maze/MazeScene.swift | 131 ++++++++++++++++------------ 1 file changed, 75 insertions(+), 56 deletions(-) diff --git a/Moco/View/User/Maze/MazeScene.swift b/Moco/View/User/Maze/MazeScene.swift index 58da8b8..ce3d9f5 100644 --- a/Moco/View/User/Maze/MazeScene.swift +++ b/Moco/View/User/Maze/MazeScene.swift @@ -72,60 +72,6 @@ class MazeScene: SKScene, SKPhysicsContactDelegate, ObservableObject { motionViewModel.startUpdates() } - func createMap() { - let screenWidth = size.width - let screenHeight = size.height - let tileSize = min(screenWidth, screenHeight) / CGFloat(mazeModel.arrayPoint.count) - - var xRenderPos: CGFloat - var yRenderPos: CGFloat = screenHeight - for index in 0 ..< mazeModel.arrayPoint.count { - xRenderPos = tileSize / 2 + screenWidth / 2 - xRenderPos -= (tileSize * CGFloat(mazeModel.arrayPoint.first!.count)) / 2 - - if index == 0 { - yRenderPos = screenHeight - tileSize / 2 - } else { - yRenderPos -= tileSize - } - - for jIndex in 0 ..< mazeModel.arrayPoint[index].count { - let ground = SKSpriteNode() - ground.size = CGSize(width: tileSize, height: tileSize) - - if mazeModel.arrayPoint[index][jIndex] == 0 { - ground.name = "0" - ground.texture = SKTexture(imageNamed: "Maze/floor") - if index == mazeModel.arrayPoint.count - 1 { // last tile - ground.name = FinishType.wrong.rawValue - if jIndex == mazeModel.correctPoint.xPos { - ground.name = FinishType.correct.rawValue - } - ground.physicsBody = SKPhysicsBody(rectangleOf: CGSize( - width: ground.size.width * 0.5, - height: ground.size.height * 0.5) - ) - ground.physicsBody?.categoryBitMask = CollisionTypes.finish.rawValue - ground.physicsBody?.contactTestBitMask = CollisionTypes.player.rawValue - ground.physicsBody?.isDynamic = false - ground.physicsBody?.collisionBitMask = 0 - } - } else if mazeModel.arrayPoint[index][jIndex] == 1 { - ground.name = "1" - ground.texture = SKTexture(imageNamed: "Maze/wall") - ground.physicsBody = SKPhysicsBody(rectangleOf: ground.size) - ground.physicsBody?.categoryBitMask = CollisionTypes.wall.rawValue - ground.physicsBody?.isDynamic = false - } - - ground.position = CGPoint(x: xRenderPos, y: yRenderPos) - xRenderPos += tileSize - addChild(ground) - mazeModel.points[index][jIndex] = ground.position - } - } - } - func move(_ direction: MoveDirection) { guard moco != nil && !moco.actionForKeyIsRunning(key: "moving") else { return } guard mazeModel.move(direction) else { return } @@ -139,11 +85,9 @@ class MazeScene: SKScene, SKPhysicsContactDelegate, ObservableObject { if mazeModel.characterLocationPoint.yPos == mazeModel.correctPoint.yPos && mazeModel.characterLocationPoint.xPos != mazeModel.correctPoint.xPos { - print("char", mazeModel.characterLocationPoint, "goal", mazeModel.correctPoint) correctAnswer = false wrongAnswer = true } else if mazeModel.characterLocationPoint == mazeModel.correctPoint { - print("char", mazeModel.characterLocationPoint, "goal", mazeModel.correctPoint) correctAnswer = true wrongAnswer = false } else { @@ -281,6 +225,81 @@ class MazeScene: SKScene, SKPhysicsContactDelegate, ObservableObject { } } +extension MazeScene { + func createMap() { + let screenWidth = size.width + let screenHeight = size.height + let tileSize = min(screenWidth, screenHeight) / CGFloat(mazeModel.arrayPoint.count) + + var xRenderPos: CGFloat + var yRenderPos: CGFloat = screenHeight + for index in mazeModel.arrayPoint.indices { + xRenderPos = tileSize / 2 + screenWidth / 2 + xRenderPos -= (tileSize * CGFloat(mazeModel.arrayPoint.first!.count)) / 2 + + if index == 0 { + yRenderPos = screenHeight - tileSize / 2 + } else { + yRenderPos -= tileSize + } + + for jIndex in mazeModel.arrayPoint[index].indices { + let ground = SKSpriteNode() + ground.size = CGSize(width: tileSize, height: tileSize) + + if mazeModel.arrayPoint[index][jIndex] == 0 { + ground.name = "0" + ground.texture = SKTexture(imageNamed: "Maze/floor") + if index == mazeModel.arrayPoint.indices.last { // last tile + ground.name = FinishType.wrong.rawValue + if jIndex == mazeModel.correctPoint.xPos { + ground.name = FinishType.correct.rawValue + } + ground.physicsBody = SKPhysicsBody(rectangleOf: CGSize( + width: ground.size.width * 0.5, + height: ground.size.height * 0.5) + ) + ground.physicsBody?.categoryBitMask = CollisionTypes.finish.rawValue + ground.physicsBody?.contactTestBitMask = CollisionTypes.player.rawValue + ground.physicsBody?.isDynamic = false + ground.physicsBody?.collisionBitMask = 0 + } + } else if mazeModel.arrayPoint[index][jIndex] == 1 { + ground.name = "1" + ground.texture = SKTexture(imageNamed: "Maze/wall") + ground.physicsBody = SKPhysicsBody(rectangleOf: ground.size) + ground.physicsBody?.categoryBitMask = CollisionTypes.wall.rawValue + ground.physicsBody?.isDynamic = false + } + + // MARK: - Outer wall + + if mazeModel.arrayPoint[index][jIndex] == 0 && + [0, mazeModel.arrayPoint.indices.last].contains(index) { + let outerWall = SKSpriteNode() + outerWall.size = CGSize(width: tileSize, height: tileSize) + outerWall.name = "outer_wall" + outerWall.texture = SKTexture(imageNamed: "Maze/wall") + outerWall.alpha = 0 + outerWall.physicsBody = SKPhysicsBody(rectangleOf: outerWall.size) + outerWall.physicsBody?.categoryBitMask = CollisionTypes.wall.rawValue + outerWall.physicsBody?.isDynamic = false + outerWall.position = CGPoint( + x: xRenderPos, + y: yRenderPos + (index == 0 ? tileSize : -tileSize) + ) + addChild(outerWall) + } + + ground.position = CGPoint(x: xRenderPos, y: yRenderPos) + xRenderPos += tileSize + addChild(ground) + mazeModel.points[index][jIndex] = ground.position + } + } + } +} + #Preview { MazeView() } From cc11e2c92c493c51c454f9d09e2ca732646d3b29 Mon Sep 17 00:00:00 2001 From: Aaron Christopher Tanhar Date: Sun, 3 Dec 2023 13:29:34 +0700 Subject: [PATCH 3/6] feat: maze prompt game over --- Moco/Model/MazePromptModel.swift | 2 + .../Components/Prompts/Maze/MazePrompt.swift | 27 +++- Moco/View/User/Maze/MazeScene.swift | 45 +++--- Moco/View/User/Maze/MazeView.swift | 130 +++++++++--------- Moco/View/User/StoryView.swift | 5 +- Moco/ViewModel/MazePromptViewModel.swift | 15 ++ Moco/ViewModel/StoryViewViewModel.swift | 29 +++- 7 files changed, 162 insertions(+), 91 deletions(-) diff --git a/Moco/Model/MazePromptModel.swift b/Moco/Model/MazePromptModel.swift index a158ff4..0a096db 100644 --- a/Moco/Model/MazePromptModel.swift +++ b/Moco/Model/MazePromptModel.swift @@ -15,6 +15,7 @@ struct MazePromptModel { var isCorrectAnswer: Bool = false var isWrongAnswer: Bool = false var isTutorialDone = GlobalStorage.mazeTutorialFinished + var isGameOver = false var mazeCount = -1 var currentMazeIndex = 0 @@ -28,6 +29,7 @@ struct MazePromptModel { progress = 0.0 isCorrectAnswer = false isWrongAnswer = false + isGameOver = false isTutorialDone = GlobalStorage.mazeTutorialFinished mazeCount = -1 currentMazeIndex = 0 diff --git a/Moco/View/Components/Prompts/Maze/MazePrompt.swift b/Moco/View/Components/Prompts/Maze/MazePrompt.swift index 45c1da7..0aedf72 100644 --- a/Moco/View/Components/Prompts/Maze/MazePrompt.swift +++ b/Moco/View/Components/Prompts/Maze/MazePrompt.swift @@ -8,6 +8,7 @@ import SwiftUI struct MazePrompt: View { + @Environment(\.navigate) private var navigate @Environment(\.settingsViewModel) private var settingsViewModel @Environment(\.audioViewModel) private var audioViewModel @Environment(\.episodeViewModel) private var episodeViewModel @@ -15,6 +16,7 @@ struct MazePrompt: View { @State private var isCorrectAnswerPopup = false @State private var isWrongAnswerPopup = false + @State private var gameOverPopup = false @State private var updateTimer = true @State private var elapsedSecond = 0 @@ -30,6 +32,8 @@ struct MazePrompt: View { var action: () -> Void = {} + var onRestart: (() -> Void)? + func playInitialNarration() { if mazePromptViewModel.isTutorialDone { audioViewModel.playSound( @@ -49,7 +53,14 @@ struct MazePrompt: View { Spacer() TimerView( durationParamInSeconds: mazePromptViewModel.durationInSeconds - ) + ) { + // MARK: - Game Over + + gameOverPopup = true + mazePromptViewModel.isGameOver = true + + // MARK: - + } .padding(.trailing, Screen.width * 0.3) } Text(promptText) @@ -112,8 +123,20 @@ struct MazePrompt: View { .popUp(isActive: $isWrongAnswerPopup, title: "Oh tidak! Kamu pergi ke jalan yang salah", disableCancel: true) { action() } + .popUp( + isActive: $gameOverPopup, + title: "Waktu telah habis!", + cancelText: "Keluar", + confirmText: "Ulangi", + disableCancel: true, + type: .danger + ) { + onRestart?() + } cancelHandler: { + navigate.popToRoot() + } .onReceive(timer) { _ in - if updateTimer { + if updateTimer && mazePromptViewModel.isTutorialDone { elapsedSecond += 1 } } diff --git a/Moco/View/User/Maze/MazeScene.swift b/Moco/View/User/Maze/MazeScene.swift index ce3d9f5..4918a14 100644 --- a/Moco/View/User/Maze/MazeScene.swift +++ b/Moco/View/User/Maze/MazeScene.swift @@ -35,6 +35,7 @@ struct MazeAnswerAssets { class MazeScene: SKScene, SKPhysicsContactDelegate, ObservableObject { let motionViewModel = MotionViewModel.shared + let mazePromptViewModel = MazePromptViewModel.shared var moco: SKSpriteNode! var obj01: SKSpriteNode! @@ -168,25 +169,25 @@ class MazeScene: SKScene, SKPhysicsContactDelegate, ObservableObject { obj03?.texture = SKTexture(imageNamed: wrongAnswerAsset[1]) } - override func touchesBegan(_ touches: Set, with event: UIEvent?) { + override func touchesBegan(_ touches: Set, with _: UIEvent?) { if let touch = touches.first { let location = touch.location(in: self) lastTouchPosition = location } } - override func touchesMoved(_ touches: Set, with event: UIEvent?) { + override func touchesMoved(_ touches: Set, with _: UIEvent?) { if let touch = touches.first { let location = touch.location(in: self) lastTouchPosition = location } } - override func touchesEnded(_ touches: Set, with event: UIEvent?) { + override func touchesEnded(_: Set, with _: UIEvent?) { lastTouchPosition = nil } - override func touchesCancelled(_ touches: Set, with event: UIEvent?) { + override func touchesCancelled(_: Set, with _: UIEvent?) { lastTouchPosition = nil } @@ -208,20 +209,27 @@ class MazeScene: SKScene, SKPhysicsContactDelegate, ObservableObject { } } - override func update(_ currentTime: TimeInterval) { -#if targetEnvironment(simulator) - if let currentTouch = lastTouchPosition { - let diff = CGPoint(x: currentTouch.x - player.position.x, y: currentTouch.y - player.position.y) - physicsWorld.gravity = CGVector(dx: diff.x / 100, dy: diff.y / 100) - } -#else - if let accelerometerData = motionViewModel.accelerometerData { + override func update(_: TimeInterval) { + if !mazePromptViewModel.canMove { physicsWorld.gravity = CGVector( - dx: accelerometerData.acceleration.y * -2, - dy: accelerometerData.acceleration.x * 2 + dx: 0, + dy: 0 ) + return } -#endif + #if targetEnvironment(simulator) + if let currentTouch = lastTouchPosition { + let diff = CGPoint(x: currentTouch.x - player.position.x, y: currentTouch.y - player.position.y) + physicsWorld.gravity = CGVector(dx: diff.x / 100, dy: diff.y / 100) + } + #else + if let accelerometerData = motionViewModel.accelerometerData { + physicsWorld.gravity = CGVector( + dx: accelerometerData.acceleration.y * -2, + dy: accelerometerData.acceleration.x * 2 + ) + } + #endif } } @@ -257,7 +265,8 @@ extension MazeScene { } ground.physicsBody = SKPhysicsBody(rectangleOf: CGSize( width: ground.size.width * 0.5, - height: ground.size.height * 0.5) + height: ground.size.height * 0.5 + ) ) ground.physicsBody?.categoryBitMask = CollisionTypes.finish.rawValue ground.physicsBody?.contactTestBitMask = CollisionTypes.player.rawValue @@ -274,8 +283,8 @@ extension MazeScene { // MARK: - Outer wall - if mazeModel.arrayPoint[index][jIndex] == 0 && - [0, mazeModel.arrayPoint.indices.last].contains(index) { + if mazeModel.arrayPoint[index][jIndex] == 0, + [0, mazeModel.arrayPoint.indices.last].contains(index) { let outerWall = SKSpriteNode() outerWall.size = CGSize(width: tileSize, height: tileSize) outerWall.name = "outer_wall" diff --git a/Moco/View/User/Maze/MazeView.swift b/Moco/View/User/Maze/MazeView.swift index ec85ca4..e13f077 100644 --- a/Moco/View/User/Maze/MazeView.swift +++ b/Moco/View/User/Maze/MazeView.swift @@ -20,7 +20,6 @@ struct MazeView: View { @EnvironmentObject var motionViewModel: MotionViewModel @EnvironmentObject var orientationInfo: OrientationInfo @Environment(\.mazePromptViewModel) private var mazePromptViewModel - @State private var timerViewModel = TimerViewModel() var answersAsset = ["Maze/answer_one", "Maze/answer_two"] { didSet { @@ -113,73 +112,6 @@ struct MazeView: View { motionViewModel.startUpdates() scene.correctAnswerAsset = correctAnswerAsset scene.wrongAnswerAsset = answersAsset - timerViewModel.stopTimer("mazeTimer\(correctAnswerAsset)") - timerViewModel.setTimer(key: "mazeTimer\(correctAnswerAsset)", withInterval: 0.02) { - return - guard mazePromptViewModel.isTutorialDone else { return } - motionViewModel.updateMotion() - if orientationInfo.orientation == .landscapeLeft { - if abs(motionViewModel.rollNum) > abs(motionViewModel.pitchNum) { - if motionViewModel.rollNum > 0 { - switch motionViewModel.gravityDegree { - case -75 ... -10, 10 ... 80: - scene.move(.right) - case 100 ... 170, 190 ... 255: - scene.move(.left) - default: - scene.move(.up) - } - } else if motionViewModel.rollNum < 0 { - switch motionViewModel.gravityDegree { - case -75 ... -10, 105 ... 170: - scene.move(.right) - case 10 ... 75, 190 ... 255: - scene.move(.left) - default: - scene.move(.down) - } - } - } else { - if motionViewModel.pitchNum > 0 { - scene.move(.right) - } else if motionViewModel.pitchNum < 0 { - scene.move(.left) - } - } - } else if orientationInfo.orientation == .landscapeRight { - if abs(motionViewModel.rollNum) > abs(motionViewModel.pitchNum) { - if motionViewModel.rollNum > 0 { - switch motionViewModel.gravityDegree { - case -75 ... -10, 105 ... 170: - scene.move(.right) - case 10 ... 75, 190 ... 255: - scene.move(.left) - default: - scene.move(.down) - } - } else if motionViewModel.rollNum < 0 { - switch motionViewModel.gravityDegree { - case -75 ... -10, 10 ... 80: - scene.move(.left) - case 100 ... 170, 190 ... 255: - scene.move(.right) - default: - scene.move(.up) - } - } - } else { - if motionViewModel.pitchNum > 0 { - scene.move(.left) - } else if motionViewModel.pitchNum < 0 { - scene.move(.right) - } - } - } - } - } - .onDisappear { -// motionViewModel.stopUpdates() - timerViewModel.stopTimer("mazeTimer\(correctAnswerAsset)") } .onChange(of: scene.correctAnswer) { if let sceneCorrectAnswer = scene.correctAnswer { @@ -192,6 +124,68 @@ struct MazeView: View { } } } + + private func updateMazeControl() { + guard mazePromptViewModel.isTutorialDone else { return } + motionViewModel.updateMotion() + if orientationInfo.orientation == .landscapeLeft { + if abs(motionViewModel.rollNum) > abs(motionViewModel.pitchNum) { + if motionViewModel.rollNum > 0 { + switch motionViewModel.gravityDegree { + case -75 ... -10, 10 ... 80: + scene.move(.right) + case 100 ... 170, 190 ... 255: + scene.move(.left) + default: + scene.move(.up) + } + } else if motionViewModel.rollNum < 0 { + switch motionViewModel.gravityDegree { + case -75 ... -10, 105 ... 170: + scene.move(.right) + case 10 ... 75, 190 ... 255: + scene.move(.left) + default: + scene.move(.down) + } + } + } else { + if motionViewModel.pitchNum > 0 { + scene.move(.right) + } else if motionViewModel.pitchNum < 0 { + scene.move(.left) + } + } + } else if orientationInfo.orientation == .landscapeRight { + if abs(motionViewModel.rollNum) > abs(motionViewModel.pitchNum) { + if motionViewModel.rollNum > 0 { + switch motionViewModel.gravityDegree { + case -75 ... -10, 105 ... 170: + scene.move(.right) + case 10 ... 75, 190 ... 255: + scene.move(.left) + default: + scene.move(.down) + } + } else if motionViewModel.rollNum < 0 { + switch motionViewModel.gravityDegree { + case -75 ... -10, 10 ... 80: + scene.move(.left) + case 100 ... 170, 190 ... 255: + scene.move(.right) + default: + scene.move(.up) + } + } + } else { + if motionViewModel.pitchNum > 0 { + scene.move(.left) + } else if motionViewModel.pitchNum < 0 { + scene.move(.right) + } + } + } + } } struct MazeViewPreview: View { diff --git a/Moco/View/User/StoryView.swift b/Moco/View/User/StoryView.swift index 5e3f671..34205be 100644 --- a/Moco/View/User/StoryView.swift +++ b/Moco/View/User/StoryView.swift @@ -105,7 +105,10 @@ struct StoryView: View { promptId: mazePrompt.uid ) { svvm.nextPage() - }.id(mazePrompt.id) + } onRestart: { + svvm.restart(true) + } + .id(mazePrompt.id) } case .ar: if let ARPrompt = promptViewModel.prompts?[0] { diff --git a/Moco/ViewModel/MazePromptViewModel.swift b/Moco/ViewModel/MazePromptViewModel.swift index 86eeeb9..a7f7e91 100644 --- a/Moco/ViewModel/MazePromptViewModel.swift +++ b/Moco/ViewModel/MazePromptViewModel.swift @@ -105,6 +105,21 @@ import SwiftUI } } + var canMove: Bool { + get { + isTutorialDone && !mazePromptModel.isGameOver + } + } + + var isGameOver: Bool { + get { + mazePromptModel.isGameOver + } + set { + mazePromptModel.isGameOver = newValue + } + } + func playPrompt() { mazePromptModel.isStarted = false withAnimation(.easeInOut(duration: 3)) { diff --git a/Moco/ViewModel/StoryViewViewModel.swift b/Moco/ViewModel/StoryViewViewModel.swift index 1c14bd1..2e0d45f 100644 --- a/Moco/ViewModel/StoryViewViewModel.swift +++ b/Moco/ViewModel/StoryViewViewModel.swift @@ -89,7 +89,7 @@ extension StoryViewViewModel { } } - func onPageChange() { + func onPageChange(_ earlyPrompt: Bool? = false) { stop() setNewStoryPage(scrollPosition ?? -1) @@ -106,7 +106,7 @@ extension StoryViewViewModel { promptViewModel.fetchPrompts(storyPage) } startPrompt() - if let storyPage = storyViewModel.storyPage, storyPage.earlyPrompt { + if let storyPage = storyViewModel.storyPage, storyPage.earlyPrompt || earlyPrompt! { promptViewModel.fetchPrompts(storyPage) if let prompt = promptViewModel.prompts?.first { activePrompt = prompt @@ -232,4 +232,29 @@ extension StoryViewViewModel { onPageChange() mazePromptViewModel.reset(true) } + + func reset() { + scrollPosition = 0 + isExitPopUpActive = false + isEpisodeFinished = false + isMuted = false + text = "" + narrativeIndex = -1 + showPromptButton = false + activePrompt = nil + peelEffectState = PeelEffectState.stop + toBeExecutedByPeelEffect = {} + peelBackground = AnyView(EmptyView()) + isReversePeel = false + showWrongAnsPopup = false + mazeQuestionIndex = 0 + forceShowNext = false + showPauseMenu = false + } + + func restart(_ earlyPrompt: Bool? = false) { + reset() + onPageChange(earlyPrompt!) + mazePromptViewModel.reset(true) + } } From 49128d683ba79eec4373e160e8b114feb564f337 Mon Sep 17 00:00:00 2001 From: Aaron Christopher Tanhar Date: Sun, 3 Dec 2023 13:30:22 +0700 Subject: [PATCH 4/6] style: stylelint fixing --- Moco/View/Components/Prompts/Maze/MazePrompt.swift | 4 ++-- Moco/ViewModel/MazePromptViewModel.swift | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Moco/View/Components/Prompts/Maze/MazePrompt.swift b/Moco/View/Components/Prompts/Maze/MazePrompt.swift index 0aedf72..d38a865 100644 --- a/Moco/View/Components/Prompts/Maze/MazePrompt.swift +++ b/Moco/View/Components/Prompts/Maze/MazePrompt.swift @@ -126,8 +126,8 @@ struct MazePrompt: View { .popUp( isActive: $gameOverPopup, title: "Waktu telah habis!", - cancelText: "Keluar", - confirmText: "Ulangi", + cancelText: "Keluar", + confirmText: "Ulangi", disableCancel: true, type: .danger ) { diff --git a/Moco/ViewModel/MazePromptViewModel.swift b/Moco/ViewModel/MazePromptViewModel.swift index a7f7e91..6a00a96 100644 --- a/Moco/ViewModel/MazePromptViewModel.swift +++ b/Moco/ViewModel/MazePromptViewModel.swift @@ -106,9 +106,7 @@ import SwiftUI } var canMove: Bool { - get { - isTutorialDone && !mazePromptModel.isGameOver - } + isTutorialDone && !mazePromptModel.isGameOver } var isGameOver: Bool { From ca93f9e35ff19ef1bc67da993025a68139298cef Mon Sep 17 00:00:00 2001 From: Aaron Christopher Tanhar Date: Sun, 3 Dec 2023 13:48:47 +0700 Subject: [PATCH 5/6] style: fix stylelint violation --- Moco/View/User/Maze/MazeView.swift | 106 ++++++++++++++++------------- 1 file changed, 57 insertions(+), 49 deletions(-) diff --git a/Moco/View/User/Maze/MazeView.swift b/Moco/View/User/Maze/MazeView.swift index e13f077..7416fc4 100644 --- a/Moco/View/User/Maze/MazeView.swift +++ b/Moco/View/User/Maze/MazeView.swift @@ -125,65 +125,73 @@ struct MazeView: View { } } - private func updateMazeControl() { - guard mazePromptViewModel.isTutorialDone else { return } - motionViewModel.updateMotion() - if orientationInfo.orientation == .landscapeLeft { - if abs(motionViewModel.rollNum) > abs(motionViewModel.pitchNum) { - if motionViewModel.rollNum > 0 { - switch motionViewModel.gravityDegree { - case -75 ... -10, 10 ... 80: - scene.move(.right) - case 100 ... 170, 190 ... 255: - scene.move(.left) - default: - scene.move(.up) - } - } else if motionViewModel.rollNum < 0 { - switch motionViewModel.gravityDegree { - case -75 ... -10, 105 ... 170: - scene.move(.right) - case 10 ... 75, 190 ... 255: - scene.move(.left) - default: - scene.move(.down) - } + private func landscapeLeftControl() { + if abs(motionViewModel.rollNum) > abs(motionViewModel.pitchNum) { + if motionViewModel.rollNum > 0 { + switch motionViewModel.gravityDegree { + case -75 ... -10, 10 ... 80: + scene.move(.right) + case 100 ... 170, 190 ... 255: + scene.move(.left) + default: + scene.move(.up) } - } else { - if motionViewModel.pitchNum > 0 { + } else if motionViewModel.rollNum < 0 { + switch motionViewModel.gravityDegree { + case -75 ... -10, 105 ... 170: scene.move(.right) - } else if motionViewModel.pitchNum < 0 { + case 10 ... 75, 190 ... 255: scene.move(.left) + default: + scene.move(.down) } } - } else if orientationInfo.orientation == .landscapeRight { - if abs(motionViewModel.rollNum) > abs(motionViewModel.pitchNum) { - if motionViewModel.rollNum > 0 { - switch motionViewModel.gravityDegree { - case -75 ... -10, 105 ... 170: - scene.move(.right) - case 10 ... 75, 190 ... 255: - scene.move(.left) - default: - scene.move(.down) - } - } else if motionViewModel.rollNum < 0 { - switch motionViewModel.gravityDegree { - case -75 ... -10, 10 ... 80: - scene.move(.left) - case 100 ... 170, 190 ... 255: - scene.move(.right) - default: - scene.move(.up) - } + } else { + if motionViewModel.pitchNum > 0 { + scene.move(.right) + } else if motionViewModel.pitchNum < 0 { + scene.move(.left) + } + } + } + + private func landscapeRightControl() { + if abs(motionViewModel.rollNum) > abs(motionViewModel.pitchNum) { + if motionViewModel.rollNum > 0 { + switch motionViewModel.gravityDegree { + case -75 ... -10, 105 ... 170: + scene.move(.right) + case 10 ... 75, 190 ... 255: + scene.move(.left) + default: + scene.move(.down) } - } else { - if motionViewModel.pitchNum > 0 { + } else if motionViewModel.rollNum < 0 { + switch motionViewModel.gravityDegree { + case -75 ... -10, 10 ... 80: scene.move(.left) - } else if motionViewModel.pitchNum < 0 { + case 100 ... 170, 190 ... 255: scene.move(.right) + default: + scene.move(.up) } } + } else { + if motionViewModel.pitchNum > 0 { + scene.move(.left) + } else if motionViewModel.pitchNum < 0 { + scene.move(.right) + } + } + } + + private func updateMazeControl() { + guard mazePromptViewModel.isTutorialDone else { return } + motionViewModel.updateMotion() + if orientationInfo.orientation == .landscapeLeft { + landscapeLeftControl() + } else if orientationInfo.orientation == .landscapeRight { + landscapeRightControl() } } } From d731f6c4ae9aa431dbc1d62763df90e82d52f448 Mon Sep 17 00:00:00 2001 From: Aaron Christopher Tanhar Date: Sun, 3 Dec 2023 13:58:28 +0700 Subject: [PATCH 6/6] fix: simulator mazescene update --- Moco/View/User/Maze/MazeScene.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moco/View/User/Maze/MazeScene.swift b/Moco/View/User/Maze/MazeScene.swift index 4918a14..14017c0 100644 --- a/Moco/View/User/Maze/MazeScene.swift +++ b/Moco/View/User/Maze/MazeScene.swift @@ -219,7 +219,7 @@ class MazeScene: SKScene, SKPhysicsContactDelegate, ObservableObject { } #if targetEnvironment(simulator) if let currentTouch = lastTouchPosition { - let diff = CGPoint(x: currentTouch.x - player.position.x, y: currentTouch.y - player.position.y) + let diff = CGPoint(x: currentTouch.x - moco.position.x, y: currentTouch.y - moco.position.y) physicsWorld.gravity = CGVector(dx: diff.x / 100, dy: diff.y / 100) } #else