Created
November 18, 2025 15:29
-
-
Save profh/c504005e06e807314083308e0b6a1cd0 to your computer and use it in GitHub Desktop.
BalloonPop_Solutions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // TODO: TASK 1 - Handle user touches to detect balloon pops | |
| /* Handle touch-down events during active gameplay | |
| **Note**: This demonstrates several key concepts: | |
| 1. Converting touch coordinates between different coordinate systems | |
| 2. Hit-testing to find which nodes were touched | |
| 3. Filtering nodes by their 'name' property | |
| 4. Early exit patterns with guard statements | |
| - Parameters: | |
| - touches: Set of UITouch objects (usually just one for single tap) | |
| - event: The event containing all touches (can be nil) | |
| */ | |
| func handleTouchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { | |
| // Exit early if game is not active (prevents touches during game over screen) | |
| guard isGameActive else { return } | |
| // Get the first touch from the set | |
| // **Note**: iOS supports multi-touch, so touches is a Set. | |
| // For this game, we only care about single touches, so we get .first | |
| guard let touch = touches.first else { return } | |
| // Convert touch location from view coordinates to scene coordinates | |
| // **Note**: Critical concept! Touches come in UIView coordinates, | |
| // but SpriteKit nodes use scene coordinates. Always convert with location(in:) | |
| let location = touch.location(in: self) | |
| // Get all nodes at the touch location | |
| // **Note**: nodes(at:) returns an array of ALL nodes at that point, | |
| // from top to bottom (highest zPosition first). This is called "hit testing" | |
| let touchedNodes = nodes(at: location) | |
| // Search through touched nodes to find a balloon | |
| // **Note**: We iterate through nodes checking the 'name' property | |
| // This is why we set balloon.name = "balloon" when creating balloons | |
| for node in touchedNodes { | |
| if node.name == "balloon" { | |
| // Found a balloon! Pop it | |
| popBalloon(node) | |
| break // Only pop one balloon per touch (prevents scoring multiple times) | |
| } | |
| } | |
| } | |
| // TODO: TASK 2 - Create a pop animation and remove the balloon | |
| /* Handle a balloon being successfully popped by the player | |
| **Note**: This demonstrates: | |
| - Combining multiple SKActions with group (simultaneous) and sequence (serial) | |
| - Creating satisfying visual feedback for player actions | |
| - The importance of "game feel" - animations make the game more enjoyable | |
| - Parameter balloon: The balloon node that was tapped/popped | |
| */ | |
| func popBalloon(_ balloon: SKNode) { | |
| // Update game state | |
| score += 1 | |
| updateScoreLabel() | |
| // Create visual feedback animations | |
| // **Note**: Good games provide immediate, satisfying feedback for player actions | |
| // A simple scale+fade makes popping feel impactful | |
| // Scale up the balloon as it "pops" | |
| let scaleUp = SKAction.scale(to: 1.5, duration: 0.1) | |
| // Fade out simultaneously | |
| let fadeOut = SKAction.fadeOut(withDuration: 0.1) | |
| // Run both animations at the same time using group | |
| // **Note**: group vs sequence: | |
| // - group: all actions run simultaneously | |
| // - sequence: actions run one after another | |
| let popAnimation = SKAction.group([scaleUp, fadeOut]) | |
| // After the pop animation finishes, remove the balloon from the scene | |
| let remove = SKAction.removeFromParent() | |
| // Sequence: pop animation THEN removal | |
| let sequence = SKAction.sequence([popAnimation, remove]) | |
| // Execute the animation sequence | |
| balloon.run(sequence) | |
| run(SKAction.playSoundFileNamed("pop_sound.mp3", waitForCompletion: false)) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment