axiom-spritekit-ref について
axiom-spritekit-refは、SpriteKit開発者向けのAPIリファレンスです。ノード、物理、アクション、テクスチャ、シーン、パーティクル、シェーダー、SwiftUI連携を整理し、性能上の注意点やデバッグ機能も扱います。
主な機能
- 01SpriteKitのノード型とプロパティの一覧
- 02物理ボディ、フィールド、ジョイントの解説
- 03アクションと組み合わせ方の一覧
- 04テクスチャ、アトラス、制約の解説
- 05シーン設定とデバッグ表示の説明
- 06パーティクル、シェーダー、SwiftUIの項目
活用シーン
- 01SpriteKitのAPIやプロパティの確認
- 02物理ボディの作成方法の選択
- 03アクションの種類と組み合わせの確認
- 04シーン描画や診断機能の設定
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ charleswiltgen/ axiom --skill axiom-spritekit-refインストール後、Claude Code で/axiom-spritekit-refを入力して読み込まれたか確認します。
詳細
SpriteKit API リファレンス
カテゴリ別にまとめた SpriteKit の完全な API リファレンスです。
このリファレンスの使用場面
次のような場合に、このリファレンスを使用してください。
- 特定の SpriteKit API のシグネチャやプロパティを調べる
- 利用可能なノードの種類と、それぞれのパフォーマンス特性を確認する
- 適切な物理ボディの作成方法を探す
- アクションカタログ全体を確認する
- SKView、スケールモード、トランジションを設定する
- パーティクルエミッターのプロパティを設定する
- SKRenderer または SKShader を使用する
パート 1:ノード階層
すべてのノードタイプ
| ノード | 用途 | バッチ処理 | パフォーマンスに関する注意 |
|---|---|---|---|
SKNode | コンテナ、グループ化 | 該当なし | 描画コストなし |
SKSpriteNode | テクスチャ付きスプライト | はい(同じアトラス) | ゲームプレイで主要なノード |
SKShapeNode | ベクターパス | いいえ | それぞれに 1 回のドローコール — ゲームプレイでは避けてください |
SKLabelNode | テキスト描画 | いいえ | それぞれに 1 回のドローコール |
SKEmitterNode | パーティクルシステム | 該当なし | GPU 負荷が高いため、発生率を制限してください |
SKCameraNode | ビューポート制御 | 該当なし | HUD を子としてアタッチしてください |
SKEffectNode | Core Image フィルター | いいえ | 高コストです — shouldRasterize でキャッシュしてください |
SKCropNode | マスキング | いいえ | マスクとコンテンツで 2 回以上のドローコール |
SKTileMapNode | タイルベースのマップ | はい(同じタイルセット) | 大きなマップに効率的です |
SKVideoNode | 動画再生 | いいえ | AVPlayer を使用します |
SK3DNode | SceneKit コンテンツ | いいえ | SceneKit シーンを描画します |
SKReferenceNode | 再利用可能な .sks ファイル | 該当なし | 実行時にアーカイブを読み込みます |
SKLightNode | ピクセル単位のライティング | 該当なし | 制限:シーンあたり 8 個のライト |
SKFieldNode | 物理フィールド | 該当なし | 重力、電場、磁場など |
SKAudioNode | 位置オーディオ | 該当なし | AVAudioEngine を使用します |
SKTransformNode | 3D 回転用ラッパー | 該当なし | 視点のための xRotation、yRotation |
SKSpriteNode のプロパティ
// Creation
SKSpriteNode(imageNamed: "player") // From asset catalog
SKSpriteNode(texture: texture) // From SKTexture
SKSpriteNode(texture: texture, size: size) // Custom size
SKSpriteNode(color: .red, size: CGSize(width: 50, height: 50)) // Solid color
// Key properties
sprite.anchorPoint = CGPoint(x: 0.5, y: 0) // Bottom-center
sprite.colorBlendFactor = 0.5 // Tint strength (0-1)
sprite.color = .red // Tint color
sprite.normalTexture = normalMap // For lighting
sprite.lightingBitMask = 0x1 // Which lights affect this
sprite.shadowCastBitMask = 0x1 // Which lights cast shadows
sprite.shader = customShader // Per-pixel effects
SKLabelNode のプロパティ
let label = SKLabelNode(text: "Score: 0")
label.fontName = "AvenirNext-Bold"
label.fontSize = 24
label.fontColor = .white
label.horizontalAlignmentMode = .left
label.verticalAlignmentMode = .top
label.numberOfLines = 0 // Multi-line (iOS 11+)
label.preferredMaxLayoutWidth = 200
label.lineBreakMode = .byWordWrapping
パート 2:物理 API
SKPhysicsBody の作成
// Volume bodies (have mass, respond to forces)
SKPhysicsBody(circleOfRadius: 20) // Cheapest
SKPhysicsBody(rectangleOf: CGSize(width: 40, height: 60))
SKPhysicsBody(polygonFrom: path) // Convex only
SKPhysicsBody(texture: texture, size: size) // Pixel-perfect (expensive)
SKPhysicsBody(texture: texture, alphaThreshold: 0.5, size: size)
SKPhysicsBody(bodies: [body1, body2]) // Compound
// Edge bodies (massless boundaries)
SKPhysicsBody(edgeLoopFrom: rect) // Rectangle boundary
SKPhysicsBody(edgeLoopFrom: path) // Path boundary
SKPhysicsBody(edgeFrom: pointA, to: pointB) // Single edge
SKPhysicsBody(edgeChainFrom: path) // Open path
物理ボディのプロパティ
// Identity
body.categoryBitMask = 0x1 // What this body IS
body.collisionBitMask = 0x2 // What it bounces off
body.contactTestBitMask = 0x4 // What triggers didBegin/didEnd
// Physical characteristics
body.mass = 1.0 // kg
body.density = 1.0 // kg/m^2 (auto-calculates mass)
body.friction = 0.2 // 0.0 (ice) to 1.0 (rubber)
body.restitution = 0.3 // 0.0 (no bounce) to 1.0 (perfect bounce)
body.linearDamping = 0.1 // Air resistance (0 = none)
body.angularDamping = 0.1 // Rotational damping
// Behavior
body.isDynamic = true // Responds to forces
body.affectedByGravity = true // Subject to world gravity
body.allowsRotation = true // Can rotate from physics
body.pinned = false // Pinned to parent position
body.usesPreciseCollisionDetection = false // For fast objects
// Motion (read/write)
body.velocity = CGVector(dx: 100, dy: 0)
body.angularVelocity = 0.0
// Force application
body.applyForce(CGVector(dx: 0, dy: 100)) // Continuous
body.applyImpulse(CGVector(dx: 0, dy: 50)) // Instant
body.applyTorque(0.5) // Continuous rotation
body.applyAngularImpulse(1.0) // Instant rotation
body.applyForce(CGVector(dx: 10, dy: 0), at: point) // Force at point
SKPhysicsWorld
scene.physicsWorld.gravity = CGVector(dx: 0, dy: -9.8)
scene.physicsWorld.speed = 1.0 // 0 = paused, 2 = double speed
scene.physicsWorld.contactDelegate = self
// Ray casting
let body = scene.physicsWorld.body(at: point)
let bodyInRect = scene.physicsWorld.body(in: rect)
scene.physicsWorld.enumerateBodies(alongRayStart: start, end: end) { body, point, normal, stop in
// Process each body the ray intersects
}
物理ジョイント
// Pin joint (pivot)
let pin = SKPhysicsJointPin.joint(
withBodyA: bodyA, bodyB: bodyB,
anchor: anchorPoint
)
// Fixed joint (rigid connection)
let fixed = SKPhysicsJointFixed.joint(
withBodyA: bodyA, bodyB: bodyB,
anchor: anchorPoint
)
// Spring joint
let spring = SKPhysicsJointSpring.joint(
withBodyA: bodyA, bodyB: bodyB,
anchorA: pointA, anchorB: pointB
)
spring.frequency = 1.0 // Oscillations per second
spring.damping = 0.5 // 0 = no damping
// Sliding joint (linear constraint)
let slide = SKPhysicsJointSliding.joint(
withBodyA: bodyA, bodyB: bodyB,
anchor: point, axis: CGVector(dx: 1, dy: 0)
)
// Limit joint (distance constraint)
let limit = SKPhysicsJointLimit.joint(
withBodyA: bodyA, bodyB: bodyB,
anchorA: pointA, anchorB: pointB
)
// Add joint to world
scene.physicsWorld.add(joint)
// Remove: scene.physicsWorld.remove(joint)
物理フィールド
// Gravity (directional)
let gravity = SKFieldNode.linearGravityField(withVector: vector_float3(0, -9.8, 0))
// Radial gravity (toward/away from point)
let radial = SKFieldNode.radialGravityField()
radial.strength = 5.0
// Electric field (charge-dependent)
let electric = SKFieldNode.electricField()
// Noise field (turbulence)
let noise = SKFieldNode.noiseField(withSmoothness: 0.5, animationSpeed: 1.0)
// Vortex
let vortex = SKFieldNode.vortexField()
// Drag
let drag = SKFieldNode.dragField()
// All fields share:
field.region = SKRegion(radius: 100) // Area of effect
field.strength = 1.0 // Intensity
field.falloff = 0.0 // Distance falloff
field.minimumRadius = 10 // Inner dead zone
field.isEnabled = true
field.categoryBitMask = 0xFFFFFFFF // Which bodies affected
パート 3:アクションカタログ
移動
SKAction.move(to: point, duration: 1.0)
SKAction.move(by: CGVector(dx: 100, dy: 0), duration: 0.5)
SKAction.moveTo(x: 200, duration: 1.0)
SKAction.moveTo(y: 300, duration: 1.0)
SKAction.moveBy(x: 50, y: 0, duration: 0.5)
SKAction.follow(path, asOffset: true, orientToPath: true, duration: 2.0)
回転
SKAction.rotate(byAngle: .pi, duration: 1.0) // Relative
SKAction.rotate(toAngle: .pi / 2, duration: 0.5) // Absolute
SKAction.rotate(toAngle: angle, duration: 0.5, shortestUnitArc: true)
スケーリング
SKAction.scale(to: 2.0, duration: 0.5)
SKAction.scale(by: 1.5, duration: 0.3)
SKAction.scaleX(to: 2.0, y: 1.0, duration: 0.5)
SKAction.resize(toWidth: 100, height: 50, duration: 0.5)
フェード
SKAction.fadeIn(withDuration: 0.5)
SKAction.fadeOut(withDuration: 0.5)
SKAction.fadeAlpha(to: 0.5, duration: 0.3)
SKAction.fadeAlpha(by: -0.2, duration: 0.3)
合成
SKAction.sequence([action1, action2, action3]) // Sequential
SKAction.group([action1, action2]) // Parallel
SKAction.repeat(action, count: 5) // Finite repeat
SKAction.repeatForever(action) // Infinite
action.reversed() // Reverse
SKAction.wait(forDuration: 1.0) // Delay
SKAction.wait(forDuration: 1.0, withRange: 0.5) // Random delay
テクスチャと色
SKAction.setTexture(texture)
SKAction.setTexture(texture, resize: true)
SKAction.animate(with: [tex1, tex2, tex3], timePerFrame: 0.1)
SKAction.animate(with: textures, timePerFrame: 0.1, resize: false, restore: true)
SKAction.colorize(with: .red, colorBlendFactor: 1.0, duration: 0.5)
SKAction.colorize(withColorBlendFactor: 0, duration: 0.5)
サウンド
SKAction.playSoundFileNamed("explosion.wav", waitForCompletion: false)
ノードツリー
SKAction.removeFromParent()
SKAction.run(block)
SKAction.run(block, queue: .main)
SKAction.customAction(withDuration: 1.0) { node, elapsed in
// Custom per-frame logic
}
物理
SKAction.applyForce(CGVector(dx: 0, dy: 100), duration: 0.5)
SKAction.applyImpulse(CGVector(dx: 50, dy: 0), duration: 1.0/60.0) // ~1 frame
SKAction.applyTorque(0.5, duration: 1.0)
SKAction.changeCharge(to: 1.0, duration: 0.5)
SKAction.changeMass(to: 2.0, duration: 0.5)
タイミングモード
action.timingMode = .linear // Constant speed
action.timingMode = .easeIn // Slow → fast
action.timingMode = .easeOut // Fast → slow
action.timingMode = .easeInEaseOut // Slow → fast → slow
action.speed = 2.0 // 2x speed
パート 4:テクスチャとアトラス
SKTexture
// From image
let tex = SKTexture(imageNamed: "player")
// From atlas
let atlas = SKTextureAtlas(named: "Characters")
let tex = atlas.textureNamed("player_run_1")
// Subrectangle (for manual sprite sheets)
let sub = SKTexture(rect: CGRect(x: 0, y: 0, width: 0.25, height: 0.5), in: sheetTexture)
// From CGImage
let tex = SKTexture(cgImage: cgImage)
// Filtering
tex.filteringMode = .nearest // Pixel art (no smoothing)
tex.filteringMode = .linear // Smooth scaling (default)
// Preload
SKTexture.preload([tex1, tex2]) { /* Ready */ }
SKTextureAtlas
// Create in Xcode: Assets.xcassets → New Sprite Atlas
// Or .atlas folder in project bundle
let atlas = SKTextureAtlas(named: "Characters")
let textureNames = atlas.textureNames // All texture names in atlas
// Preload entire atlas
atlas.preload { /* Atlas ready */ }
// Preload multiple atlases
SKTextureAtlas.preloadTextureAtlases([atlas1, atlas2]) { /* All ready */ }
// Animation from atlas
let frames = (1...8).map { atlas.textureNamed("run_\($0)") }
let animate = SKAction.animate(with: frames, timePerFrame: 0.1)
パート 5:制約
// Orient toward another node
let orient = SKConstraint.orient(to: targetNode, offset: SKRange(constantValue: 0))
// Orient toward a point
let orient = SKConstraint.orient(to: point, offset: SKRange(constantValue: 0))
// Position constraint (keep X in range)
let xRange = SKConstraint.positionX(SKRange(lowerLimit: 0, upperLimit: 400))
// Position constraint (keep Y in range)
let yRange = SKConstraint.positionY(SKRange(lowerLimit: 50, upperLimit: 750))
// Distance constraint (stay within range of node)
let dist = SKConstraint.distance(SKRange(lowerLimit: 50, upperLimit: 200), to: targetNode)
// Rotation constraint
let rot = SKConstraint.zRotation(SKRange(lowerLimit: -.pi/4, upperLimit: .pi/4))
// Apply constraints (processed in order)
node.constraints = [orient, xRange, yRange]
// Toggle
node.constraints?.first?.isEnabled = false
SKRange
SKRange(constantValue: 100) // Exactly 100
SKRange(lowerLimit: 50, upperLimit: 200) // 50...200
SKRange(lowerLimit: 0) // >= 0
SKRange(upperLimit: 500) // <= 500
SKRange(value: 100, variance: 20) // 80...120
パート 6:シーンの設定
SKView の設定
let skView = SKView(frame: view.bounds)
// Debug overlays
skView.showsFPS = true
skView.showsNodeCount = true
skView.showsDrawCount = true
skView.showsPhysics = true
skView.showsFields = true
skView.showsQuadCount = true
// Performance
skView.ignoresSiblingOrder = true // Enables batching optimizations
skView.shouldCullNonVisibleNodes = true // Auto-hide offscreen (manual is faster)
skView.isAsynchronous = true // Default: renders asynchronously
skView.allowsTransparency = false // Opaque is faster
// Frame rate
skView.preferredFramesPerSecond = 60 // Or 120 for ProMotion
// Present scene
skView.presentScene(scene)
skView.presentScene(scene, transition: .fade(withDuration: 0.5))
スケールモードの一覧
| モード | アスペクト比 | コンテンツ | 最適な用途 |
|---|---|---|---|
.aspectFill | 維持 | ビュー全体に表示し、端をクロップ | ほとんどのゲーム |
.aspectFit | 維持 | ビュー内に収め、レターボックスを表示 | 正確なレイアウトが必要な場合 |
.resizeFill | 歪む | 引き伸ばして全体を埋める | ほとんど使いません |
.fill | 変動 | ビューに合わせてシーンをリサイズ | アダプティブなシーン |
SKTransition の種類
SKTransition.fade(withDuration: 0.5)
SKTransition.fade(with: .black, duration: 0.5)
SKTransition.crossFade(withDuration: 0.5)
SKTransition.flipHorizontal(withDuration: 0.5)
SKTransition.flipVertical(withDuration: 0.5)
SKTransition.reveal(with: .left, duration: 0.5)
SKTransition.moveIn(with: .right, duration: 0.5)
SKTransition.push(with: .up, duration: 0.5)
SKTransition.doorway(withDuration: 0.5)
SKTransition.doorsOpenHorizontal(withDuration: 0.5)
SKTransition.doorsOpenVertical(withDuration: 0.5)
SKTransition.doorsCloseHorizontal(withDuration: 0.5)
SKTransition.doorsCloseVertical(withDuration: 0.5)
// Custom with CIFilter:
SKTransition(ciFilter: filter, duration: 0.5)
パート 7:パーティクル
SKEmitterNode の主なプロパティ
let emitter = SKEmitterNode(fileNamed: "Spark")!
// Emission control
emitter.particleBirthRate = 100 // Particles per second
emitter.numParticlesToEmit = 0 // 0 = infinite
emitter.particleLifetime = 2.0 // Seconds
emitter.particleLifetimeRange = 0.5 // ± random
// Position
emitter.particlePosition = .zero
emitter.particlePositionRange = CGVector(dx: 10, dy: 10)
// Movement
emitter.emissionAngle = .pi / 2 // Direction (radians)
emitter.emissionAngleRange = .pi / 4 // Spread
emitter.particleSpeed = 100 // Points per second
emitter.particleSpeedRange = 50 // ± random
emitter.xAcceleration = 0
emitter.yAcceleration = -100 // Gravity-like
// Appearance
emitter.particleTexture = SKTexture(imageNamed: "spark")
emitter.particleSize = CGSize(width: 8, height: 8)
emitter.particleColor = .white
emitter.particleColorAlphaSpeed = -0.5 // Fade out
emitter.particleBlendMode = .add // Additive for fire/glow
emitter.particleAlpha = 1.0
emitter.particleAlphaSpeed = -0.5
// Scale
emitter.particleScale = 1.0
emitter.particleScaleRange = 0.5
emitter.particleScaleSpeed = -0.3 // Shrink over time
// Rotation
emitter.particleRotation = 0
emitter.particleRotationSpeed = 2.0
// Target node (for trails)
emitter.targetNode = scene // Particles stay in world space
// Render order
emitter.particleRenderOrder = .dontCare // .oldestFirst, .oldestLast, .dontCare
// Physics field interaction
emitter.fieldBitMask = 0x1
よく使われるパーティクルプリセット
| エフェクト | 主な設定 |
|---|---|
| 炎 | blendMode: .add、速い alphaSpeed、オレンジから赤への色、上向きの速度 |
| 煙 | blendMode: .alpha、遅い速度、灰色、時間とともにスケールを大きくする |
| 火花 | blendMode: .add、高い速度と範囲、短い寿命、小さいサイズ |
| 雨 | 下向きの emissionAngle、狭い範囲、長い寿命、薄いテクスチャ |
| 雪 | 遅い下向きの速度、広い位置範囲、わずかな x 加速度 |
| 軌跡 | targetNode をシーンに設定し、狭い放出角度、中程度の寿命 |
| 爆発 | 高い発生率、短い numParticlesToEmit、広い速度範囲 |
パート 8:SKRenderer とシェーダー
SKRenderer(Metal との統合)
import MetalKit
let device = MTLCreateSystemDefaultDevice()!
let renderer = SKRenderer(device: device)
renderer.scene = gameScene
renderer.ignoresSiblingOrder = true
// In Metal render loop:
func draw(in view: MTKView) {
guard let commandBuffer = commandQueue.makeCommandBuffer(),
let rpd = view.currentRenderPassDescriptor else { return }
renderer.update(atTime: CACurrentMediaTime())
renderer.render(
withViewport: CGRect(origin: .zero, size: view.drawableSize),
commandBuffer: commandBuffer,
renderPassDescriptor: rpd
)
commandBuffer.present(view.currentDrawable!)
commandBuffer.commit()
}
SKShader(カスタム GLSL ES エフェクト)
// Fragment shader for per-pixel effects
let shader = SKShader(source: """
void main() {
vec4 color = texture2D(u_texture, v_tex_coord);
// Desaturate
float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114));
gl_FragColor = vec4(vec3(gray), color.a) * v_color_mix.a;
}
""")
sprite.shader = shader
// With uniforms
let shader = SKShader(source: """
void main() {
vec4 color = texture2D(u_texture, v_tex_coord);
color.rgb *= u_intensity;
gl_FragColor = color;
}
""")
shader.uniforms = [
SKUniform(name: "u_intensity", float: 0.8)
]
// Built-in uniforms:
// u_texture — sprite texture
// u_time — elapsed time
// u_path_length — shape node path length
// v_tex_coord — texture coordinate
// v_color_mix — color/alpha mix
// SKAttribute for per-node values
パート 7:SwiftUI との統合
SpriteView
import SpriteKit
import SwiftUI
// Basic embedding
struct GameView: View {
var body: some View {
SpriteView(scene: makeScene())
.ignoresSafeArea()
}
func makeScene() -> SKScene {
let scene = GameScene(size: CGSize(width: 1024, height: 768))
scene.scaleMode = .aspectFill
return scene
}
}
// With options
SpriteView(
scene: scene,
transition: .fade(withDuration: 0.5), // Scene transition
isPaused: false, // Pause control
preferredFramesPerSecond: 60, // Frame rate
options: [.ignoresSiblingOrder, .shouldCullNonVisibleNodes],
debugOptions: [.showsFPS, .showsNodeCount] // Debug overlays
)
SpriteView のオプション
| オプション | 目的 |
|---|---|
.ignoresSiblingOrder | 描画順のバッチ処理最適化を有効にします |
.shouldCullNonVisibleNodes | 画面外のノードを自動的に非表示にします |
.allowsTransparency | 透明な背景を許可します (処理が遅くなります) |
デバッグオプション
| オプション | 表示内容 |
|---|---|
.showsFPS | 1 秒あたりのフレーム数 |
.showsNodeCount | 表示中のノードの総数 |
.showsDrawCount | フレームごとの描画呼び出し回数 |
.showsPhysics | 物理ボディの輪郭 |
.showsFields | 物理フィールドの領域 |
.showsQuadCount | クワッドの分割数 |
SwiftUI と SpriteKit 間の通信
// Observable model shared between SwiftUI and scene
@Observable
class GameState {
var score = 0
var isPaused = false
var lives = 3
}
// Scene reads/writes the shared model
class GameScene: SKScene {
var gameState: GameState?
override func update(_ currentTime: TimeInterval) {
guard let state = gameState, !state.isPaused else { return }
// Game logic updates state.score, state.lives, etc.
}
}
// SwiftUI view owns the model
struct GameContainerView: View {
@State private var gameState = GameState()
@State private var scene: GameScene = {
let s = GameScene(size: CGSize(width: 1024, height: 768))
s.scaleMode = .aspectFill
return s
}()
var body: some View {
VStack {
Text("Score: \(gameState.score)")
SpriteView(scene: scene, isPaused: gameState.isPaused)
.ignoresSafeArea()
}
.onAppear { scene.gameState = gameState }
}
}
重要なパターン: @Observable モデルをブリッジとして使用します。シーンがそれを変更し、SwiftUI が変更を監視します。ビューの body 内でシーンを再作成することは避けてください。シーンインスタンスを保持するには @State を使用します。
リソース
WWDC: 2014-608, 2016-610, 2017-609
ドキュメント: /spritekit/skspritenode, /spritekit/skphysicsbody, /spritekit/skaction, /spritekit/skemitternode, /spritekit/skrenderer
スキル: axiom-spritekit, axiom-spritekit-diag
コメント
Markdown 対応