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

iOS 11.3下Swift游戏didBegin函数异常:碰撞计分多次累加问题

解决iOS 11.3后SpriteKit碰撞触发多次didBegin的问题

这个问题我之前帮好几个开发者排查过——iOS 11.3确实对SpriteKit的碰撞检测逻辑做了调整,导致你移除碰撞节点后,didBegin(_:)还是会被多次调用,尤其是当节点移动速度快的时候,缓存的碰撞事件会持续触发,直接导致分数疯狂上涨。

给你两个靠谱的解决思路,亲测有效:

方法一:给节点添加“已处理”标记

给你的子弹和敌人节点加个自定义属性,标记它们是否已经被处理过,这样重复触发的碰撞事件会直接跳过:

首先创建一个自定义的Sprite类(或者给SKSpriteNode加扩展):

class GameNode: SKSpriteNode {
    var hasBeenProcessed = false // 标记是否已经处理过碰撞
}

然后在didBegin里先检查标记,再执行逻辑:

func didBegin(_ contact: SKPhysicsContact) {
    // 确保两个节点都是我们的自定义GameNode
    guard let bullet = contact.bodyA.node as? GameNode,
          let enemy = contact.bodyB.node as? GameNode else {
        return
    }
    
    // 如果任意一个节点已经被处理过,直接返回
    if bullet.hasBeenProcessed || enemy.hasBeenProcessed {
        return
    }
    
    // 先标记为已处理,避免重复触发
    bullet.hasBeenProcessed = true
    enemy.hasBeenProcessed = true
    
    // 执行移除和加分逻辑
    bullet.removeFromParent()
    enemy.removeFromParent()
    gameScore += 1
}

方法二:立即禁用碰撞体

另一种思路是在处理碰撞的第一时间,把两个节点的物理体禁用,这样后续就不会再触发碰撞检测了:

func didBegin(_ contact: SKPhysicsContact) {
    let node1 = contact.bodyA.node
    let node2 = contact.bodyB.node
    
    // 先禁用物理体的动态性,阻止后续碰撞检测
    node1?.physicsBody?.isDynamic = false
    node2?.physicsBody?.isDynamic = false
    
    // 移除节点并加分
    node1?.removeFromParent()
    node2?.removeFromParent()
    gameScore += 1
}

为什么会出现这个问题?

iOS 11.3之后,SpriteKit的碰撞事件会被缓存几帧,哪怕你在当前帧移除了节点,已经进入队列的碰撞事件还是会在后续帧触发didBegin。节点移动速度越快,缓存的碰撞事件就越多,分数上涨的数值也就越大——这完全符合你描述的现象。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 07:47:19