如何拖动SpriteKit高长精灵时避免其跳转到锚点位置?
解决SpriteKit拖动精灵初始跳转问题
方案一:不修改锚点(推荐)
核心思路是记录触摸点相对于精灵的偏移量,拖动时用触摸位置减去偏移量设置精灵位置,完全规避锚点带来的跳转问题,适配任意锚点设置。
- 在场景类中声明两个变量:
private var touchOffset: CGPoint = .zero private var currentTouchedSprite: SKSpriteNode?
- 实现
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 ) } }
- 实现
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 ) }
- 实现
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阶段完成锚点修改和位置校准,避免初始跳转:
- 声明变量:
private var currentTouchedSprite: SKSpriteNode?
- 在
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 } }
- 在
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) }
- 同样需要实现
touchesEnded和touchesCancelled清除状态。
原方法出现初始跳转的原因
你之前的代码仅在touchesMoved中修改锚点,而初始触摸的touchesBegan阶段未做处理,第一次拖动时精灵仍使用原锚点计算位置,导致视觉跳转。无论采用哪种方案,都必须在触摸开始瞬间完成初始偏移计算或锚点调整。
内容的提问来源于stack exchange,提问作者Dominick
相关产品推荐
相关产品推荐

