axiom-spritekit-ref
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSpriteKit API Reference
SpriteKit API参考
Complete API reference for SpriteKit organized by category.
按类别组织的SpriteKit完整API参考。
When to Use This Reference
何时使用本参考文档
Use this reference when:
- Looking up specific SpriteKit API signatures or properties
- Checking which node types are available and their performance characteristics
- Finding the right physics body creation method
- Browsing the complete action catalog
- Configuring SKView, scale modes, or transitions
- Setting up particle emitter properties
- Working with SKRenderer or SKShader
在以下场景使用本参考文档:
- 查找特定SpriteKit API的签名或属性
- 查看可用的节点类型及其性能特征
- 寻找合适的物理体创建方法
- 浏览完整的动作目录
- 配置SKView、缩放模式或转场效果
- 设置粒子发射器属性
- 使用SKRenderer或SKShader
Part 1: Node Hierarchy
第一部分:节点层级
All Node Types
所有节点类型
| Node | Purpose | Batches? | Performance Notes |
|---|---|---|---|
| Container, grouping | N/A | Zero rendering cost |
| Textured sprites | Yes (same atlas) | Primary gameplay node |
| Vector paths | No | 1 draw call each — avoid in gameplay |
| Text rendering | No | 1 draw call each |
| Particle systems | N/A | GPU-bound, limit birth rate |
| Viewport control | N/A | Attach HUD as children |
| Core Image filters | No | Expensive — cache with |
| Masking | No | Mask + content = 2+ draw calls |
| Tile-based maps | Yes (same tileset) | Efficient for large maps |
| Video playback | No | Uses AVPlayer |
| SceneKit content | No | Renders SceneKit scene |
| Reusable .sks files | N/A | Loads archive at runtime |
| Per-pixel lighting | N/A | Limits: 8 lights per scene |
| Physics fields | N/A | Gravity, electric, magnetic, etc. |
| Positional audio | N/A | Uses AVAudioEngine |
| 3D rotation wrapper | N/A | xRotation, yRotation for perspective |
| 节点 | 用途 | 支持批处理? | 性能说明 |
|---|---|---|---|
| 容器、分组 | N/A | 无渲染开销 |
| 带纹理的精灵 | 是(同一图集) | 主要游戏玩法节点 |
| 矢量路径 | 否 | 每个节点1次绘制调用——游戏玩法中避免使用 |
| 文本渲染 | 否 | 每个节点1次绘制调用 |
| 粒子系统 | N/A | 受GPU限制,限制生成速率 |
| 视口控制 | N/A | 将HUD作为子节点附加 |
| Core Image滤镜 | 否 | 开销大——使用 |
| 遮罩 | 否 | 遮罩+内容=2次以上绘制调用 |
| 瓦片地图 | 是(同一瓦片集) | 大地图高效渲染 |
| 视频播放 | 否 | 使用AVPlayer |
| SceneKit内容 | 否 | 渲染SceneKit场景 |
| 可复用的.sks文件 | N/A | 运行时加载归档文件 |
| 逐像素光照 | N/A | 限制:每个场景最多8盏灯 |
| 物理场 | N/A | 重力、电场、磁场等 |
| 3D位置音频 | N/A | 使用AVAudioEngine |
| 3D旋转包装器 | N/A | xRotation、yRotation用于透视效果 |
SKSpriteNode Properties
SKSpriteNode属性
swift
// 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 effectsswift
// 创建
SKSpriteNode(imageNamed: "player") // 从资源目录
SKSpriteNode(texture: texture) // 从SKTexture
SKSpriteNode(texture: texture, size: size) // 自定义尺寸
SKSpriteNode(color: .red, size: CGSize(width: 50, height: 50)) // 纯色
// 关键属性
sprite.anchorPoint = CGPoint(x: 0.5, y: 0) // 底部中心
sprite.colorBlendFactor = 0.5 // 着色强度(0-1)
sprite.color = .red // 着色颜色
sprite.normalTexture = normalMap // 用于光照
sprite.lightingBitMask = 0x1 // 受哪些灯光影响
sprite.shadowCastBitMask = 0x1 // 对哪些灯光投射阴影
sprite.shader = customShader // 逐像素效果SKLabelNode Properties
SKLabelNode属性
swift
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 = .byWordWrappingswift
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 // 多行(iOS 11+)
label.preferredMaxLayoutWidth = 200
label.lineBreakMode = .byWordWrappingPart 2: Physics API
第二部分:物理API
SKPhysicsBody Creation
SKPhysicsBody创建
swift
// 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 pathswift
// 体积物理体(有质量,响应力)
SKPhysicsBody(circleOfRadius: 20) // 开销最低
SKPhysicsBody(rectangleOf: CGSize(width: 40, height: 60))
SKPhysicsBody(polygonFrom: path) // 仅支持凸多边形
SKPhysicsBody(texture: texture, size: size) // 像素级精确(开销大)
SKPhysicsBody(texture: texture, alphaThreshold: 0.5, size: size)
SKPhysicsBody(bodies: [body1, body2]) // 复合物理体
// 边缘物理体(无质量边界)
SKPhysicsBody(edgeLoopFrom: rect) // 矩形边界
SKPhysicsBody(edgeLoopFrom: path) // 路径边界
SKPhysicsBody(edgeFrom: pointA, to: pointB) // 单条边缘
SKPhysicsBody(edgeChainFrom: path) // 开放路径Physics Body Properties
物理体属性
swift
// 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 pointswift
// 标识
body.categoryBitMask = 0x1 // 该物理体的类别
body.collisionBitMask = 0x2 // 会与之碰撞的类别
body.contactTestBitMask = 0x4 // 会触发didBegin/didEnd的类别
// 物理特性
body.mass = 1.0 // 千克
body.density = 1.0 // 千克/平方米(自动计算质量)
body.friction = 0.2 // 0.0(冰)到1.0(橡胶)
body.restitution = 0.3 // 0.0(无反弹)到1.0(完美反弹)
body.linearDamping = 0.1 // 空气阻力(0=无)
body.angularDamping = 0.1 // 旋转阻尼
// 行为
body.isDynamic = true // 响应力
body.affectedByGravity = true // 受世界重力影响
body.allowsRotation = true // 可因物理效果旋转
body.pinned = false // 是否固定在父节点位置
body.usesPreciseCollisionDetection = false // 用于快速移动的物体
// 运动(可读可写)
body.velocity = CGVector(dx: 100, dy: 0)
body.angularVelocity = 0.0
// 施加力
body.applyForce(CGVector(dx: 0, dy: 100)) // 持续力
body.applyImpulse(CGVector(dx: 0, dy: 50)) // 瞬时冲量
body.applyTorque(0.5) // 持续旋转力
body.applyAngularImpulse(1.0) // 瞬时旋转冲量
body.applyForce(CGVector(dx: 10, dy: 0), at: point) // 在指定点施加力SKPhysicsWorld
SKPhysicsWorld
swift
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
}swift
scene.physicsWorld.gravity = CGVector(dx: 0, dy: -9.8)
scene.physicsWorld.speed = 1.0 // 0=暂停,2=两倍速度
scene.physicsWorld.contactDelegate = self
// 射线检测
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
// 处理射线相交的每个物理体
}Physics Joints
物理关节
swift
// 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)swift
// 销钉关节(枢轴)
let pin = SKPhysicsJointPin.joint(
withBodyA: bodyA, bodyB: bodyB,
anchor: anchorPoint
)
// 固定关节(刚性连接)
let fixed = SKPhysicsJointFixed.joint(
withBodyA: bodyA, bodyB: bodyB,
anchor: anchorPoint
)
// 弹簧关节
let spring = SKPhysicsJointSpring.joint(
withBodyA: bodyA, bodyB: bodyB,
anchorA: pointA, anchorB: pointB
)
spring.frequency = 1.0 // 每秒振荡次数
spring.damping = 0.5 // 0=无阻尼
// 滑动关节(线性约束)
let slide = SKPhysicsJointSliding.joint(
withBodyA: bodyA, bodyB: bodyB,
anchor: point, axis: CGVector(dx: 1, dy: 0)
)
// 限制关节(距离约束)
let limit = SKPhysicsJointLimit.joint(
withBodyA: bodyA, bodyB: bodyB,
anchorA: pointA, anchorB: pointB
)
// 将关节添加到物理世界
scene.physicsWorld.add(joint)
// 移除:scene.physicsWorld.remove(joint)Physics Fields
物理场
swift
// 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 affectedswift
// 重力(方向型)
let gravity = SKFieldNode.linearGravityField(withVector: vector_float3(0, -9.8, 0))
// 径向重力(朝向/远离某点)
let radial = SKFieldNode.radialGravityField()
radial.strength = 5.0
// 电场(依赖电荷)
let electric = SKFieldNode.electricField()
// 噪声场(湍流)
let noise = SKFieldNode.noiseField(withSmoothness: 0.5, animationSpeed: 1.0)
// 漩涡
let vortex = SKFieldNode.vortexField()
// 阻力
let drag = SKFieldNode.dragField()
// 所有场共享以下属性:
field.region = SKRegion(radius: 100) // 作用区域
field.strength = 1.0 // 强度
field.falloff = 0.0 // 距离衰减
field.minimumRadius = 10 // 内部盲区
field.isEnabled = true
field.categoryBitMask = 0xFFFFFFFF // 影响哪些物理体Part 3: Action Catalog
第三部分:动作目录
Movement
移动
swift
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)swift
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)Rotation
旋转
swift
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)swift
SKAction.rotate(byAngle: .pi, duration: 1.0) // 相对旋转
SKAction.rotate(toAngle: .pi / 2, duration: 0.5) // 绝对旋转
SKAction.rotate(toAngle: angle, duration: 0.5, shortestUnitArc: true)Scaling
缩放
swift
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)swift
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)Fading
淡入淡出
swift
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)swift
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)Composition
组合动作
swift
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 delayswift
SKAction.sequence([action1, action2, action3]) // 顺序执行
SKAction.group([action1, action2]) // 并行执行
SKAction.repeat(action, count: 5) // 有限次重复
SKAction.repeatForever(action) // 无限重复
action.reversed() // 反向执行
SKAction.wait(forDuration: 1.0) // 延迟
SKAction.wait(forDuration: 1.0, withRange: 0.5) // 随机延迟Texture & Color
纹理与颜色
swift
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)swift
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)Sound
声音
swift
SKAction.playSoundFileNamed("explosion.wav", waitForCompletion: false)swift
SKAction.playSoundFileNamed("explosion.wav", waitForCompletion: false)Node Tree
节点树
swift
SKAction.removeFromParent()
SKAction.run(block)
SKAction.run(block, queue: .main)
SKAction.customAction(withDuration: 1.0) { node, elapsed in
// Custom per-frame logic
}swift
SKAction.removeFromParent()
SKAction.run(block)
SKAction.run(block, queue: .main)
SKAction.customAction(withDuration: 1.0) { node, elapsed in
// 自定义每帧逻辑
}Physics
物理相关动作
swift
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)swift
SKAction.applyForce(CGVector(dx: 0, dy: 100), duration: 0.5)
SKAction.applyImpulse(CGVector(dx: 50, dy: 0), duration: 1.0/60.0) // ~1帧
SKAction.applyTorque(0.5, duration: 1.0)
SKAction.changeCharge(to: 1.0, duration: 0.5)
SKAction.changeMass(to: 2.0, duration: 0.5)Timing Modes
时间模式
swift
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 speedswift
action.timingMode = .linear // 匀速
action.timingMode = .easeIn // 慢→快
action.timingMode = .easeOut // 快→慢
action.timingMode = .easeInEaseOut // 慢→快→慢
action.speed = 2.0 // 2倍速度Part 4: Textures and Atlases
第四部分:纹理与图集
SKTexture
SKTexture
swift
// 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 */ }swift
// 从图片
let tex = SKTexture(imageNamed: "player")
// 从图集
let atlas = SKTextureAtlas(named: "Characters")
let tex = atlas.textureNamed("player_run_1")
// 子矩形(用于手动精灵表)
let sub = SKTexture(rect: CGRect(x: 0, y: 0, width: 0.25, height: 0.5), in: sheetTexture)
// 从CGImage
let tex = SKTexture(cgImage: cgImage)
// 过滤
tex.filteringMode = .nearest // 像素风格(无平滑)
tex.filteringMode = .linear // 平滑缩放(默认)
// 预加载
SKTexture.preload([tex1, tex2]) { /* 准备就绪 */ }SKTextureAtlas
SKTextureAtlas
swift
// 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)swift
// 在Xcode中创建:Assets.xcassets → 新建Sprite图集
// 或项目包中的.atlas文件夹
let atlas = SKTextureAtlas(named: "Characters")
let textureNames = atlas.textureNames // 图集中的所有纹理名称
// 预加载整个图集
atlas.preload { /* 图集准备就绪 */ }
// 预加载多个图集
SKTextureAtlas.preloadTextureAtlases([atlas1, atlas2]) { /* 全部就绪 */ }
// 从图集创建动画
let frames = (1...8).map { atlas.textureNamed("run_\($0)") }
let animate = SKAction.animate(with: frames, timePerFrame: 0.1)Part 5: Constraints
第五部分:约束
swift
// 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 = falseswift
// 朝向另一个节点
let orient = SKConstraint.orient(to: targetNode, offset: SKRange(constantValue: 0))
// 朝向某个点
let orient = SKConstraint.orient(to: point, offset: SKRange(constantValue: 0))
// 位置约束(保持X在范围内)
let xRange = SKConstraint.positionX(SKRange(lowerLimit: 0, upperLimit: 400))
// 位置约束(保持Y在范围内)
let yRange = SKConstraint.positionY(SKRange(lowerLimit: 50, upperLimit: 750))
// 距离约束(与节点保持在范围内)
let dist = SKConstraint.distance(SKRange(lowerLimit: 50, upperLimit: 200), to: targetNode)
// 旋转约束
let rot = SKConstraint.zRotation(SKRange(lowerLimit: -.pi/4, upperLimit: .pi/4))
// 应用约束(按顺序处理)
node.constraints = [orient, xRange, yRange]
// 启用/禁用
node.constraints?.first?.isEnabled = falseSKRange
SKRange
swift
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...120swift
SKRange(constantValue: 100) // 精确值100
SKRange(lowerLimit: 50, upperLimit: 200) // 50到200
SKRange(lowerLimit: 0) // 大于等于0
SKRange(upperLimit: 500) // 小于等于500
SKRange(value: 100, variance: 20) // 80到120Part 6: Scene Setup
第六部分:场景设置
SKView Configuration
SKView配置
swift
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))swift
let skView = SKView(frame: view.bounds)
// 调试叠加层
skView.showsFPS = true
skView.showsNodeCount = true
skView.showsDrawCount = true
skView.showsPhysics = true
skView.showsFields = true
skView.showsQuadCount = true
// 性能
skView.ignoresSiblingOrder = true // 启用批处理优化
skView.shouldCullNonVisibleNodes = true // 自动隐藏屏幕外节点(手动处理更快)
skView.isAsynchronous = true // 默认:异步渲染
skView.allowsTransparency = false // 不透明模式更快
// 帧率
skView.preferredFramesPerSecond = 60 // 或120适配ProMotion
// 呈现场景
skView.presentScene(scene)
skView.presentScene(scene, transition: .fade(withDuration: 0.5))Scale Mode Matrix
缩放模式矩阵
| Mode | Aspect Ratio | Content | Best For |
|---|---|---|---|
| Preserved | Fills view, crops edges | Most games |
| Preserved | Fits in view, letterboxes | Exact layout needed |
| Distorted | Stretches to fill | Almost never |
| Varies | Scene resizes to match view | Adaptive scenes |
| 模式 | 宽高比 | 内容表现 | 最佳适用场景 |
|---|---|---|---|
| 保持 | 填充视图,裁剪边缘 | 大多数游戏 |
| 保持 | 适配视图,留黑边 | 需要精确布局的场景 |
| 扭曲 | 拉伸填充 | 几乎不使用 |
| 变化 | 场景调整大小以匹配视图 | 自适应场景 |
SKTransition Types
SK转场类型
swift
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)swift
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)
// 使用CIFilter自定义:
SKTransition(ciFilter: filter, duration: 0.5)Part 7: Particles
第七部分:粒子系统
SKEmitterNode Key Properties
SKEmitterNode关键属性
swift
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 = 0x1swift
let emitter = SKEmitterNode(fileNamed: "Spark")!
// 发射控制
emitter.particleBirthRate = 100 // 每秒发射粒子数
emitter.numParticlesToEmit = 0 // 0=无限发射
emitter.particleLifetime = 2.0 // 粒子生命周期(秒)
emitter.particleLifetimeRange = 0.5 // 随机波动范围±
// 位置
emitter.particlePosition = .zero
emitter.particlePositionRange = CGVector(dx: 10, dy: 10)
// 运动
emitter.emissionAngle = .pi / 2 // 发射方向(弧度)
emitter.emissionAngleRange = .pi / 4 // 发射扩散角度
emitter.particleSpeed = 100 // 粒子速度(点/秒)
emitter.particleSpeedRange = 50 // 速度随机波动范围±
emitter.xAcceleration = 0
emitter.yAcceleration = -100 // 类重力加速度
// 外观
emitter.particleTexture = SKTexture(imageNamed: "spark")
emitter.particleSize = CGSize(width: 8, height: 8)
emitter.particleColor = .white
emitter.particleColorAlphaSpeed = -0.5 // 淡出速度
emitter.particleBlendMode = .add // 叠加混合(用于火焰/发光效果)
emitter.particleAlpha = 1.0
emitter.particleAlphaSpeed = -0.5
// 缩放
emitter.particleScale = 1.0
emitter.particleScaleRange = 0.5
emitter.particleScaleSpeed = -0.3 // 随时间缩小
// 旋转
emitter.particleRotation = 0
emitter.particleRotationSpeed = 2.0
// 目标节点(用于轨迹)
emitter.targetNode = scene // 粒子保留在世界空间
// 渲染顺序
emitter.particleRenderOrder = .dontCare // .oldestFirst, .oldestLast, .dontCare
// 物理场交互
emitter.fieldBitMask = 0x1Common Particle Presets
常见粒子预设
| Effect | Key Settings |
|---|---|
| Fire | |
| Smoke | |
| Sparks | |
| Rain | Downward |
| Snow | Slow downward speed, wide position range, slight x acceleration |
| Trail | Set |
| Explosion | High birth rate, short |
| 效果 | 关键设置 |
|---|---|
| 火焰 | |
| 烟雾 | |
| 火花 | |
| 雨 | 向下 |
| 雪 | 低速下落,宽位置范围,轻微x方向加速度 |
| 轨迹 | 设置 |
| 爆炸 | 高发射速率, |
Part 8: SKRenderer and Shaders
第八部分:SKRenderer与着色器
SKRenderer (Metal Integration)
SKRenderer(Metal集成)
swift
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()
}swift
import MetalKit
let device = MTLCreateSystemDefaultDevice()!
let renderer = SKRenderer(device: device)
renderer.scene = gameScene
renderer.ignoresSiblingOrder = true
// 在Metal渲染循环中:
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 (Custom GLSL ES Effects)
SKShader(自定义GLSL ES效果)
swift
// 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 valuesswift
// 用于逐像素效果的片段着色器
let shader = SKShader(source: """
void main() {
vec4 color = texture2D(u_texture, v_tex_coord);
// 去饱和
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
// 带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)
]
// 内置Uniforms:
// u_texture — 精灵纹理
// u_time — 已流逝时间
// u_path_length — 形状节点路径长度
// v_tex_coord — 纹理坐标
// v_color_mix — 颜色/alpha混合
// SKAttribute用于每个节点的自定义值Resources
资源
WWDC: 2014-608, 2016-610, 2017-609
Docs: /spritekit/skspritenode, /spritekit/skphysicsbody, /spritekit/skaction, /spritekit/skemitternode, /spritekit/skrenderer
Skills: axiom-spritekit, axiom-spritekit-diag
WWDC: 2014-608, 2016-610, 2017-609
文档: /spritekit/skspritenode, /spritekit/skphysicsbody, /spritekit/skaction, /spritekit/skemitternode, /spritekit/skrenderer
技能: axiom-spritekit, axiom-spritekit-diag