spritekit
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSpriteKit
SpriteKit
Build 2D games and interactive animations for iOS 26+ using SpriteKit and
Swift 6.3. Covers scene lifecycle, node hierarchy, actions, physics, particles,
camera, touch handling, and SwiftUI integration.
使用SpriteKit和Swift 6.3为iOS 26+构建2D游戏与交互式动画。内容涵盖场景生命周期、节点层级、动作、物理系统、粒子特效、相机、触摸处理以及SwiftUI集成。
Contents
目录
Scene Setup
场景设置
SpriteKit renders content through , which presents an -- the
root node of a tree that the framework animates and renders each frame.
SKViewSKSceneSpriteKit通过渲染内容,负责展示——这是一个节点树的根节点,框架会逐帧对其进行动画处理和渲染。
SKViewSKViewSKSceneCreating a Scene
创建场景
Subclass and override lifecycle methods. The coordinate system
origin is at the bottom-left by default.
SKSceneswift
import SpriteKit
final class GameScene: SKScene {
override func didMove(to view: SKView) {
backgroundColor = .darkGray
physicsWorld.contactDelegate = self
physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
setupNodes()
}
override func update(_ currentTime: TimeInterval) {
// Called once per frame before actions are evaluated.
}
}继承并重写生命周期方法。默认情况下,坐标系原点位于左下角。
SKSceneswift
import SpriteKit
final class GameScene: SKScene {
override func didMove(to view: SKView) {
backgroundColor = .darkGray
physicsWorld.contactDelegate = self
physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
setupNodes()
}
override func update(_ currentTime: TimeInterval) {
// 在动作执行前每帧调用一次,用于处理游戏逻辑
}
}Presenting a Scene (UIKit)
在UIKit中展示场景
swift
guard let skView = view as? SKView else { return }
skView.ignoresSiblingOrder = true
let scene = GameScene(size: skView.bounds.size)
scene.scaleMode = .resizeFill
skView.presentScene(scene)swift
guard let skView = view as? SKView else { return }
skView.ignoresSiblingOrder = true
let scene = GameScene(size: skView.bounds.size)
scene.scaleMode = .resizeFill
skView.presentScene(scene)Scale Modes
缩放模式
Use when the scene should adapt to view size changes (rotation,
multitasking). Use for fixed-design game scenes.
letterboxes; stretches and may distort.
.resizeFill.aspectFill.aspectFit.fill当场景需要适配视图尺寸变化(如旋转、多任务分屏)时,使用。固定设计的游戏场景可使用。会添加黑边;会拉伸内容,可能导致变形。
.resizeFill.aspectFill.aspectFit.fillFrame Cycle
帧循环
Each frame follows this order:
- -- game logic
update(_:) - Evaluate actions
- -- post-action logic
didEvaluateActions() - Simulate physics
- -- post-physics adjustments
didSimulatePhysics() - Apply constraints
didApplyConstraints()- -- final adjustments before rendering
didFinishUpdate()
Override only the callbacks where work is needed.
每帧遵循以下顺序:
- —— 处理游戏逻辑
update(_:) - 执行动作
- —— 动作执行后的逻辑处理
didEvaluateActions() - 模拟物理效果
- —— 物理模拟后的调整
didSimulatePhysics() - 应用约束
didApplyConstraints()- —— 渲染前的最终调整
didFinishUpdate()
仅在需要处理工作时重写对应的回调方法。
Nodes and Sprites
节点与精灵
Use (without a visual) as an invisible container or layout group.
Child nodes inherit parent position, scale, rotation, alpha, and speed.
is the primary visual node.
SKNodeSKSpriteNode使用(无视觉效果)作为不可见的容器或布局组。子节点会继承父节点的位置、缩放、旋转、透明度和速度。是主要的视觉节点。
SKNodeSKSpriteNodeCommon Node Types
常见节点类型
| Class | Purpose |
|---|---|
| Textured image or solid color |
| Text rendering |
| Vector paths (expensive per draw call) |
| Particle effects |
| Viewport control |
| Grid-based tiles |
| Positional audio |
| Masking / CIFilter |
| Embedded SceneKit content |
| 类 | 用途 |
|---|---|
| 带纹理的图像或纯色图形 |
| 文本渲染 |
| 矢量路径(每次绘制调用开销较大) |
| 粒子特效 |
| 视口控制 |
| 基于网格的瓦片地图 |
| 3D定位音频 |
| 遮罩 / CIFilter滤镜 |
| 嵌入SceneKit内容 |
Creating Sprites
创建精灵
swift
let player = SKSpriteNode(imageNamed: "hero")
player.position = CGPoint(x: frame.midX, y: frame.midY)
player.name = "player"
addChild(player)swift
let player = SKSpriteNode(imageNamed: "hero")
player.position = CGPoint(x: frame.midX, y: frame.midY)
player.name = "player"
addChild(player)Drawing Order
绘制顺序
Set on for better performance; SpriteKit
then uses to determine order. Without it, nodes draw in tree order.
ignoresSiblingOrder = trueSKViewzPositionswift
background.zPosition = -1
player.zPosition = 0
foregroundUI.zPosition = 10在上设置以提升性能;此时SpriteKit会使用来决定绘制顺序。若不设置,则节点会按照树的顺序进行绘制。
SKViewignoresSiblingOrder = truezPositionswift
background.zPosition = -1
player.zPosition = 0
foregroundUI.zPosition = 10Naming and Searching
节点命名与查找
Assign to find nodes without instance variables. Use ,
, or . Patterns: searches
the entire tree, matches any characters, refers to the parent.
namechildNode(withName:)enumerateChildNodes(withName:using:)subscript//*..swift
player.name = "player"
if let found = childNode(withName: "player") as? SKSpriteNode { /* ... */ }为节点分配,无需实例变量即可查找节点。使用, 或下标进行查找。匹配模式: 搜索整个节点树, 匹配任意字符, 指代父节点。
namechildNode(withName:)enumerateChildNodes(withName:using:)//*..swift
player.name = "player"
if let found = childNode(withName: "player") as? SKSpriteNode { /* ... */ }Actions and Animation
动作与动画
SKActionnode.run(_:)SKActionnode.run(_:)Basic Actions
基础动作
swift
let moveUp = SKAction.moveBy(x: 0, y: 100, duration: 0.5)
let grow = SKAction.scale(to: 1.5, duration: 0.3)
let spin = SKAction.rotate(byAngle: .pi * 2, duration: 1.0)
let fadeOut = SKAction.fadeOut(withDuration: 0.3)
let remove = SKAction.removeFromParent()swift
let moveUp = SKAction.moveBy(x: 0, y: 100, duration: 0.5)
let grow = SKAction.scale(to: 1.5, duration: 0.3)
let spin = SKAction.rotate(byAngle: .pi * 2, duration: 1.0)
let fadeOut = SKAction.fadeOut(withDuration: 0.3)
let remove = SKAction.removeFromParent()Combining Actions
组合动作
swift
// Sequential: run one after another
let dropAndRemove = SKAction.sequence([
SKAction.moveBy(x: 0, y: -500, duration: 1.0),
SKAction.removeFromParent()
])
// Parallel: run simultaneously
let scaleAndFade = SKAction.group([
SKAction.scale(to: 0.0, duration: 0.3),
SKAction.fadeOut(withDuration: 0.3)
])
// Repeat
let pulse = SKAction.repeatForever(
SKAction.sequence([
SKAction.scale(to: 1.2, duration: 0.5),
SKAction.scale(to: 1.0, duration: 0.5)
])
)swift
// 序列动作:按顺序执行
let dropAndRemove = SKAction.sequence([
SKAction.moveBy(x: 0, y: -500, duration: 1.0),
SKAction.removeFromParent()
])
// 组动作:同时执行
let scaleAndFade = SKAction.group([
SKAction.scale(to: 0.0, duration: 0.3),
SKAction.fadeOut(withDuration: 0.3)
])
// 重复动作
let pulse = SKAction.repeatForever(
SKAction.sequence([
SKAction.scale(to: 1.2, duration: 0.5),
SKAction.scale(to: 1.0, duration: 0.5)
])
)Texture Animation
纹理动画
swift
let walkFrames = (1...8).map { SKTexture(imageNamed: "walk_\($0)") }
let walkAction = SKAction.animate(with: walkFrames, timePerFrame: 0.1)
player.run(SKAction.repeatForever(walkAction))Control the speed curve with (, , ,
). Assign keys to actions for later access:
timingMode.linear.easeIn.easeOut.easeInEaseOutswift
let easeIn = SKAction.moveTo(x: 300, duration: 1.0)
easeIn.timingMode = .easeInEaseOut
player.run(pulse, withKey: "pulse")
player.removeAction(forKey: "pulse") // stop laterswift
let walkFrames = (1...8).map { SKTexture(imageNamed: "walk_\($0)") }
let walkAction = SKAction.animate(with: walkFrames, timePerFrame: 0.1)
player.run(SKAction.repeatForever(walkAction))通过控制速度曲线(, , , )。可为动作分配键以便后续访问:
timingMode.linear.easeIn.easeOut.easeInEaseOutswift
let easeIn = SKAction.moveTo(x: 300, duration: 1.0)
easeIn.timingMode = .easeInEaseOut
player.run(pulse, withKey: "pulse")
player.removeAction(forKey: "pulse") // 后续停止动作Physics
物理系统
SpriteKit provides a built-in 2D physics engine. The scene's
manages gravity and collision detection.
physicsWorldSpriteKit内置了2D物理引擎。场景的负责管理重力与碰撞检测。
physicsWorldAdding Physics Bodies
添加物理体
swift
// Circle body
player.physicsBody = SKPhysicsBody(circleOfRadius: player.size.width / 2)
player.physicsBody?.restitution = 0.3
// Static rectangle
ground.physicsBody = SKPhysicsBody(rectangleOf: ground.size)
ground.physicsBody?.isDynamic = false
// Texture-based body for irregular shapes
player.physicsBody = SKPhysicsBody(texture: player.texture!, size: player.size)swift
// 圆形物理体
player.physicsBody = SKPhysicsBody(circleOfRadius: player.size.width / 2)
player.physicsBody?.restitution = 0.3
// 静态矩形物理体
ground.physicsBody = SKPhysicsBody(rectangleOf: ground.size)
ground.physicsBody?.isDynamic = false
// 基于纹理的不规则形状物理体
player.physicsBody = SKPhysicsBody(texture: player.texture!, size: player.size)Category and Contact Masks
类别与碰撞掩码
Use bit masks to control collisions and contact callbacks:
swift
struct PhysicsCategory {
static let player: UInt32 = 0b0001
static let enemy: UInt32 = 0b0010
static let ground: UInt32 = 0b0100
}
player.physicsBody?.categoryBitMask = PhysicsCategory.player
player.physicsBody?.contactTestBitMask = PhysicsCategory.enemy
player.physicsBody?.collisionBitMask = PhysicsCategory.groundcategoryBitMaskcollisionBitMaskcontactTestBitMaskdidBegindidEnd使用位掩码控制碰撞与接触回调:
swift
struct PhysicsCategory {
static let player: UInt32 = 0b0001
static let enemy: UInt32 = 0b0010
static let ground: UInt32 = 0b0100
}
player.physicsBody?.categoryBitMask = PhysicsCategory.player
player.physicsBody?.contactTestBitMask = PhysicsCategory.enemy
player.physicsBody?.collisionBitMask = PhysicsCategory.groundcategoryBitMaskcollisionBitMaskcontactTestBitMaskdidBegindidEndContact Detection
碰撞检测
Implement and set
in :
SKPhysicsContactDelegatephysicsWorld.contactDelegate = selfdidMove(to:)swift
extension GameScene: SKPhysicsContactDelegate {
func didBegin(_ contact: SKPhysicsContact) {
let mask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
if mask == PhysicsCategory.player | PhysicsCategory.enemy {
handlePlayerHit(contact)
}
}
}实现并在中设置:
SKPhysicsContactDelegatedidMove(to:)physicsWorld.contactDelegate = selfswift
extension GameScene: SKPhysicsContactDelegate {
func didBegin(_ contact: SKPhysicsContact) {
let mask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
if mask == PhysicsCategory.player | PhysicsCategory.enemy {
handlePlayerHit(contact)
}
}
}Forces and Impulses
力与冲量
swift
player.physicsBody?.applyForce(CGVector(dx: 0, dy: 50)) // continuous
player.physicsBody?.applyImpulse(CGVector(dx: 0, dy: 200)) // instant
player.physicsBody?.applyAngularImpulse(0.5) // spinUse for jumps and projectile launches. Configure gravity with
and per-body with
.
.applyImpulsephysicsWorld.gravity = CGVector(dx: 0, dy: -9.8)affectedByGravityswift
player.physicsBody?.applyForce(CGVector(dx: 0, dy: 50)) // 持续力
player.physicsBody?.applyImpulse(CGVector(dx: 0, dy: 200)) // 瞬时冲量
player.physicsBody?.applyAngularImpulse(0.5) // 旋转冲量使用处理跳跃和抛射物发射。通过配置全局重力,也可通过为单个物理体设置是否受重力影响。
.applyImpulsephysicsWorld.gravity = CGVector(dx: 0, dy: -9.8)affectedByGravityTouch Handling
触摸处理
SKSceneUIRespondertouchesBegantouchesMovedtouchesEndednodes(at:)swift
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let location = touch.location(in: self)
let tappedNodes = nodes(at: location)
if tappedNodes.contains(where: { $0.name == "playButton" }) {
startGame()
}
}For node-level touch handling, subclass the node and set
. That node then receives touches directly
instead of the scene.
isUserInteractionEnabled = trueSKSceneUIRespondertouchesBegantouchesMovedtouchesEndednodes(at:)swift
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let location = touch.location(in: self)
let tappedNodes = nodes(at: location)
if tappedNodes.contains(where: { $0.name == "playButton" }) {
startGame()
}
}若要在节点层面处理触摸,可继承节点并设置。此时节点会直接接收触摸事件,而非由场景接收。
isUserInteractionEnabled = trueCamera
相机
SKCameraNodescene.cameraswift
let cameraNode = SKCameraNode()
addChild(cameraNode)
camera = cameraNode
cameraNode.position = CGPoint(x: frame.midX, y: frame.midY)SKCameraNodescene.cameraswift
let cameraNode = SKCameraNode()
addChild(cameraNode)
camera = cameraNode
cameraNode.position = CGPoint(x: frame.midX, y: frame.midY)Following a Character
跟随角色
Update the camera position in or use constraints:
didSimulatePhysics()swift
override func didSimulatePhysics() {
cameraNode.position = player.position
}
// Constrain camera to world bounds
let xRange = SKRange(lowerLimit: frame.midX, upperLimit: worldWidth - frame.midX)
let yRange = SKRange(lowerLimit: frame.midY, upperLimit: worldHeight - frame.midY)
cameraNode.constraints = [SKConstraint.positionX(xRange, y: yRange)]在中更新相机位置,或使用约束:
didSimulatePhysics()swift
override func didSimulatePhysics() {
cameraNode.position = player.position
}
// 约束相机在世界范围内
let xRange = SKRange(lowerLimit: frame.midX, upperLimit: worldWidth - frame.midX)
let yRange = SKRange(lowerLimit: frame.midY, upperLimit: worldHeight - frame.midY)
cameraNode.constraints = [SKConstraint.positionX(xRange, y: yRange)]Camera Zoom and HUD
相机缩放与HUD
Scale the camera node inversely: zooms in 2x,
zooms out 2x. Nodes added as children of the camera stay fixed on screen
(HUD elements):
setScale(0.5)setScale(2.0)swift
let scoreLabel = SKLabelNode(text: "Score: 0")
scoreLabel.position = CGPoint(x: 0, y: frame.height / 2 - 40)
scoreLabel.fontName = "AvenirNext-Bold"
scoreLabel.fontSize = 24
cameraNode.addChild(scoreLabel)反向缩放相机节点:表示放大2倍,表示缩小2倍。添加为相机子节点的节点会固定在屏幕上(如HUD元素):
setScale(0.5)setScale(2.0)swift
let scoreLabel = SKLabelNode(text: "Score: 0")
scoreLabel.position = CGPoint(x: 0, y: frame.height / 2 - 40)
scoreLabel.fontName = "AvenirNext-Bold"
scoreLabel.fontSize = 24
cameraNode.addChild(scoreLabel)Particle Effects
粒子特效
SKEmitterNode.sksswift
// Load from file
guard let emitter = SKEmitterNode(fileNamed: "Fire") else { return }
emitter.position = CGPoint(x: frame.midX, y: 100)
addChild(emitter)SKEmitterNode.sksswift
// 从文件加载
guard let emitter = SKEmitterNode(fileNamed: "Fire") else { return }
emitter.position = CGPoint(x: frame.midX, y: 100)
addChild(emitter)One-Shot Emitters
一次性发射器
Set for finite effects and remove after completion:
numParticlesToEmitswift
func spawnExplosion(at position: CGPoint) {
guard let explosion = SKEmitterNode(fileNamed: "Explosion") else { return }
explosion.position = position
explosion.numParticlesToEmit = 100
addChild(explosion)
let wait = SKAction.wait(forDuration: TimeInterval(explosion.particleLifetime))
explosion.run(SKAction.sequence([wait, .removeFromParent()]))
}Set to the scene so particles stay in world space when the
emitter moves: .
targetNodeemitter.targetNode = self设置实现有限次数的特效,并在完成后移除节点:
numParticlesToEmitswift
func spawnExplosion(at position: CGPoint) {
guard let explosion = SKEmitterNode(fileNamed: "Explosion") else { return }
explosion.position = position
explosion.numParticlesToEmit = 100
addChild(explosion)
let wait = SKAction.wait(forDuration: TimeInterval(explosion.particleLifetime))
explosion.run(SKAction.sequence([wait, .removeFromParent()]))
}当发射器移动时,若希望粒子保持在世界空间中,可设置。
targetNode = selfSwiftUI Integration
SwiftUI集成
SpriteViewswift
import SwiftUI
import SpriteKit
struct GameView: View {
@State private var scene: GameScene = {
let s = GameScene()
s.size = CGSize(width: 390, height: 844)
s.scaleMode = .resizeFill
return s
}()
var body: some View {
SpriteView(scene: scene)
.ignoresSafeArea()
}
}SpriteViewswift
import SwiftUI
import SpriteKit
struct GameView: View {
@State private var scene: GameScene = {
let s = GameScene()
s.size = CGSize(width: 390, height: 844)
s.scaleMode = .resizeFill
return s
}()
var body: some View {
SpriteView(scene: scene)
.ignoresSafeArea()
}
}SpriteView Options
SpriteView选项
Pass for transparent backgrounds,
for offscreen culling, or
for -based draw order. Use
during development.
options: [.allowsTransparency].shouldCullNonVisibleNodes.ignoresSiblingOrderzPositiondebugOptions: [.showsFPS, .showsNodeCount]传递实现透明背景,实现屏幕外节点剔除,或启用基于的绘制顺序。开发期间可使用。
options: [.allowsTransparency].shouldCullNonVisibleNodes.ignoresSiblingOrderzPositiondebugOptions: [.showsFPS, .showsNodeCount]Communicating Between SwiftUI and the Scene
SwiftUI与场景间的通信
Pass data through a shared object. Store the scene in
to avoid re-creation on view re-renders:
@Observable@Stateswift
@Observable final class GameState {
var score = 0
var isPaused = false
}
struct GameContainerView: View {
@State private var gameState = GameState()
@State private var scene = GameScene()
var body: some View {
SpriteView(scene: scene, isPaused: gameState.isPaused)
.onAppear { scene.gameState = gameState }
}
}通过共享的对象传递数据。将场景存储在中,避免视图重渲染时重新创建场景:
@Observable@Stateswift
@Observable final class GameState {
var score = 0
var isPaused = false
}
struct GameContainerView: View {
@State private var gameState = GameState()
@State private var scene = GameScene()
var body: some View {
SpriteView(scene: scene, isPaused: gameState.isPaused)
.onAppear { scene.gameState = gameState }
}
}Common Mistakes
常见错误
Creating a new scene on every SwiftUI re-render
SwiftUI每次重渲染时创建新场景
swift
// DON'T: Scene is recreated on every body evaluation
var body: some View {
SpriteView(scene: GameScene(size: CGSize(width: 390, height: 844)))
}
// DO: Create once and reuse
@State private var scene = GameScene(size: CGSize(width: 390, height: 844))
var body: some View {
SpriteView(scene: scene)
}swift
// 错误:每次body计算时都会重新创建场景
var body: some View {
SpriteView(scene: GameScene(size: CGSize(width: 390, height: 844)))
}
// 正确:创建一次并复用
@State private var scene = GameScene(size: CGSize(width: 390, height: 844))
var body: some View {
SpriteView(scene: scene)
}Adding a child node that already has a parent
添加已有父节点的子节点
A node can only have one parent. Remove from the current parent first or
create a separate instance. Adding a node that already has a parent crashes.
一个节点只能有一个父节点。在添加到新父节点前,需先从当前父节点移除,或创建独立实例。添加已有父节点的节点会导致崩溃。
Forgetting to set contactTestBitMask
忘记设置contactTestBitMask
swift
// DON'T: Bodies collide but didBegin is never called
player.physicsBody?.categoryBitMask = PhysicsCategory.player
enemy.physicsBody?.categoryBitMask = PhysicsCategory.enemy
// DO: Set contactTestBitMask to receive contact callbacks
player.physicsBody?.contactTestBitMask = PhysicsCategory.enemyswift
// 错误:物体会碰撞,但didBegin永远不会被调用
player.physicsBody?.categoryBitMask = PhysicsCategory.player
enemy.physicsBody?.categoryBitMask = PhysicsCategory.enemy
// 正确:设置contactTestBitMask以接收碰撞回调
player.physicsBody?.contactTestBitMask = PhysicsCategory.enemyUsing SKShapeNode for performance-critical rendering
在性能关键渲染中使用SKShapeNode
SKShapeNodeSKSpriteNodeSKShapeNodeSKSpriteNodeNot removing nodes that leave the screen
不移除离开屏幕的节点
swift
// DON'T
enemy.run(SKAction.moveBy(x: -800, y: 0, duration: 3.0))
addChild(enemy)
// DO: Remove after leaving the visible area
enemy.run(SKAction.sequence([
SKAction.moveBy(x: -800, y: 0, duration: 3.0),
SKAction.removeFromParent()
]))
addChild(enemy)swift
// 错误
enemy.run(SKAction.moveBy(x: -800, y: 0, duration: 3.0))
addChild(enemy)
// 正确:离开可见区域后移除节点
enemy.run(SKAction.sequence([
SKAction.moveBy(x: -800, y: 0, duration: 3.0),
SKAction.removeFromParent()
]))
addChild(enemy)Setting physicsWorld.contactDelegate too late
过晚设置physicsWorld.contactDelegate
Set in , not in
or after a delay.
physicsWorld.contactDelegate = selfdidMove(to:)update(_:)应在中设置,而非在中或延迟设置。
didMove(to:)physicsWorld.contactDelegate = selfupdate(_:)Review Checklist
检查清单
- Scene subclass overrides for setup, not
didMove(to:)init - chosen appropriately for the game's design
scaleMode - set to
ignoresSiblingOrderontruefor performanceSKView - used consistently when
zPositionis enabledignoresSiblingOrder - Physics set in
contactDelegatedidMove(to:) - Category, collision, and contact bit masks configured correctly
- set for any pair needing
contactTestBitMask/didBegincallbacksdidEnd - Static bodies use
isDynamic = false - avoided in performance-critical paths;
SKShapeNodepreferredSKSpriteNode - Actions that move nodes offscreen include in sequence
.removeFromParent() - One-shot emitters remove themselves after particle lifetime expires
- Emitter set when particles should stay in world space
targetNode - Scene stored in when used with
@Statein SwiftUISpriteView - Texture atlases used for related sprites to reduce draw calls
- uses delta time for frame-rate-independent movement
update(_:) - Nodes removed from parent before being re-added elsewhere
- 场景子类重写进行初始化,而非
didMove(to:)init - 根据游戏设计选择合适的
scaleMode - 在上设置
SKView以提升性能ignoresSiblingOrder = true - 启用后,一致使用
ignoresSiblingOrderzPosition - 在中设置物理系统的
didMove(to:)contactDelegate - 正确配置物理体的类别、碰撞与接触掩码
- 为需要/
didBegin回调的物理体对设置didEndcontactTestBitMask - 静态物理体设置
isDynamic = false - 在性能关键路径中避免使用;优先使用
SKShapeNodeSKSpriteNode - 使节点移出屏幕的动作序列中包含
.removeFromParent() - 一次性发射器在粒子生命周期结束后自动移除
- 当粒子需要保持在世界空间时,设置发射器的
targetNode - 在SwiftUI中使用时,将场景存储在
SpriteView中@State - 为相关精灵使用纹理图集以减少绘制调用
- 使用增量时间实现帧率无关的移动
update(_:) - 节点在重新添加到其他位置前,先从原父节点移除
References
参考资料
- See references/spritekit-patterns.md for tile maps, texture atlases, shaders, scene transitions, game loop patterns, audio, and SceneKit embedding.
- SpriteKit documentation
- SKScene
- SKSpriteNode
- SKAction
- SKPhysicsBody
- SKEmitterNode
- SKCameraNode
- SpriteView
- SKTileMapNode
- 详见references/spritekit-patterns.md,内容涵盖瓦片地图、纹理图集、着色器、场景过渡、游戏循环模式、音频以及SceneKit嵌入。
- SpriteKit官方文档
- SKScene文档
- SKSpriteNode文档
- SKAction文档
- SKPhysicsBody文档
- SKEmitterNode文档
- SKCameraNode文档
- SpriteView文档
- SKTileMapNode文档