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

如何拖动SpriteKit高长精灵时避免其跳转到锚点位置?

解决SpriteKit拖动精灵初始跳转问题

方案一:不修改锚点(推荐)

核心思路是记录触摸点相对于精灵的偏移量,拖动时用触摸位置减去偏移量设置精灵位置,完全规避锚点带来的跳转问题,适配任意锚点设置。

  1. 在场景类中声明两个变量:
private var touchOffset: CGPoint = .zero
private var currentTouchedSprite: SKSpriteNode?
  1. 实现touchesBegan方法,检测触摸并计算偏移量:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    super.touchesBegan(touches, with: event)
    guard let touch = touches.first else { return }
    let touchLocation = touch.location(in: self)
    
    // 检测是否点击到目标精灵(替换为你的monkeySprite)
    if let sprite = nodes(at: touchLocation).first as? SKSpriteNode, sprite == monkeySprite {
        currentTouchedSprite = sprite
        // 计算触摸点相对于精灵锚点的偏移值
        let touchInSprite = touch.location(in: sprite)
        touchOffset = CGPoint(
            x: touchInSprite.x - sprite.anchorPoint.x * sprite.size.width,
            y: touchInSprite.y - sprite.anchorPoint.y * sprite.size.height
        )
    }
}
  1. 实现touchesMoved方法,拖动时更新精灵位置:
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    super.touchesMoved(touches, with: event)
    guard let touch = touches.first, let sprite = currentTouchedSprite else { return }
    let touchLocation = touch.location(in: self)
    
    // 用触摸位置减去偏移量,保证触摸点始终对应精灵上的初始触摸位置
    sprite.position = CGPoint(
        x: touchLocation.x - touchOffset.x,
        y: touchLocation.y - touchOffset.y
    )
}
  1. 实现touchesEnded和touchesCancelled清除触摸状态:
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    super.touchesEnded(touches, with: event)
    currentTouchedSprite = nil
}

override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
    super.touchesCancelled(touches, with: event)
    currentTouchedSprite = nil
}

方案二:修改锚点(适合必须调整锚点的场景)

如果一定要通过修改锚点实现拖动,必须在touchesBegan阶段完成锚点修改和位置校准,避免初始跳转:

  1. 声明变量:
private var currentTouchedSprite: SKSpriteNode?
  1. 在touchesBegan中修改锚点并校准位置:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    super.touchesBegan(touches, with: event)
    guard let touch = touches.first else { return }
    let touchLocation = touch.location(in: self)
    
    if let sprite = nodes(at: touchLocation).first as? SKSpriteNode, sprite == monkeySprite {
        currentTouchedSprite = sprite
        // 计算触摸点对应的新锚点(基于精灵尺寸的比例)
        let touchInSprite = touch.location(in: sprite)
        let newAnchorX = touchInSprite.x / sprite.size.width
        let newAnchorY = touchInSprite.y / sprite.size.height
        
        // 保存精灵当前视觉位置,修改锚点后重新设置位置,避免视觉跳转
        let visualPosition = sprite.position
        sprite.anchorPoint = CGPoint(x: newAnchorX, y: newAnchorY)
        sprite.position = visualPosition
    }
}
  1. 在touchesMoved中直接将精灵位置设置为触摸位置:
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    super.touchesMoved(touches, with: event)
    guard let touch = touches.first, let sprite = currentTouchedSprite else { return }
    sprite.position = touch.location(in: self)
}
  1. 同样需要实现touchesEnded和touchesCancelled清除状态。

原方法出现初始跳转的原因

你之前的代码仅在touchesMoved中修改锚点,而初始触摸的touchesBegan阶段未做处理,第一次拖动时精灵仍使用原锚点计算位置,导致视觉跳转。无论采用哪种方案,都必须在触摸开始瞬间完成初始偏移计算或锚点调整。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 21:30:33