From 6443e746a498d92663acc678d255efce48fb6a12 Mon Sep 17 00:00:00 2001 From: mathieu J Date: Sun, 12 Nov 2023 22:08:12 +0200 Subject: [PATCH 1/9] :sparkles: (GEK): Add Association Six SKScene and update baseScene --- .../DragAndDropAssociationBaseScene.swift | 12 +-- ...ragAndDropAssociationSixChoicesScene.swift | 80 +++++++++++++++++++ .../DragAndDropAssociationView.swift | 6 +- 3 files changed, 91 insertions(+), 7 deletions(-) create mode 100644 Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationSixChoices/DragAndDropAssociationSixChoicesScene.swift diff --git a/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationBaseScene.swift b/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationBaseScene.swift index ac0ead2606..9e539d4f86 100644 --- a/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationBaseScene.swift +++ b/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationBaseScene.swift @@ -9,16 +9,16 @@ import SwiftUI class DragAndDropAssociationBaseScene: SKScene { var viewModel: DragAndDropAssociationViewViewModel - private var biggerSide: CGFloat = 150 + var spacer: CGFloat = 455 + var defaultPosition = CGPoint.zero + var initialNodeX: CGFloat = .zero + var verticalSpacing: CGFloat = .zero + var dropDestinationAnchor: CGPoint = .zero + var biggerSide: CGFloat = 150 private var selectedNodes: [UITouch: DraggableImageAnswerNode] = [:] private var playedNode: DraggableImageAnswerNode? - private var spacer: CGFloat = 455 - private var defaultPosition = CGPoint.zero private var expectedItemsNodes: [String: [SKSpriteNode]] = [:] private var dropDestinations: [DraggableImageAnswerNode] = [] - private var dropDestinationAnchor: CGPoint = .zero - private var initialNodeX: CGFloat = .zero - private var verticalSpacing: CGFloat = .zero private var cancellables: Set = [] init(viewModel: DragAndDropAssociationViewViewModel) { diff --git a/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationSixChoices/DragAndDropAssociationSixChoicesScene.swift b/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationSixChoices/DragAndDropAssociationSixChoicesScene.swift new file mode 100644 index 0000000000..012db2de11 --- /dev/null +++ b/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationSixChoices/DragAndDropAssociationSixChoicesScene.swift @@ -0,0 +1,80 @@ +// Leka - iOS Monorepo +// Copyright 2023 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +import Combine +import ContentKit +import SpriteKit +import SwiftUI + +final class DragAndDropAssociationSixChoicesScene: DragAndDropAssociationBaseScene { + + private var freeSlots: [String: [Bool]] = [:] + private var endAbscissa: CGFloat = .zero + private var finalXPosition: CGFloat = .zero + + override init(viewModel: DragAndDropAssociationViewViewModel) { + super.init(viewModel: viewModel) + self.viewModel = viewModel + self.spacer = 340 + self.defaultPosition = CGPoint(x: spacer, y: self.size.height) + + subscribeToChoicesUpdates() + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func setFirstAnswerPosition() { + initialNodeX = (size.width - (spacer * 2)) / 2 + verticalSpacing = self.size.height / 3 + defaultPosition = CGPoint(x: initialNodeX, y: verticalSpacing - 30) + } + + override func setNextAnswerPosition(_ index: Int) { + if [0, 1, 3, 4].contains(index) { + defaultPosition.x += spacer + } else { + defaultPosition.x = initialNodeX + defaultPosition.y += verticalSpacing + 60 + } + } + + private func setFinalXPosition(context: String) { + guard endAbscissa <= dropDestinationAnchor.x else { + finalXPosition = { + guard freeSlots[context]![1] else { + freeSlots[context]![0] = false + return dropDestinationAnchor.x - 80 + } + freeSlots[context]![1] = false + return dropDestinationAnchor.x + 80 + }() + return + } + finalXPosition = { + guard freeSlots[context]![0] else { + freeSlots[context]![1] = false + return dropDestinationAnchor.x + 80 + } + freeSlots[context]![0] = false + return dropDestinationAnchor.x - 80 + }() + } + + override func goodAnswerBehavior(_ node: DraggableImageAnswerNode) { + node.scaleForMax(sizeOf: biggerSide * 0.8) + node.position = CGPoint( + x: finalXPosition, + y: dropDestinationAnchor.y - 60) + node.zPosition = 10 + node.isDraggable = false + onDropAction(node) + if viewModel.exercicesSharedData.state == .completed { + DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { [self] in + exerciseCompletedBehavior() + } + } + } +} diff --git a/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationView.swift b/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationView.swift index 805c0ca883..32a9b57a7a 100644 --- a/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationView.swift +++ b/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationView.swift @@ -36,7 +36,11 @@ public struct DragAndDropAssociationView: View { ) .frame(width: proxy.size.width, height: proxy.size.height) .onAppear { - scene = DragAndDropAssociationBaseScene(viewModel: viewModel) + if viewModel.choices.count == 4 { + scene = DragAndDropAssociationBaseScene(viewModel: viewModel) + } else { + scene = DragAndDropAssociationSixChoicesScene(viewModel: viewModel) + } } } .edgesIgnoringSafeArea(.horizontal) From 8c4bcb0eac9be108a7746ef93e6c31dcf082db2a Mon Sep 17 00:00:00 2001 From: mathieu J Date: Sun, 12 Nov 2023 22:36:12 +0200 Subject: [PATCH 2/9] :sparkles: (UIExplorer): Add associationSix yaml and Activity --- ...n-two_categories_of_three_items-images.yml | 95 +++++++++++++++++++ ...on-two_categories_of_two_items-images.yml} | 0 .../GEKNewSystem/GEKNewSystemView.swift | 3 +- ...ragAndDropAssociationSixChoicesScene.swift | 2 +- 4 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 Apps/LekaActivityUIExplorer/Resources/GEKNewSystem/activities/activity-association-two_categories_of_three_items-images.yml rename Apps/LekaActivityUIExplorer/Resources/GEKNewSystem/activities/{activity-association-two_categories-multiple_right_answers-images.yml => activity-association-two_categories_of_two_items-images.yml} (100%) diff --git a/Apps/LekaActivityUIExplorer/Resources/GEKNewSystem/activities/activity-association-two_categories_of_three_items-images.yml b/Apps/LekaActivityUIExplorer/Resources/GEKNewSystem/activities/activity-association-two_categories_of_three_items-images.yml new file mode 100644 index 0000000000..d2e2144158 --- /dev/null +++ b/Apps/LekaActivityUIExplorer/Resources/GEKNewSystem/activities/activity-association-two_categories_of_three_items-images.yml @@ -0,0 +1,95 @@ +# Leka - iOS Monorepo +# Copyright 2023 APF France handicap +# SPDX-License-Identifier: Apache-2.0 + +id: 5e5394ee819a11eeb9620242ac120002 +name: Association - Two Categories Of Three Items - Images +description: L'objectif est de trier les images par categories. +image: activity_image_recognition_1 +sequence: + - exercises: + - instructions: Rassemble les instruments par familles d'instruments + type: association + interface: association + gameplay: association + payload: + type: association + shuffle_choices: true + choices: + - value: image-instrument-violin-no_background + type: image + category: catA + - value: image-instrument-guitar-no_background + type: image + category: catA + - value: image-instrument-piano-no_background + type: image + category: catA + - value: image-instrument-flute-no_background + type: image + category: catB + - value: image-instrument-saxophone-no_background + type: image + category: catB + - value: image-instrument-harmonica-no_background + type: image + category: catB + + + - instructions: Rassemble les instruments par familles d'instruments + type: association + interface: association + gameplay: association + payload: + type: association + shuffle_choices: true + choices: + - value: image-instrument-harmonica-no_background + type: image + category: catB + - value: image-instrument-violin-no_background + type: image + category: catA + - value: image-instrument-piano-no_background + type: image + category: catA + - value: image-instrument-saxophone-no_background + type: image + category: catB + - value: image-instrument-guitar-no_background + type: image + category: catA + - value: image-instrument-flute-no_background + type: image + category: catB + + + - instructions: Rassemble les instruments par familles d'instruments + type: association + interface: association + gameplay: association + payload: + type: association + shuffle_choices: true + choices: + - value: image-instrument-piano-no_background + type: image + category: catA + - value: image-instrument-harmonica-no_background + type: image + category: catB + - value: image-instrument-guitar-no_background + type: image + category: catA + - value: image-instrument-flute-no_background + type: image + category: catB + - value: image-instrument-violin-no_background + type: image + category: catA + - value: image-instrument-saxophone-no_background + type: image + category: catB + + + diff --git a/Apps/LekaActivityUIExplorer/Resources/GEKNewSystem/activities/activity-association-two_categories-multiple_right_answers-images.yml b/Apps/LekaActivityUIExplorer/Resources/GEKNewSystem/activities/activity-association-two_categories_of_two_items-images.yml similarity index 100% rename from Apps/LekaActivityUIExplorer/Resources/GEKNewSystem/activities/activity-association-two_categories-multiple_right_answers-images.yml rename to Apps/LekaActivityUIExplorer/Resources/GEKNewSystem/activities/activity-association-two_categories_of_two_items-images.yml diff --git a/Apps/LekaActivityUIExplorer/Sources/GEKNewSystem/GEKNewSystemView.swift b/Apps/LekaActivityUIExplorer/Sources/GEKNewSystem/GEKNewSystemView.swift index 03c7719278..017ee4b564 100644 --- a/Apps/LekaActivityUIExplorer/Sources/GEKNewSystem/GEKNewSystemView.swift +++ b/Apps/LekaActivityUIExplorer/Sources/GEKNewSystem/GEKNewSystemView.swift @@ -39,7 +39,8 @@ let kActivities: [Activity] = [ // ContentKit.decodeActivity("activity-dragAndDrop-two_zones-multiple_right_answer-mixed"), ContentKit.decodeActivity("activity-medley"), - ContentKit.decodeActivity("activity-association-two_categories-multiple_right_answers-images"), + ContentKit.decodeActivity("activity-association-two_categories_of_two_items-images"), + ContentKit.decodeActivity("activity-association-two_categories_of_three_items-images"), ] struct GEKNewSystemView: View { diff --git a/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationSixChoices/DragAndDropAssociationSixChoicesScene.swift b/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationSixChoices/DragAndDropAssociationSixChoicesScene.swift index 012db2de11..c9b33dcd94 100644 --- a/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationSixChoices/DragAndDropAssociationSixChoicesScene.swift +++ b/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationSixChoices/DragAndDropAssociationSixChoicesScene.swift @@ -8,7 +8,7 @@ import SpriteKit import SwiftUI final class DragAndDropAssociationSixChoicesScene: DragAndDropAssociationBaseScene { - + private var freeSlots: [String: [Bool]] = [:] private var endAbscissa: CGFloat = .zero private var finalXPosition: CGFloat = .zero From 40d23b68c3c8378d216d22f1faaf20b19bc6ea3f Mon Sep 17 00:00:00 2001 From: mathieu J Date: Mon, 13 Nov 2023 10:34:23 +0200 Subject: [PATCH 3/9] :bug: (GEK): Fix answers positionning and subsequent crash --- .../DragAndDropAssociationBaseScene.swift | 6 +- ...ragAndDropAssociationSixChoicesScene.swift | 70 +++++++++++++++++-- 2 files changed, 66 insertions(+), 10 deletions(-) diff --git a/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationBaseScene.swift b/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationBaseScene.swift index 9e539d4f86..b18ffa0dcd 100644 --- a/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationBaseScene.swift +++ b/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationBaseScene.swift @@ -15,10 +15,10 @@ class DragAndDropAssociationBaseScene: SKScene { var verticalSpacing: CGFloat = .zero var dropDestinationAnchor: CGPoint = .zero var biggerSide: CGFloat = 150 - private var selectedNodes: [UITouch: DraggableImageAnswerNode] = [:] - private var playedNode: DraggableImageAnswerNode? + var playedNode: DraggableImageAnswerNode? + var dropDestinations: [DraggableImageAnswerNode] = [] + var selectedNodes: [UITouch: DraggableImageAnswerNode] = [:] private var expectedItemsNodes: [String: [SKSpriteNode]] = [:] - private var dropDestinations: [DraggableImageAnswerNode] = [] private var cancellables: Set = [] init(viewModel: DragAndDropAssociationViewViewModel) { diff --git a/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationSixChoices/DragAndDropAssociationSixChoicesScene.swift b/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationSixChoices/DragAndDropAssociationSixChoicesScene.swift index c9b33dcd94..8dbda394ca 100644 --- a/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationSixChoices/DragAndDropAssociationSixChoicesScene.swift +++ b/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationSixChoices/DragAndDropAssociationSixChoicesScene.swift @@ -12,6 +12,8 @@ final class DragAndDropAssociationSixChoicesScene: DragAndDropAssociationBaseSce private var freeSlots: [String: [Bool]] = [:] private var endAbscissa: CGFloat = .zero private var finalXPosition: CGFloat = .zero + // private var answersTurnedToDropAreas: [String: DraggableImageAnswerNode] = [:] + // private var answeredButNotTurnedToDropArea: [Int] = [] override init(viewModel: DragAndDropAssociationViewViewModel) { super.init(viewModel: viewModel) @@ -26,6 +28,28 @@ final class DragAndDropAssociationSixChoicesScene: DragAndDropAssociationBaseSce fatalError("init(coder:) has not been implemented") } + override func reset() { + self.backgroundColor = .clear + self.removeAllChildren() + self.removeAllActions() + + dropDestinations = [] + // answersTurnedToDropAreas = [:] + // answeredButNotTurnedToDropArea = [] + + setAnswersFinalPositionSlots() + setFirstAnswerPosition() + layoutAnswers() + } + + private func setAnswersFinalPositionSlots() { + freeSlots = [:] + for gameplayChoiceModel in viewModel.choices { + let category = gameplayChoiceModel.choice.category.rawValue + freeSlots[category] = [true, true] + } + } + override func setFirstAnswerPosition() { initialNodeX = (size.width - (spacer * 2)) / 2 verticalSpacing = self.size.height / 3 @@ -41,24 +65,24 @@ final class DragAndDropAssociationSixChoicesScene: DragAndDropAssociationBaseSce } } - private func setFinalXPosition(context: String) { + private func setFinalXPosition(category: String) { guard endAbscissa <= dropDestinationAnchor.x else { finalXPosition = { - guard freeSlots[context]![1] else { - freeSlots[context]![0] = false + guard freeSlots[category]![1] else { + freeSlots[category]![0] = false return dropDestinationAnchor.x - 80 } - freeSlots[context]![1] = false + freeSlots[category]![1] = false return dropDestinationAnchor.x + 80 }() return } finalXPosition = { - guard freeSlots[context]![0] else { - freeSlots[context]![1] = false + guard freeSlots[category]![0] else { + freeSlots[category]![1] = false return dropDestinationAnchor.x + 80 } - freeSlots[context]![0] = false + freeSlots[category]![0] = false return dropDestinationAnchor.x - 80 }() } @@ -77,4 +101,36 @@ final class DragAndDropAssociationSixChoicesScene: DragAndDropAssociationBaseSce } } } + + override func touchesEnded(_ touches: Set, with: UIEvent?) { + for touch in touches { + guard selectedNodes.keys.contains(touch) else { + break + } + playedNode = selectedNodes[touch]! + playedNode!.scaleForMax(sizeOf: biggerSide) + + // make dropArea out of target node + guard + let destinationNode = dropDestinations.first(where: { + $0.frame.contains(touch.location(in: self)) && $0.name != playedNode!.name + }) + else { + wrongAnswerBehavior(playedNode!) + break + } + dropDestinationAnchor = destinationNode.position + + guard let destination = viewModel.choices.first(where: { $0.choice.value == destinationNode.name }) + else { return } + guard let choice = viewModel.choices.first(where: { $0.choice.value == playedNode!.name }) + else { return } + + // dropped within the bounds of the proper sibling + endAbscissa = touch.location(in: self).x + destinationNode.isDraggable = false + setFinalXPosition(category: destination.choice.category.rawValue) + viewModel.onChoiceTapped(choice: choice, destination: destination) + } + } } From b42c00270a942d8f8dc5609b9ea1050922f1f426 Mon Sep 17 00:00:00 2001 From: mathieu J Date: Mon, 13 Nov 2023 14:48:36 +0200 Subject: [PATCH 4/9] :sparkles: (GEK): Finalize GameplayAssociation for 4 and 6 --- ...iation-two_categories_of_two_items-images.yml | 2 +- .../Gameplays/GameplayAssociation.swift | 16 ++++++++++------ .../DragAndDropAssociationBaseScene.swift | 9 ++++----- .../DragAndDropAssociationSixChoicesScene.swift | 13 ++++--------- 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/Apps/LekaActivityUIExplorer/Resources/GEKNewSystem/activities/activity-association-two_categories_of_two_items-images.yml b/Apps/LekaActivityUIExplorer/Resources/GEKNewSystem/activities/activity-association-two_categories_of_two_items-images.yml index a923214c01..42d8523964 100644 --- a/Apps/LekaActivityUIExplorer/Resources/GEKNewSystem/activities/activity-association-two_categories_of_two_items-images.yml +++ b/Apps/LekaActivityUIExplorer/Resources/GEKNewSystem/activities/activity-association-two_categories_of_two_items-images.yml @@ -3,7 +3,7 @@ # SPDX-License-Identifier: Apache-2.0 id: 24bf568972da46c294f1737c3ed36994 -name: Association - Two Categories - Multiple Right Answers - Images +name: Association - Two Categories Of Two Items - Images description: L'objectif est de trier les images par categories. image: activity_image_recognition_1 sequence: diff --git a/Modules/GameEngineKit/Sources/_NewSystem/Gameplays/GameplayAssociation.swift b/Modules/GameEngineKit/Sources/_NewSystem/Gameplays/GameplayAssociation.swift index 632638f86e..7c32f65d47 100644 --- a/Modules/GameEngineKit/Sources/_NewSystem/Gameplays/GameplayAssociation.swift +++ b/Modules/GameEngineKit/Sources/_NewSystem/Gameplays/GameplayAssociation.swift @@ -11,6 +11,7 @@ where ChoiceModelType: GameplayChoiceModelProtocol { var choices: CurrentValueSubject<[GameplayAssociationChoiceModel], Never> = .init([]) var rightAnswers: [ChoiceModelType] = [] + var destinations: [ChoiceModelType] = [] var state: CurrentValueSubject = .init(.idle) func updateChoice(_ choice: ChoiceModelType, state: GameplayChoiceState) { @@ -31,17 +32,20 @@ extension GameplayAssociation where ChoiceModelType == GameplayAssociationChoice } func process(_ choice: ChoiceModelType, _ destination: ChoiceModelType) { - if choice.choice.category == destination.choice.category { + if choice.choice.category == destination.choice.category + && !rightAnswers.contains(where: { $0.id == destination.id }) + { updateChoice(choice, state: .rightAnswer) rightAnswers.append(choice) - rightAnswers.append(destination) + if !destinations.contains(where: { $0.id == destination.id }) { + destinations.append(destination) + } + if (rightAnswers.count + destinations.count) == self.choices.value.count { + state.send(.completed) + } } else { updateChoice(choice, state: .wrongAnswer) } - - if rightAnswers.count == self.choices.value.count { - state.send(.completed) - } } } diff --git a/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationBaseScene.swift b/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationBaseScene.swift index b18ffa0dcd..205902079c 100644 --- a/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationBaseScene.swift +++ b/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationBaseScene.swift @@ -16,6 +16,7 @@ class DragAndDropAssociationBaseScene: SKScene { var dropDestinationAnchor: CGPoint = .zero var biggerSide: CGFloat = 150 var playedNode: DraggableImageAnswerNode? + var playedDestination: DraggableImageAnswerNode? var dropDestinations: [DraggableImageAnswerNode] = [] var selectedNodes: [UITouch: DraggableImageAnswerNode] = [:] private var expectedItemsNodes: [String: [SKSpriteNode]] = [:] @@ -24,7 +25,6 @@ class DragAndDropAssociationBaseScene: SKScene { init(viewModel: DragAndDropAssociationViewViewModel) { self.viewModel = viewModel super.init(size: CGSize.zero) - self.defaultPosition = CGPoint(x: spacer, y: self.size.height) subscribeToChoicesUpdates() } @@ -122,6 +122,7 @@ class DragAndDropAssociationBaseScene: SKScene { y: dropDestinationAnchor.y - 60) node.zPosition = 10 node.isDraggable = false + playedDestination?.isDraggable = false onDropAction(node) if viewModel.exercicesSharedData.state == .completed { DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { [self] in @@ -193,7 +194,7 @@ class DragAndDropAssociationBaseScene: SKScene { node.run(SKAction.move(to: location, duration: 0.05).moveAnimation(.linear)) node.position = location } else { - self.touchesEnded(touches, with: event) + wrongAnswerBehavior(node) } } } @@ -207,7 +208,6 @@ class DragAndDropAssociationBaseScene: SKScene { playedNode = selectedNodes[touch]! playedNode!.scaleForMax(sizeOf: biggerSide) - // make dropArea out of target node guard let destinationNode = dropDestinations.first(where: { $0.frame.contains(touch.location(in: self)) && $0.name != playedNode!.name @@ -217,14 +217,13 @@ class DragAndDropAssociationBaseScene: SKScene { break } dropDestinationAnchor = destinationNode.position + playedDestination = destinationNode guard let destination = viewModel.choices.first(where: { $0.choice.value == destinationNode.name }) else { return } guard let choice = viewModel.choices.first(where: { $0.choice.value == playedNode!.name }) else { return } - // dropped within the bounds of the proper sibling - destinationNode.isDraggable = false viewModel.onChoiceTapped(choice: choice, destination: destination) } } diff --git a/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationSixChoices/DragAndDropAssociationSixChoicesScene.swift b/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationSixChoices/DragAndDropAssociationSixChoicesScene.swift index 8dbda394ca..235d31e74d 100644 --- a/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationSixChoices/DragAndDropAssociationSixChoicesScene.swift +++ b/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationSixChoices/DragAndDropAssociationSixChoicesScene.swift @@ -12,14 +12,10 @@ final class DragAndDropAssociationSixChoicesScene: DragAndDropAssociationBaseSce private var freeSlots: [String: [Bool]] = [:] private var endAbscissa: CGFloat = .zero private var finalXPosition: CGFloat = .zero - // private var answersTurnedToDropAreas: [String: DraggableImageAnswerNode] = [:] - // private var answeredButNotTurnedToDropArea: [Int] = [] override init(viewModel: DragAndDropAssociationViewViewModel) { super.init(viewModel: viewModel) self.viewModel = viewModel - self.spacer = 340 - self.defaultPosition = CGPoint(x: spacer, y: self.size.height) subscribeToChoicesUpdates() } @@ -34,8 +30,6 @@ final class DragAndDropAssociationSixChoicesScene: DragAndDropAssociationBaseSce self.removeAllActions() dropDestinations = [] - // answersTurnedToDropAreas = [:] - // answeredButNotTurnedToDropArea = [] setAnswersFinalPositionSlots() setFirstAnswerPosition() @@ -51,6 +45,7 @@ final class DragAndDropAssociationSixChoicesScene: DragAndDropAssociationBaseSce } override func setFirstAnswerPosition() { + spacer = 340 initialNodeX = (size.width - (spacer * 2)) / 2 verticalSpacing = self.size.height / 3 defaultPosition = CGPoint(x: initialNodeX, y: verticalSpacing - 30) @@ -94,6 +89,7 @@ final class DragAndDropAssociationSixChoicesScene: DragAndDropAssociationBaseSce y: dropDestinationAnchor.y - 60) node.zPosition = 10 node.isDraggable = false + playedDestination?.isDraggable = false onDropAction(node) if viewModel.exercicesSharedData.state == .completed { DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { [self] in @@ -109,8 +105,8 @@ final class DragAndDropAssociationSixChoicesScene: DragAndDropAssociationBaseSce } playedNode = selectedNodes[touch]! playedNode!.scaleForMax(sizeOf: biggerSide) + endAbscissa = touch.location(in: self).x - // make dropArea out of target node guard let destinationNode = dropDestinations.first(where: { $0.frame.contains(touch.location(in: self)) && $0.name != playedNode!.name @@ -120,6 +116,7 @@ final class DragAndDropAssociationSixChoicesScene: DragAndDropAssociationBaseSce break } dropDestinationAnchor = destinationNode.position + playedDestination = destinationNode guard let destination = viewModel.choices.first(where: { $0.choice.value == destinationNode.name }) else { return } @@ -127,8 +124,6 @@ final class DragAndDropAssociationSixChoicesScene: DragAndDropAssociationBaseSce else { return } // dropped within the bounds of the proper sibling - endAbscissa = touch.location(in: self).x - destinationNode.isDraggable = false setFinalXPosition(category: destination.choice.category.rawValue) viewModel.onChoiceTapped(choice: choice, destination: destination) } From 70a410f70f3e9c064428a4d76b93cbbaa896192b Mon Sep 17 00:00:00 2001 From: mathieu J Date: Mon, 13 Nov 2023 17:38:40 +0200 Subject: [PATCH 5/9] :construction: (GEK): Shuffle choices for Association templates --- .../Gameplays/GameplayAssociation.swift | 4 ++-- .../DragAndDropAssociationView.swift | 18 ++++++++++++++---- .../DragAndDropAssociationViewViewModel.swift | 8 ++++---- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/Modules/GameEngineKit/Sources/_NewSystem/Gameplays/GameplayAssociation.swift b/Modules/GameEngineKit/Sources/_NewSystem/Gameplays/GameplayAssociation.swift index 7c32f65d47..6d2046cc31 100644 --- a/Modules/GameEngineKit/Sources/_NewSystem/Gameplays/GameplayAssociation.swift +++ b/Modules/GameEngineKit/Sources/_NewSystem/Gameplays/GameplayAssociation.swift @@ -25,9 +25,9 @@ where ChoiceModelType: GameplayChoiceModelProtocol { extension GameplayAssociation where ChoiceModelType == GameplayAssociationChoiceModel { - convenience init(choices: [GameplayAssociationChoiceModel]) { + convenience init(choices: [GameplayAssociationChoiceModel], shuffle: Bool = false) { self.init() - self.choices.send(choices) + self.choices.send(shuffle ? choices.shuffled() : choices) self.state.send(.playing) } diff --git a/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationView.swift b/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationView.swift index 32a9b57a7a..acccf01a8a 100644 --- a/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationView.swift +++ b/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationView.swift @@ -12,9 +12,9 @@ public struct DragAndDropAssociationView: View { @StateObject private var viewModel: DragAndDropAssociationViewViewModel @State private var scene: SKScene = SKScene() - public init(choices: [AssociationChoice]) { + public init(choices: [AssociationChoice], shuffle: Bool = false) { self._viewModel = StateObject( - wrappedValue: DragAndDropAssociationViewViewModel(choices: choices) + wrappedValue: DragAndDropAssociationViewViewModel(choices: choices, shuffle: shuffle) ) } @@ -23,9 +23,19 @@ public struct DragAndDropAssociationView: View { fatalError("Exercise payload is not .association") } - // self._viewModel = StateObject(wrappedValue: DragAndDropAssociationViewViewModel(choices: payload.choices)) self._viewModel = StateObject( - wrappedValue: DragAndDropAssociationViewViewModel(choices: payload.choices, shared: data)) + wrappedValue: DragAndDropAssociationViewViewModel( + choices: payload.choices, + shuffle: payload.shuffleChoices + ) + ) + self._viewModel = StateObject( + wrappedValue: DragAndDropAssociationViewViewModel( + choices: payload.choices, + shuffle: payload.shuffleChoices, + shared: data + ) + ) } public var body: some View { diff --git a/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationViewViewModel.swift b/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationViewViewModel.swift index d80b8a7cdd..90954c7e5a 100644 --- a/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationViewViewModel.swift +++ b/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationViewViewModel.swift @@ -14,13 +14,13 @@ class DragAndDropAssociationViewViewModel: ObservableObject { private let gameplay: GameplayAssociation private var cancellables: Set = [] - init(choices: [AssociationChoice], shared: ExerciseSharedData? = nil) { + init(choices: [AssociationChoice], shuffle: Bool = false, shared: ExerciseSharedData? = nil) { let gameplayChoiceModel = choices.map { GameplayAssociationChoiceModel(choice: $0) } self.choices = gameplayChoiceModel - self.gameplay = GameplayAssociation(choices: gameplayChoiceModel) + self.gameplay = GameplayAssociation(choices: gameplayChoiceModel, shuffle: shuffle) self.exercicesSharedData = shared ?? ExerciseSharedData() - subscribeToGameplayDragAndDropChoicesUpdates() + subscribeToGameplayDragAndDropAssociationChoicesUpdates() subscribeToGameplayStateUpdates() } @@ -28,7 +28,7 @@ class DragAndDropAssociationViewViewModel: ObservableObject { gameplay.process(choice, destination) } - private func subscribeToGameplayDragAndDropChoicesUpdates() { + private func subscribeToGameplayDragAndDropAssociationChoicesUpdates() { gameplay.choices .receive(on: DispatchQueue.main) .sink { From 7f500d694fb2c197d46f4ee966e7208069418589 Mon Sep 17 00:00:00 2001 From: mathieu J Date: Mon, 13 Nov 2023 17:38:40 +0200 Subject: [PATCH 6/9] :sparkles: (GEK): Shuffle choices for Association templates --- .../Views/Association/DragAndDropAssociationViewViewModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationViewViewModel.swift b/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationViewViewModel.swift index 90954c7e5a..7fe318d30e 100644 --- a/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationViewViewModel.swift +++ b/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationViewViewModel.swift @@ -16,7 +16,7 @@ class DragAndDropAssociationViewViewModel: ObservableObject { init(choices: [AssociationChoice], shuffle: Bool = false, shared: ExerciseSharedData? = nil) { let gameplayChoiceModel = choices.map { GameplayAssociationChoiceModel(choice: $0) } - self.choices = gameplayChoiceModel + self.choices = shuffle ? gameplayChoiceModel.shuffled() : gameplayChoiceModel self.gameplay = GameplayAssociation(choices: gameplayChoiceModel, shuffle: shuffle) self.exercicesSharedData = shared ?? ExerciseSharedData() From a879ad0cc9f751590693eaab7889a11214494d79 Mon Sep 17 00:00:00 2001 From: mathieu J Date: Tue, 14 Nov 2023 12:53:23 +0200 Subject: [PATCH 7/9] :bug: (GEK): Move shuffle from gameplay to ViewViewModel --- .../Sources/_NewSystem/Gameplays/GameplayAssociation.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/GameEngineKit/Sources/_NewSystem/Gameplays/GameplayAssociation.swift b/Modules/GameEngineKit/Sources/_NewSystem/Gameplays/GameplayAssociation.swift index 6d2046cc31..0f75acf8ce 100644 --- a/Modules/GameEngineKit/Sources/_NewSystem/Gameplays/GameplayAssociation.swift +++ b/Modules/GameEngineKit/Sources/_NewSystem/Gameplays/GameplayAssociation.swift @@ -27,7 +27,7 @@ extension GameplayAssociation where ChoiceModelType == GameplayAssociationChoice convenience init(choices: [GameplayAssociationChoiceModel], shuffle: Bool = false) { self.init() - self.choices.send(shuffle ? choices.shuffled() : choices) + self.choices.send(choices) self.state.send(.playing) } From b6c45dece69b390690630e97c1d9d5ff478183a0 Mon Sep 17 00:00:00 2001 From: mathieu J Date: Wed, 15 Nov 2023 16:43:12 +0200 Subject: [PATCH 8/9] :recycle: (GEK): Refactor Association to extract BaseScene --- .../Models/CharacteristicModelNotifying.swift | 5 +- .../Models/CharacteristicModelWriteOnly.swift | 2 +- ...agAndDropAssociationFourChoicesScene.swift | 25 ++++ ...ragAndDropAssociationSixChoicesScene.swift | 25 ++++ .../DragAndDropAssociationBaseScene.swift | 30 ++-- ...ragAndDropAssociationSixChoicesScene.swift | 131 ------------------ .../DragAndDropAssociationView.swift | 8 +- .../DraggableImageShadowNode.swift | 2 +- .../UI/Buttons/ButtonStyle+Extension.swift | 8 +- 9 files changed, 70 insertions(+), 166 deletions(-) create mode 100644 Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociation/DragAndDropAssociationFourChoicesScene.swift create mode 100644 Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociation/DragAndDropAssociationSixChoicesScene.swift delete mode 100644 Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationSixChoices/DragAndDropAssociationSixChoicesScene.swift diff --git a/Modules/BLEKit/Sources/Models/CharacteristicModelNotifying.swift b/Modules/BLEKit/Sources/Models/CharacteristicModelNotifying.swift index e6a46f838d..c013dc83a3 100644 --- a/Modules/BLEKit/Sources/Models/CharacteristicModelNotifying.swift +++ b/Modules/BLEKit/Sources/Models/CharacteristicModelNotifying.swift @@ -13,7 +13,10 @@ public struct CharacteristicModelNotifying { public let cbCharacteristic: CBCharacteristic? public let onNotification: Callback? - public init(characteristicUUID: CBUUID, serviceUUID: CBUUID, cbCharacteristic: CBCharacteristic? = nil, onNotification: Callback? = nil ) { + public init( + characteristicUUID: CBUUID, serviceUUID: CBUUID, cbCharacteristic: CBCharacteristic? = nil, + onNotification: Callback? = nil + ) { self.characteristicUUID = characteristicUUID self.serviceUUID = serviceUUID self.cbCharacteristic = cbCharacteristic diff --git a/Modules/BLEKit/Sources/Models/CharacteristicModelWriteOnly.swift b/Modules/BLEKit/Sources/Models/CharacteristicModelWriteOnly.swift index c2b4e9e1cd..f19f100a3d 100644 --- a/Modules/BLEKit/Sources/Models/CharacteristicModelWriteOnly.swift +++ b/Modules/BLEKit/Sources/Models/CharacteristicModelWriteOnly.swift @@ -12,7 +12,7 @@ public struct CharacteristicModelWriteOnly { public let serviceUUID: CBUUID public let onWrite: Callback? - public init(characteristicUUID: CBUUID, serviceUUID: CBUUID, onWrite: Callback? = nil ) { + public init(characteristicUUID: CBUUID, serviceUUID: CBUUID, onWrite: Callback? = nil) { self.characteristicUUID = characteristicUUID self.serviceUUID = serviceUUID self.onWrite = onWrite diff --git a/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociation/DragAndDropAssociationFourChoicesScene.swift b/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociation/DragAndDropAssociationFourChoicesScene.swift new file mode 100644 index 0000000000..bdb65b69e3 --- /dev/null +++ b/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociation/DragAndDropAssociationFourChoicesScene.swift @@ -0,0 +1,25 @@ +// Leka - iOS Monorepo +// Copyright 2023 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +import SpriteKit + +final class DragAndDropAssociationFourChoicesScene: DragAndDropAssociationBaseScene { + + override func setFirstAnswerPosition() { + spacer = 455 + initialNodeX = (size.width - spacer) / 2 + verticalSpacing = self.size.height / 3 + defaultPosition = CGPoint(x: initialNodeX, y: verticalSpacing - 30) + } + + override func setNextAnswerPosition(_ index: Int) { + if [0, 2].contains(index) { + defaultPosition.x += spacer + } else { + defaultPosition.x = initialNodeX + defaultPosition.y += verticalSpacing + 60 + } + } + +} diff --git a/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociation/DragAndDropAssociationSixChoicesScene.swift b/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociation/DragAndDropAssociationSixChoicesScene.swift new file mode 100644 index 0000000000..95569a32c9 --- /dev/null +++ b/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociation/DragAndDropAssociationSixChoicesScene.swift @@ -0,0 +1,25 @@ +// Leka - iOS Monorepo +// Copyright 2023 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +import SpriteKit + +final class DragAndDropAssociationSixChoicesScene: DragAndDropAssociationBaseScene { + + override func setFirstAnswerPosition() { + spacer = 340 + initialNodeX = (size.width - (spacer * 2)) / 2 + verticalSpacing = self.size.height / 3 + defaultPosition = CGPoint(x: initialNodeX, y: verticalSpacing - 30) + } + + override func setNextAnswerPosition(_ index: Int) { + if [0, 1, 3, 4].contains(index) { + defaultPosition.x += spacer + } else { + defaultPosition.x = initialNodeX + defaultPosition.y += verticalSpacing + 60 + } + } + +} diff --git a/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationBaseScene.swift b/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationBaseScene.swift index 205902079c..52da2d78d3 100644 --- a/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationBaseScene.swift +++ b/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationBaseScene.swift @@ -9,16 +9,15 @@ import SwiftUI class DragAndDropAssociationBaseScene: SKScene { var viewModel: DragAndDropAssociationViewViewModel - var spacer: CGFloat = 455 + var spacer = CGFloat.zero var defaultPosition = CGPoint.zero var initialNodeX: CGFloat = .zero var verticalSpacing: CGFloat = .zero - var dropDestinationAnchor: CGPoint = .zero - var biggerSide: CGFloat = 150 - var playedNode: DraggableImageAnswerNode? - var playedDestination: DraggableImageAnswerNode? - var dropDestinations: [DraggableImageAnswerNode] = [] - var selectedNodes: [UITouch: DraggableImageAnswerNode] = [:] + private var biggerSide: CGFloat = 150 + private var playedNode: DraggableImageAnswerNode? + private var playedDestination: DraggableImageAnswerNode? + private var dropDestinations: [DraggableImageAnswerNode] = [] + private var selectedNodes: [UITouch: DraggableImageAnswerNode] = [:] private var expectedItemsNodes: [String: [SKSpriteNode]] = [:] private var cancellables: Set = [] @@ -92,7 +91,7 @@ class DragAndDropAssociationBaseScene: SKScene { } } - func bindNodesToSafeArea(_ nodes: [SKSpriteNode], limit: CGFloat = 120) { + func bindNodesToSafeArea(_ nodes: [SKSpriteNode], limit: CGFloat = 80) { let xRange = SKRange(lowerLimit: 0, upperLimit: size.width - limit) let yRange = SKRange(lowerLimit: 0, upperLimit: size.height - limit) for node in nodes { @@ -101,25 +100,15 @@ class DragAndDropAssociationBaseScene: SKScene { } func setFirstAnswerPosition() { - initialNodeX = (size.width - spacer) / 2 - verticalSpacing = self.size.height / 3 - defaultPosition = CGPoint(x: initialNodeX, y: verticalSpacing - 30) + fatalError("setFirstAnswerPosition() has not been implemented") } func setNextAnswerPosition(_ index: Int) { - if [0, 2].contains(index) { - defaultPosition.x += spacer - } else { - defaultPosition.x = initialNodeX - defaultPosition.y += verticalSpacing + 60 - } + fatalError("setNextAnswerPosition(_ index:) has not been implemented") } func goodAnswerBehavior(_ node: DraggableImageAnswerNode) { node.scaleForMax(sizeOf: biggerSide * 0.8) - node.position = CGPoint( - x: dropDestinationAnchor.x - 60, - y: dropDestinationAnchor.y - 60) node.zPosition = 10 node.isDraggable = false playedDestination?.isDraggable = false @@ -216,7 +205,6 @@ class DragAndDropAssociationBaseScene: SKScene { wrongAnswerBehavior(playedNode!) break } - dropDestinationAnchor = destinationNode.position playedDestination = destinationNode guard let destination = viewModel.choices.first(where: { $0.choice.value == destinationNode.name }) diff --git a/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationSixChoices/DragAndDropAssociationSixChoicesScene.swift b/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationSixChoices/DragAndDropAssociationSixChoicesScene.swift deleted file mode 100644 index 235d31e74d..0000000000 --- a/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationSixChoices/DragAndDropAssociationSixChoicesScene.swift +++ /dev/null @@ -1,131 +0,0 @@ -// Leka - iOS Monorepo -// Copyright 2023 APF France handicap -// SPDX-License-Identifier: Apache-2.0 - -import Combine -import ContentKit -import SpriteKit -import SwiftUI - -final class DragAndDropAssociationSixChoicesScene: DragAndDropAssociationBaseScene { - - private var freeSlots: [String: [Bool]] = [:] - private var endAbscissa: CGFloat = .zero - private var finalXPosition: CGFloat = .zero - - override init(viewModel: DragAndDropAssociationViewViewModel) { - super.init(viewModel: viewModel) - self.viewModel = viewModel - - subscribeToChoicesUpdates() - } - - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func reset() { - self.backgroundColor = .clear - self.removeAllChildren() - self.removeAllActions() - - dropDestinations = [] - - setAnswersFinalPositionSlots() - setFirstAnswerPosition() - layoutAnswers() - } - - private func setAnswersFinalPositionSlots() { - freeSlots = [:] - for gameplayChoiceModel in viewModel.choices { - let category = gameplayChoiceModel.choice.category.rawValue - freeSlots[category] = [true, true] - } - } - - override func setFirstAnswerPosition() { - spacer = 340 - initialNodeX = (size.width - (spacer * 2)) / 2 - verticalSpacing = self.size.height / 3 - defaultPosition = CGPoint(x: initialNodeX, y: verticalSpacing - 30) - } - - override func setNextAnswerPosition(_ index: Int) { - if [0, 1, 3, 4].contains(index) { - defaultPosition.x += spacer - } else { - defaultPosition.x = initialNodeX - defaultPosition.y += verticalSpacing + 60 - } - } - - private func setFinalXPosition(category: String) { - guard endAbscissa <= dropDestinationAnchor.x else { - finalXPosition = { - guard freeSlots[category]![1] else { - freeSlots[category]![0] = false - return dropDestinationAnchor.x - 80 - } - freeSlots[category]![1] = false - return dropDestinationAnchor.x + 80 - }() - return - } - finalXPosition = { - guard freeSlots[category]![0] else { - freeSlots[category]![1] = false - return dropDestinationAnchor.x + 80 - } - freeSlots[category]![0] = false - return dropDestinationAnchor.x - 80 - }() - } - - override func goodAnswerBehavior(_ node: DraggableImageAnswerNode) { - node.scaleForMax(sizeOf: biggerSide * 0.8) - node.position = CGPoint( - x: finalXPosition, - y: dropDestinationAnchor.y - 60) - node.zPosition = 10 - node.isDraggable = false - playedDestination?.isDraggable = false - onDropAction(node) - if viewModel.exercicesSharedData.state == .completed { - DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { [self] in - exerciseCompletedBehavior() - } - } - } - - override func touchesEnded(_ touches: Set, with: UIEvent?) { - for touch in touches { - guard selectedNodes.keys.contains(touch) else { - break - } - playedNode = selectedNodes[touch]! - playedNode!.scaleForMax(sizeOf: biggerSide) - endAbscissa = touch.location(in: self).x - - guard - let destinationNode = dropDestinations.first(where: { - $0.frame.contains(touch.location(in: self)) && $0.name != playedNode!.name - }) - else { - wrongAnswerBehavior(playedNode!) - break - } - dropDestinationAnchor = destinationNode.position - playedDestination = destinationNode - - guard let destination = viewModel.choices.first(where: { $0.choice.value == destinationNode.name }) - else { return } - guard let choice = viewModel.choices.first(where: { $0.choice.value == playedNode!.name }) - else { return } - - // dropped within the bounds of the proper sibling - setFinalXPosition(category: destination.choice.category.rawValue) - viewModel.onChoiceTapped(choice: choice, destination: destination) - } - } -} diff --git a/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationView.swift b/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationView.swift index acccf01a8a..f470ddaaef 100644 --- a/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationView.swift +++ b/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationView.swift @@ -23,12 +23,6 @@ public struct DragAndDropAssociationView: View { fatalError("Exercise payload is not .association") } - self._viewModel = StateObject( - wrappedValue: DragAndDropAssociationViewViewModel( - choices: payload.choices, - shuffle: payload.shuffleChoices - ) - ) self._viewModel = StateObject( wrappedValue: DragAndDropAssociationViewViewModel( choices: payload.choices, @@ -47,7 +41,7 @@ public struct DragAndDropAssociationView: View { .frame(width: proxy.size.width, height: proxy.size.height) .onAppear { if viewModel.choices.count == 4 { - scene = DragAndDropAssociationBaseScene(viewModel: viewModel) + scene = DragAndDropAssociationFourChoicesScene(viewModel: viewModel) } else { scene = DragAndDropAssociationSixChoicesScene(viewModel: viewModel) } diff --git a/Modules/GameEngineKit/Sources/_NewSystem/Views/DragAndDrop/DraggableImageShadowNode.swift b/Modules/GameEngineKit/Sources/_NewSystem/Views/DragAndDrop/DraggableImageShadowNode.swift index e49203bc68..b5db8d3508 100644 --- a/Modules/GameEngineKit/Sources/_NewSystem/Views/DragAndDrop/DraggableImageShadowNode.swift +++ b/Modules/GameEngineKit/Sources/_NewSystem/Views/DragAndDrop/DraggableImageShadowNode.swift @@ -26,5 +26,5 @@ class DraggableImageShadowNode: SKSpriteNode { required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } - + } diff --git a/Modules/RobotKit/Sources/UI/Buttons/ButtonStyle+Extension.swift b/Modules/RobotKit/Sources/UI/Buttons/ButtonStyle+Extension.swift index c933c6b0fb..284881f1d5 100644 --- a/Modules/RobotKit/Sources/UI/Buttons/ButtonStyle+Extension.swift +++ b/Modules/RobotKit/Sources/UI/Buttons/ButtonStyle+Extension.swift @@ -8,17 +8,17 @@ import SwiftUI // MARK: - Public ButtonStyle extensions // -public extension ButtonStyle where Self == RobotControlPlainButtonStyle { +extension ButtonStyle where Self == RobotControlPlainButtonStyle { - static func robotControlPlainButtonStyle(foreground: Color? = nil, background: Color? = nil) -> Self { + public static func robotControlPlainButtonStyle(foreground: Color? = nil, background: Color? = nil) -> Self { .init(foreground: foreground, background: background) } } -public extension ButtonStyle where Self == RobotControlBorderedButtonStyle { +extension ButtonStyle where Self == RobotControlBorderedButtonStyle { - static func robotControlBorderedButtonStyle(foreground: Color? = nil, border: Color? = nil) -> Self { + public static func robotControlBorderedButtonStyle(foreground: Color? = nil, border: Color? = nil) -> Self { .init(foreground: foreground, border: border) } From 9cc4d02ef536709ca4f1c38d51d154086906539d Mon Sep 17 00:00:00 2001 From: mathieu J Date: Wed, 15 Nov 2023 18:46:26 +0200 Subject: [PATCH 9/9] :recycle: (GEK): Simplify gameplay down to necessary features --- .../Gameplays/GameplayAssociation.swift | 20 ++++++++----------- .../DragAndDropAssociationBaseScene.swift | 2 +- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/Modules/GameEngineKit/Sources/_NewSystem/Gameplays/GameplayAssociation.swift b/Modules/GameEngineKit/Sources/_NewSystem/Gameplays/GameplayAssociation.swift index 0f75acf8ce..ca8b1f93f9 100644 --- a/Modules/GameEngineKit/Sources/_NewSystem/Gameplays/GameplayAssociation.swift +++ b/Modules/GameEngineKit/Sources/_NewSystem/Gameplays/GameplayAssociation.swift @@ -10,8 +10,6 @@ class GameplayAssociation: StatefulGameplayProtocol where ChoiceModelType: GameplayChoiceModelProtocol { var choices: CurrentValueSubject<[GameplayAssociationChoiceModel], Never> = .init([]) - var rightAnswers: [ChoiceModelType] = [] - var destinations: [ChoiceModelType] = [] var state: CurrentValueSubject = .init(.idle) func updateChoice(_ choice: ChoiceModelType, state: GameplayChoiceState) { @@ -32,20 +30,18 @@ extension GameplayAssociation where ChoiceModelType == GameplayAssociationChoice } func process(_ choice: ChoiceModelType, _ destination: ChoiceModelType) { - if choice.choice.category == destination.choice.category - && !rightAnswers.contains(where: { $0.id == destination.id }) - { + if choice.choice.category == destination.choice.category { updateChoice(choice, state: .rightAnswer) - rightAnswers.append(choice) - if !destinations.contains(where: { $0.id == destination.id }) { - destinations.append(destination) - } - if (rightAnswers.count + destinations.count) == self.choices.value.count { - state.send(.completed) - } + updateChoice(destination, state: .rightAnswer) + } else { updateChoice(choice, state: .wrongAnswer) } + + guard choices.value.allSatisfy({ $0.state == .rightAnswer }) else { + return + } + state.send(.completed) } } diff --git a/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationBaseScene.swift b/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationBaseScene.swift index 52da2d78d3..7a143ed833 100644 --- a/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationBaseScene.swift +++ b/Modules/GameEngineKit/Sources/_NewSystem/Views/Association/DragAndDropAssociationBaseScene.swift @@ -109,7 +109,7 @@ class DragAndDropAssociationBaseScene: SKScene { func goodAnswerBehavior(_ node: DraggableImageAnswerNode) { node.scaleForMax(sizeOf: biggerSide * 0.8) - node.zPosition = 10 + node.zPosition = (self.playedDestination?.zPosition ?? 10) + 10 node.isDraggable = false playedDestination?.isDraggable = false onDropAction(node)