You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SpriteKit中SKAction如何不调用setter修改节点属性?

SKAction移动动作为何不触发SKNode的position属性didSet观察器?

SpriteKit官方文档明确提到:动作通常不会调用节点的公开方法。比如子类化SKSpriteNode并给position属性添加didSet观察器,用来响应move(to:duration:)动作时,观察器并不会触发。

示例代码:

class MovingNode: SKSpriteNode {
    override var position: CGPoint {
        didSet {
            // 这里的代码不会在move动作执行时触发
        }
    }
}

产生的困惑点:

  • 如果不通过someNode.position = newPosition这种调用setter的方式,节点的位置是怎么改变的?
  • 为什么每帧打印position能看到数值变化,却完全绕过了didSet观察器?
  • SKAction的底层实现逻辑到底是什么?

底层实现原理

1. SKAction直接操作底层存储,绕过属性setter

SpriteKit的核心底层基于Objective-C和C++实现,为了保证高性能(尤其是在每帧更新大量节点的场景下),SKAction不会通过调用Objective-C的属性setter方法来修改节点属性。

以position为例,在OC中position是一个@property,但它对应着一个私有实例变量(比如_position)。SKAction在执行时,会直接修改这个私有实例变量的值,完全跳过了setter方法的调用流程。

2. Swift属性观察器依赖setter触发

Swift的didSet/willSet观察器是Swift语言层面的特性,它的触发必须依赖通过Swift的属性setter来修改值。当底层OC代码直接修改私有实例变量时,Swift的属性访问机制完全没被触发,自然不会调用didSet。

而我们读取position时能看到变化,是因为读取操作走的是属性getter方法,getter会直接返回底层实例变量的当前值,所以能拿到最新的位置数据。

3. 性能优先的设计选择

SpriteKit作为游戏框架,性能是核心考量点。Objective-C的消息发送(包括调用setter方法)本身存在一定开销,如果每个SKAction都通过setter来修改属性,在大量节点同时执行动作的场景下,会产生显著的性能损耗。直接操作底层存储能把这部分开销降到最低,保证游戏的流畅运行。


替代监听方案

如果需要响应SKAction带来的位置变化,可以考虑以下几种方式:

  • 在每帧调用的update(_:)方法中,手动对比当前position和上一帧的position,自行处理变化逻辑
  • 使用SKAction的completion闭包,在动作结束时执行相关逻辑(仅适用于动作结束的场景)
  • 利用Objective-C的KVO机制监听position属性(KVO能监测到底层实例变量的变化,不过在Swift中需要正确处理KVO的相关操作)

内容的提问来源于stack exchange,提问作者rayaantaneja

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 14:42:36