agentsclimarketplace

Spritekit ios

Skill ibrews/apple-platform-skills/spritekit-ios

SpriteKit 2D game development for iOS and visionOS. Use when building games with scenes, sprites, physics, tile maps, or particle systems using Apple's SpriteKit framework.From its SKILL.md

Install
npx -y skills add ibrews/apple-platform-skills --skill spritekit-ios

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

5.1 KB, ~1.3k tokens by cl100k_base, as published. Nobody here has run it

SpriteKit iOS Game Development

Scene Graph & Coordinate System

  • Y-up coordinate system — origin (0,0) at bottom-left by default
  • SKScene.anchorPoint defaults to (0.5, 0.5) — center of screen
  • zPosition controls draw order — higher = in front; siblings draw in add order if equal
  • Every node inherits parent's transform — build hierarchy intentionally
class GameScene: SKScene {
    override func didMove(to view: SKView) {
        backgroundColor = .black
        scaleMode = .resizeFill   // or .aspectFit for fixed-resolution games
    }
}

Physics

// Dynamic body (moves with physics)
let sprite = SKSpriteNode(imageNamed: "player")
sprite.physicsBody = SKPhysicsBody(circleOfRadius: sprite.size.width / 2)
sprite.physicsBody?.categoryBitMask    = PhysicsCategory.player   // what am I
sprite.physicsBody?.contactTestBitMask = PhysicsCategory.enemy    // notify on contact
sprite.physicsBody?.collisionBitMask   = PhysicsCategory.wall     // physically bounce off

// Static body (immovable)
let wall = SKSpriteNode(color: .white, size: CGSize(width: 10, height: 300))
wall.physicsBody = SKPhysicsBody(rectangleOf: wall.size)
wall.physicsBody?.isDynamic = false

// Category constants — UInt32, max 32 unique categories (bits 0–31)
struct PhysicsCategory {
    static let none:   UInt32 = 0
    static let player: UInt32 = 0b0001   // 1
    static let enemy:  UInt32 = 0b0010   // 2
    static let wall:   UInt32 = 0b0100   // 4
    // Never exceed bit 31 — UInt32 overflow will crash
}

// Contact delegate
class GameScene: SKScene, SKPhysicsContactDelegate {
    override func didMove(to view: SKView) {
        physicsWorld.contactDelegate = self
    }
    func didBegin(_ contact: SKPhysicsContact) {
        // Order of bodyA/bodyB is not guaranteed — always sort
        let maskA = contact.bodyA.categoryBitMask
        let maskB = contact.bodyB.categoryBitMask
        if (maskA | maskB) == (PhysicsCategory.player | PhysicsCategory.enemy) {
            handlePlayerEnemyContact(contact)
        }
    }
}

Actions

// Sequence — runs in order
let seq = SKAction.sequence([
    SKAction.move(to: target, duration: 0.3),
    SKAction.run { self.spawnEffect() },
    SKAction.removeFromParent()
])

// Group — runs simultaneously
let combo = SKAction.group([
    SKAction.scale(to: 1.5, duration: 0.2),
    SKAction.fadeOut(withDuration: 0.2)
])

// Repeat forever
sprite.run(SKAction.repeatForever(
    SKAction.rotate(byAngle: .pi * 2, duration: 1.0)
))

// Custom timing
let eased = SKAction.move(by: CGVector(dx: 100, dy: 0), duration: 0.5)
eased.timingMode = .easeInEaseOut

Texture Atlases & Animation

// Add a MyCharacter.atlas folder to your Xcode target with numbered frames
let atlas = SKTextureAtlas(named: "MyCharacter")
let frames = (1...8).map { atlas.textureNamed("run_\($0)") }

let anim = SKAction.animate(with: frames, timePerFrame: 0.08, resize: false, restore: true)
sprite.run(SKAction.repeatForever(anim))

Atlases batch all sprites into one draw call — always use them for animated sprites in production.

SKTileMapNode

// Usually configured in the Xcode scene editor; access in code:
if let tileMap = childNode(withName: "Ground") as? SKTileMapNode {
    // Read tile at position
    let col = tileMap.tileColumnIndex(fromPosition: point)
    let row = tileMap.tileRowIndex(fromPosition: point)
    let def = tileMap.tileDefinition(atColumn: col, row: row)
    let isWall = def?.userData?["solid"] as? Bool ?? false
}

Game Loop — What Goes Where

MethodUse for
update(_ currentTime:)Input handling, AI decisions, manual velocity changes
didEvaluateActions()State checks after actions complete (e.g., did animation finish?)
didSimulatePhysics()Camera follow, enforcing constraints after physics step

Performance

  • Node count targets: < 500 for 60 fps on older devices; < 1500 on modern hardware
  • Debug overlay: view.showsFPS = true; view.showsNodeCount = true; view.showsDrawCount = true
  • Prefer SKSpriteNode over SKShapeNode — shape nodes regenerate geometry every frame
  • Avoid SKEffectNode unless you need blur/filters — triggers expensive offscreen render pass
  • Pool and reuse frequently spawned nodes (bullets, particles) instead of add/remove

Common Pitfalls

PitfallFix
presentScene crashMust be called on main thread
Physics bitmask > bit 31UInt32 overflow → crash; cap at 32 categories
removeFromParent() off main threadAll node graph operations: main thread only
Memory leak on scene transitionNil out delegate refs; use view.presentScene(_:transition:)
anchorPoint confusionSet explicitly; docs show (0,0) examples but default SKScene is (0.5, 0.5)
Contact firing twiceBoth didBegin and didEnd fire per pair — guard with a "contacted" flag if needed

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 326,782. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.