Files
Ai/RollkofferSimulator/Nodes/PlayerNode.swift
Claude 9e501cc4e8 Add RollkofferSimulator iOS SpriteKit arcade game
Complete implementation of a 2D top-down arcade collector game where players
control a rolling suitcase through an airport, collecting good dogs and green
people while avoiding bad dogs and gray people.

Features:
- Touch & drag controls for suitcase movement
- Automatic scrolling airport floor with tile pattern
- 4 entity types: good dogs (small/big), bad dogs, green/gray humans
- Spawn system with configurable distribution rates
- Collision detection with visual feedback effects
- Score tracking with high score persistence
- 90-second time limit with 10 dogs + 5 humans goal
- 3 lives system with invincibility frames
- Menu, Game, GameOver, and Victory scenes
- German UI text (Created by Ingo K.)

Technical:
- iOS 15+ with SpriteKit framework
- Modular architecture with Nodes, Managers, Scenes
- Physics-based collision detection
- UserDefaults for score persistence
2025-12-19 16:47:49 +00:00

127 lines
4.3 KiB
Swift

//
// PlayerNode.swift
// RollkofferSimulator
//
// Created by Ingo K.
//
import SpriteKit
/// The player-controlled suitcase node
class PlayerNode: SKNode {
// MARK: - Properties
private let suitcaseBody: SKShapeNode
private let handle: SKShapeNode
private let wheels: [SKShapeNode]
// MARK: - Initialization
override init() {
let size = Constants.suitcaseSize
// Create suitcase body (rounded rectangle)
let bodyRect = CGRect(x: -size.width / 2, y: -size.height / 2 + 10,
width: size.width, height: size.height - 15)
suitcaseBody = SKShapeNode(rect: bodyRect, cornerRadius: 8)
suitcaseBody.fillColor = Constants.Colors.suitcaseColor
suitcaseBody.strokeColor = SKColor(white: 0.2, alpha: 1.0)
suitcaseBody.lineWidth = 2
// Create handle
let handlePath = CGMutablePath()
handlePath.move(to: CGPoint(x: -10, y: size.height / 2 - 5))
handlePath.addLine(to: CGPoint(x: -10, y: size.height / 2 + 15))
handlePath.addLine(to: CGPoint(x: 10, y: size.height / 2 + 15))
handlePath.addLine(to: CGPoint(x: 10, y: size.height / 2 - 5))
handle = SKShapeNode(path: handlePath)
handle.strokeColor = SKColor(white: 0.3, alpha: 1.0)
handle.lineWidth = 4
handle.lineCap = .round
// Create wheels
var tempWheels: [SKShapeNode] = []
let wheelPositions = [
CGPoint(x: -size.width / 2 + 8, y: -size.height / 2 + 5),
CGPoint(x: size.width / 2 - 8, y: -size.height / 2 + 5)
]
for pos in wheelPositions {
let wheel = SKShapeNode(circleOfRadius: 6)
wheel.position = pos
wheel.fillColor = SKColor.darkGray
wheel.strokeColor = SKColor.black
wheel.lineWidth = 1
tempWheels.append(wheel)
}
wheels = tempWheels
super.init()
// Add decorative stripes
let stripe1 = SKShapeNode(rect: CGRect(x: -size.width / 2 + 5, y: 0,
width: size.width - 10, height: 3))
stripe1.fillColor = SKColor(white: 0.3, alpha: 0.5)
stripe1.strokeColor = .clear
let stripe2 = SKShapeNode(rect: CGRect(x: -size.width / 2 + 5, y: -15,
width: size.width - 10, height: 3))
stripe2.fillColor = SKColor(white: 0.3, alpha: 0.5)
stripe2.strokeColor = .clear
addChild(suitcaseBody)
addChild(handle)
addChild(stripe1)
addChild(stripe2)
for wheel in wheels {
addChild(wheel)
}
setupPhysics()
self.zPosition = Constants.ZPosition.player
self.name = "player"
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Physics Setup
private func setupPhysics() {
let size = Constants.suitcaseSize
physicsBody = SKPhysicsBody(rectangleOf: size)
physicsBody?.isDynamic = true
physicsBody?.affectedByGravity = false
physicsBody?.allowsRotation = false
physicsBody?.categoryBitMask = Constants.PhysicsCategory.suitcase
physicsBody?.contactTestBitMask = Constants.PhysicsCategory.all
physicsBody?.collisionBitMask = Constants.PhysicsCategory.none
}
// MARK: - Visual Effects
func startBlinking() {
let fadeOut = SKAction.fadeAlpha(to: 0.3, duration: Constants.blinkDuration)
let fadeIn = SKAction.fadeAlpha(to: 1.0, duration: Constants.blinkDuration)
let blink = SKAction.sequence([fadeOut, fadeIn])
let blinkRepeat = SKAction.repeat(blink, count: Constants.blinkCount)
run(blinkRepeat, withKey: "blink")
}
func stopBlinking() {
removeAction(forKey: "blink")
alpha = 1.0
}
// MARK: - Movement
func constrainToScreen(in frame: CGRect) {
let halfWidth = Constants.suitcaseSize.width / 2
let halfHeight = Constants.suitcaseSize.height / 2
let minX = frame.minX + halfWidth + 10
let maxX = frame.maxX - halfWidth - 10
let minY = frame.minY + halfHeight + 10
let maxY = frame.maxY - halfHeight - 100 // Leave space for UI
position.x = max(minX, min(maxX, position.x))
position.y = max(minY, min(maxY, position.y))
}
}