Skip to content

Commit db5f770

Browse files
committed
Refine completion UIs and remove obsolete settings flow
1 parent aff240f commit db5f770

5 files changed

Lines changed: 569 additions & 48 deletions

File tree

IOS_PRODUCT_COMPLETION_PLAN.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,15 @@ Die iOS-Version soll sich in der **Spiellogik** wie die Android-Version verhalte
6060

6161
**Android-seitig vorhanden, iOS-seitig unvollständig/fehlend:**
6262
- [x] Hauptmenü
63-
- [ ] Settings (mind. Audio-Mute)
64-
- [ ] Credits
65-
- [ ] Tutorial/Onboarding
63+
- [ ] Settings (entfernt, da aktuell kein Audio im iOS-Build aktiv ist)
64+
- [x] Credits
65+
- [x] Tutorial/Onboarding
6666
- [x] Levelmode-Completion
6767
- [ ] Optional Loading-Übergang
6868

6969
**Umsetzung:**
7070
- [x] Story-/Navigation auf iOS erweitern
71-
- [ ] Flows als eigene Szenen oder äquivalente iOS-Navigation umsetzen
71+
- [x] Flows als eigene Szenen oder äquivalente iOS-Navigation umsetzen
7272

7373
**Abnahmekriterium:**
7474
- [ ] Alle wesentlichen Modi/Zustände sind für Nutzer erreichbar und vollständig rücknavigierbar.

SOPA/JustPlay/JustPlayGameScene.swift

Lines changed: 75 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Foundation
22
import SpriteKit
3+
import UIKit
34

45
protocol JustPlaySceneObserver: AnyObject {
56
func updateJustPlayScene(remainingTime: Int)
@@ -362,12 +363,14 @@ class JustPlayGameScene: GameScene, JustPlaySceneObserver {
362363
class JustPlayScoreScene: SKScene {
363364
private let justPlayResult: JustPlayResult
364365
private let textColor = UIColor(red: 240.0 / 255.0, green: 239.0 / 255.0, blue: 238.0 / 255.0, alpha: 1.0)
366+
private let background = UIColor(red: 90.6 / 255.0, green: 86.7 / 255.0, blue: 70.6 / 255.0, alpha: 1.0)
365367

366368
init(size: CGSize, proportionSet: ProportionSet, justPlayResult: JustPlayResult) {
367369
self.justPlayResult = justPlayResult
368370
_ = proportionSet
369371
super.init(size: size)
370-
self.backgroundColor = UIColor(red: 90.6 / 255.0, green: 86.7 / 255.0, blue: 70.6 / 255, alpha: 1.0)
372+
self.backgroundColor = background
373+
addBackgroundDecor()
371374
addLabels()
372375
addButtons()
373376
}
@@ -379,35 +382,21 @@ class JustPlayScoreScene: SKScene {
379382
private func addLabels() {
380383
let title = SKLabelNode(fontNamed: "Optima-Bold")
381384
title.text = "Level \(justPlayResult.levelCount) Complete"
382-
title.fontSize = min(size.width, size.height) * 0.07
385+
title.fontSize = min(size.width, size.height) * 0.082
383386
title.fontColor = textColor
384-
title.position = CGPoint(x: size.width * 0.5, y: size.height * 0.82)
387+
title.position = CGPoint(x: size.width * 0.5, y: size.height * 0.84)
385388
addChild(title)
386389

387-
let score = SKLabelNode(fontNamed: "Optima-Bold")
388-
score.text = "Score: \(justPlayResult.lastScore) -> \(justPlayResult.score)"
389-
score.fontSize = min(size.width, size.height) * 0.052
390-
score.fontColor = textColor
391-
score.position = CGPoint(x: size.width * 0.5, y: size.height * 0.60)
392-
addChild(score)
393-
394-
let time = SKLabelNode(fontNamed: "Optima-Bold")
395-
time.text = "Time Left: \(justPlayResult.leftTime)"
396-
time.fontSize = min(size.width, size.height) * 0.043
397-
time.fontColor = textColor
398-
time.position = CGPoint(x: size.width * 0.5, y: size.height * 0.50)
399-
addChild(time)
390+
let panel = makeInfoPanel(centerY: size.height * 0.57, width: size.width * 0.84, height: size.height * 0.34)
391+
addChild(panel)
400392

401-
let extraTime = SKLabelNode(fontNamed: "Optima-Bold")
402-
extraTime.text = "Extra Time: +\(justPlayResult.extraTime)"
403-
extraTime.fontSize = min(size.width, size.height) * 0.043
404-
extraTime.fontColor = textColor
405-
extraTime.position = CGPoint(x: size.width * 0.5, y: size.height * 0.43)
406-
addChild(extraTime)
393+
addInfoLine(icon: "star.circle.fill", text: "Score", value: "\(justPlayResult.score)", y: size.height * 0.64)
394+
addInfoLine(icon: "clock.circle.fill", text: "Time Left", value: "\(justPlayResult.leftTime)", y: size.height * 0.56)
395+
addInfoLine(icon: "plus.circle.fill", text: "Extra Time", value: "+\(justPlayResult.extraTime)", y: size.height * 0.48)
407396

408397
let helper = SKLabelNode(fontNamed: "Optima-Bold")
409398
helper.text = "Continue to the next level?"
410-
helper.fontSize = min(size.width, size.height) * 0.036
399+
helper.fontSize = min(size.width, size.height) * 0.042
411400
helper.fontColor = textColor
412401
helper.position = CGPoint(x: size.width * 0.5, y: size.height * 0.31)
413402
helper.alpha = 0.9
@@ -431,6 +420,69 @@ class JustPlayScoreScene: SKScene {
431420
levelChoice.position = CGPoint(x: size.width * 0.28, y: size.height * 0.17)
432421
addChild(levelChoice)
433422
}
423+
424+
private func addBackgroundDecor() {
425+
let topGlow = SKShapeNode(circleOfRadius: size.width * 0.28)
426+
topGlow.fillColor = UIColor(white: 1.0, alpha: 0.05)
427+
topGlow.strokeColor = .clear
428+
topGlow.position = CGPoint(x: size.width * 0.15, y: size.height * 0.88)
429+
topGlow.zPosition = -2
430+
addChild(topGlow)
431+
432+
let bottomGlow = SKShapeNode(circleOfRadius: size.width * 0.32)
433+
bottomGlow.fillColor = UIColor(white: 1.0, alpha: 0.04)
434+
bottomGlow.strokeColor = .clear
435+
bottomGlow.position = CGPoint(x: size.width * 0.88, y: size.height * 0.14)
436+
bottomGlow.zPosition = -2
437+
addChild(bottomGlow)
438+
}
439+
440+
private func makeInfoPanel(centerY: CGFloat, width: CGFloat, height: CGFloat) -> SKShapeNode {
441+
let rect = CGRect(x: -width / 2.0, y: -height / 2.0, width: width, height: height)
442+
let panel = SKShapeNode(rect: rect, cornerRadius: min(width, height) * 0.08)
443+
panel.position = CGPoint(x: size.width * 0.5, y: centerY)
444+
panel.fillColor = UIColor(white: 1.0, alpha: 0.08)
445+
panel.strokeColor = UIColor(white: 1.0, alpha: 0.18)
446+
panel.lineWidth = 2.0
447+
panel.zPosition = -1
448+
return panel
449+
}
450+
451+
private func addInfoLine(icon: String, text: String, value: String, y: CGFloat) {
452+
let iconNode = SKSpriteNode(texture: makeStatIconTexture(symbolName: icon))
453+
iconNode.size = CGSize(width: size.height * 0.045, height: size.height * 0.045)
454+
iconNode.position = CGPoint(x: size.width * 0.18, y: y)
455+
addChild(iconNode)
456+
457+
let line = SKLabelNode(fontNamed: "Optima-Bold")
458+
line.text = "\(text): \(value)"
459+
line.horizontalAlignmentMode = .left
460+
line.verticalAlignmentMode = .center
461+
line.fontSize = min(size.width, size.height) * 0.056
462+
line.fontColor = textColor
463+
line.position = CGPoint(x: size.width * 0.22, y: y)
464+
addChild(line)
465+
}
466+
467+
private func makeStatIconTexture(symbolName: String) -> SKTexture {
468+
let side = size.height * 0.06
469+
let renderer = UIGraphicsImageRenderer(size: CGSize(width: side, height: side))
470+
let image = renderer.image { _ in
471+
let config = UIImage.SymbolConfiguration(pointSize: side * 0.72, weight: .semibold)
472+
let tint = textColor
473+
if let symbol = UIImage(systemName: symbolName, withConfiguration: config)?
474+
.withTintColor(tint, renderingMode: .alwaysOriginal) {
475+
let symbolRect = CGRect(
476+
x: (side - symbol.size.width) / 2.0,
477+
y: (side - symbol.size.height) / 2.0,
478+
width: symbol.size.width,
479+
height: symbol.size.height
480+
)
481+
symbol.draw(in: symbolRect)
482+
}
483+
}
484+
return SKTexture(image: image)
485+
}
434486
}
435487

436488
class JustPlayLostScene: SKScene {

SOPA/manager/StoryService.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,20 @@ protocol StoryService {
1313
func loadStartMenuScene()
1414
func loadLevelCoiceScene()
1515
func loadLevelCoiceSceneFromLevelModeScene()
16-
16+
17+
func loadCreditsSceneFromMenuScene()
18+
func loadTutorialSceneFromMenuScene()
19+
func loadTutorialGameSceneFromTutorialScene()
20+
1721
func loadJustPlaySceneFromMenuScene()
1822
func loadJustPlaySceneFromJustPlayScene(timeBasedGameService: TimeBasedGameService, justPlayLevel: JustPlayLevel)
1923
func loadJustPlayScoreSceneFromJustPlayScene(justPlayLevelResult: JustPlayLevelResult)
2024
func loadJustPlayLostSceneFromJustPlayScene(justPlayLevelResult: JustPlayLevelResult)
2125
func loadNextJustPlayGameScene()
2226
func reloadJustPlayGameScene(level: Level)
23-
27+
2428
func loadLevelModeGameSceneFromChoiceScene(levelId: Int)
2529
func loadLevelModeScoreSceneFromLevelModeScene(levelResult: LevelResult)
2630
func reloadLevelModeGameScene(levelId: Int)
2731
func loadNextLevelModeGameScene(levelId: Int)
28-
29-
3032
}

SOPA/manager/StoryServiceImpl.swift

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,38 @@ import SpriteKit
1111

1212
class StoryServiceImpl: StoryService {
1313
private var justPlayService: JustPlayService?
14-
14+
1515
func loadStartMenuScene() {
1616
let startMenuScene = StartMenuScene(size: size)
1717
startMenuScene.scaleMode = .resizeFill
1818
currentView.presentScene(startMenuScene)
1919
}
2020

21+
func loadCreditsSceneFromMenuScene() {
22+
let creditsScene = CreditsScene(size: size)
23+
let transition = SKTransition.push(with: .left, duration: 0.35)
24+
currentView.presentScene(creditsScene, transition: transition)
25+
}
26+
27+
func loadTutorialSceneFromMenuScene() {
28+
let tutorialScene = TutorialScene(size: size)
29+
let transition = SKTransition.push(with: .left, duration: 0.35)
30+
currentView.presentScene(tutorialScene, transition: transition)
31+
}
32+
33+
func loadTutorialGameSceneFromTutorialScene() {
34+
let tutorialLevel = levelService.getLevelById(id: 1) ?? makeFallbackTutorialLevel()
35+
let tutorialGameScene = TutorialGameScene(size: size, proportionSet: proportionSet, level: tutorialLevel)
36+
currentView.presentScene(tutorialGameScene)
37+
}
38+
2139
func loadJustPlaySceneFromJustPlayScene(timeBasedGameService: TimeBasedGameService, justPlayLevel: JustPlayLevel) {
2240
guard let currentJustPlayService = justPlayService else {
2341
loadJustPlaySceneFromMenuScene()
2442
return
2543
}
2644
let justPlayGameScene = JustPlayGameScene(size: size, proportionSet: proportionSet, justPlayLevel: justPlayLevel, justPlayService: currentJustPlayService, timeBasedGameService: timeBasedGameService)
27-
let transition = SKTransition.crossFade(withDuration: 0.5)
28-
currentView.presentScene(justPlayGameScene, transition: transition)
45+
currentView.presentScene(justPlayGameScene)
2946
}
3047

3148
func loadJustPlayScoreSceneFromJustPlayScene(justPlayLevelResult: JustPlayLevelResult) {
@@ -59,8 +76,7 @@ class StoryServiceImpl: StoryService {
5976
let timeBasedGameService = TimeBasedGameServiceImpl(remainingTime: justPlayLevel.leftTime)
6077
timeBasedGameService.start()
6178
let justPlayScene = JustPlayGameScene(size: size, proportionSet: proportionSet, justPlayLevel: justPlayLevel, justPlayService: currentJustPlayService, timeBasedGameService: timeBasedGameService)
62-
let transition = SKTransition.push(with: .left, duration: 0.5)
63-
currentView.presentScene(justPlayScene, transition: transition)
79+
currentView.presentScene(justPlayScene)
6480
}
6581

6682
func reloadJustPlayGameScene(level: Level) {
@@ -70,8 +86,7 @@ class StoryServiceImpl: StoryService {
7086
let timeBasedGameService = TimeBasedGameServiceImpl(remainingTime: 10)
7187
timeBasedGameService.start()
7288
let justPlayGameScene = JustPlayGameScene(size: size, proportionSet: proportionSet, justPlayLevel: JustPlayLevel(leftTime: 10, level: level), justPlayService: justPlayService!, timeBasedGameService: timeBasedGameService)
73-
let transition = SKTransition.crossFade(withDuration: 0.5)
74-
currentView.presentScene(justPlayGameScene, transition: transition)
89+
currentView.presentScene(justPlayGameScene)
7590
}
7691

7792
func loadJustPlaySceneFromMenuScene() {
@@ -81,8 +96,7 @@ class StoryServiceImpl: StoryService {
8196
let timeBasedGameService = TimeBasedGameServiceImpl(remainingTime: justPlayLevel.leftTime)
8297
timeBasedGameService.start()
8398
let justPlayScene = JustPlayGameScene(size: size, proportionSet: proportionSet, justPlayLevel: justPlayLevel, justPlayService: currentJustPlayService, timeBasedGameService: timeBasedGameService)
84-
let transition = SKTransition.push(with: .down, duration: 0.5)
85-
currentView.presentScene(justPlayScene, transition: transition)
99+
currentView.presentScene(justPlayScene)
86100
}
87101

88102

@@ -116,8 +130,7 @@ class StoryServiceImpl: StoryService {
116130

117131
func loadLevelModeGameSceneFromChoiceScene(levelId: Int) {
118132
let levelModeGameScene = LevelModeGameScene(size: size, proportionSet: proportionSet, level: levelService.getLevelById(id: levelId)!)
119-
let transition = SKTransition.push(with: .down, duration: 0.5)
120-
currentView.presentScene(levelModeGameScene, transition: transition)
133+
currentView.presentScene(levelModeGameScene)
121134
}
122135

123136
func loadLevelModeScoreSceneFromLevelModeScene(levelResult: LevelResult) {
@@ -129,15 +142,27 @@ class StoryServiceImpl: StoryService {
129142
func reloadLevelModeGameScene(levelId: Int) {
130143
let levelModeGameScene = LevelModeGameScene(size:
131144
size, proportionSet: proportionSet, level: levelService.getLevelById(id: levelId)!)
132-
let transition = SKTransition.crossFade(withDuration: 0.5)
133-
currentView.presentScene(levelModeGameScene, transition: transition)
145+
currentView.presentScene(levelModeGameScene)
134146
}
135147

136148
func loadNextLevelModeGameScene(levelId: Int) {
137149
let levelModeGameScene = LevelModeGameScene(size: size, proportionSet: proportionSet, level: levelService.getLevelById(id: levelId)!)
138-
let transition = SKTransition.push(with: .left, duration: 0.5)
139-
currentView.presentScene(levelModeGameScene, transition: transition)
150+
currentView.presentScene(levelModeGameScene)
151+
}
152+
153+
private func makeFallbackTutorialLevel() -> Level {
154+
let levelLines = [
155+
"1",
156+
"1",
157+
"4",
158+
"#",
159+
"nnnnnn",
160+
"noooon",
161+
"soacon",
162+
"fagoon",
163+
"noooon",
164+
"nnnnnn"
165+
]
166+
return LevelTranslator().fromString(levelLines: levelLines)
140167
}
141-
142-
143168
}

0 commit comments

Comments
 (0)