Search code examples
swiftsprite-kitspritedraggable

How can I drag and drop a very tall sprite without it jumping to its anchor point?


I'm using the following code to move a very tall, thin sprite around my SpriteKit scene using Swift.

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    super.touchesMoved(touches, with: event)
    let touch = touches.first!
    let moveableNodeTouchLocation = touch.location(in: moveableNode)
    monkeySprite.position = moveableNodeTouchLocation

When I create the monkeySprite I can change the anchor point to CGPoint(x: 1.0, y: 1.0) or 0.5 or 0 and wherever the anchor point is that is where the sprite "jumps" to when dragging for the touches moved event.

Is there a way to move the sprite from the location that it is touched at? I've tried changing the anchor point to the location of the touch as follows:

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    super.touchesMoved(touches, with: event)
    guard let touch = touches.first, let touchedSprite = touchedSprite else { return }
    let location = touch.location(in: self)

    // Calculate the new anchor point based on the touch location and offset
    let newAnchorX = (location.x - touchOffset.x) / touchedSprite.size.width
    let newAnchorY = (location.y - touchOffset.y) / touchedSprite.size.height

    // Set the new anchor point
    touchedSprite.anchorPoint = CGPoint(x: newAnchorX, y: newAnchorY)

    // Adjust the sprite's position to keep it in the same visual location
    touchedSprite.position = location
}

But it didn't change the way the sprite jumps initially to get the previous anchor point to the location of the touch event before dragging smoothly across the screen to follow the touches moved after that. Is there a different way to establish a new anchor point during the initial touch or should I try a different method?


Solution

  • Thanks to bg2b for pointing me in the right direction! The below works:

    private var sprite: SKSpriteNode!
    private var initialTouchOffset: CGPoint = .zero
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    initialTouchOffset = CGPoint(x: location.x - sprite.position.x, y: location.y - sprite.position.y)
     }
    
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touch = touches.first, isDragging else { return }
        let location = touch.location(in: self)
        
        sprite.position = CGPoint(x: location.x - initialTouchOffset.x, y: location.y - initialTouchOffset.y)
    }