How do I drag and drop a sprite in Swift 3.0?

2019-02-09 16:57发布

问题:

All i'm trying to do is be able to drag and drop a sprite across the screen. I've tried the following code:

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
}

override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
    for touch in (touches ) {
        let location = touch.locationInNode(self)
        if ball.containsPoint(location) {
            ball.position = location    
}
}
}

This code does work, however, when I drag the ball quite fast, I guess it detects that the "ball" no longer contains the point "location" and the ball stops, meaning I have pick the ball up again. I want the ball to be able to respond to my touches quickly, so that the ball wont stop moving. How would I do this?

回答1:

I have an implementation where I've subclassed a UIImageView and called it a "DraggableImage"

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        originalPosition = self.center
    }

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        if let touch = touches.first {
            let position = touch.location(in: self.superview)
            self.center = CGPoint(x: position.x, y: position.y)
        }
    }

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {

        self.center = originalPosition
    }


回答2:

This is the correct way to do it in Sprite Kit. Like I said in my comment, you need to assign the moving node to an activate state, in this case I use a variable called movableNode to act is my activate state. When you touch the ball, it becomes activated by assigning it to movableNode. Then as you drag your finger, movableNode will go with the drag, Then once you release, you enter a deactivate state by setting movableNode to nil. Note that this code will only work on single touch applications, if you want to handle multitouch, then you need to track which touch is doing the dragging.

var movableNode : SKNode?

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    if let touch = touches.first {
        let location = touch.locationInNode(self)
        if ball.containsPoint(location) {
            movableNode = ball
            movableNode!.position = location    
        }
    }
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    if let touch = touches.first where movableNode != nil {
        movableNode!.position = touch.locationInNode(self)
    }
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    if let touch = touches.first where movableNode != nil {
        movableNode!.position = touch.locationInNode(self)
        movableNode = nil
    }
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
    if let touch = touches.first {
        movableNode = nil
    }
}


回答3:

KnightOFDragon solution works just fine. I just added few lines if you don't want to move sprite centre position to position where your finger touched the screen and you would like to move sprite from its centre original position.

var movableNode : SKNode?
var ballStartX: CGFloat = 0.0
var ballStartY: CGFloat = 0.0

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    if let touch = touches.first {
        let location = touch.location(in: self)
        if (map?.contains(location))! {
            movableNode = ball
            ballStartX =  (movableNode?.position.x)! - location.x // Location X of your sprite when touch started
            ballStartY =  (movableNode?.position.y)! - location.y // Location Y of your sprite when touch started
        }
    }
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    if let touch = touches.first, movableNode != nil {
        let location = touch.location(in: self)
        movableNode!.position = CGPoint(x: location.x+ballStartX, y: location.y+ballStartY) // Move node around with distance to your finger
    }
}

override  func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    if let _ = touches.first, movableNode != nil {
        movableNode = nil
    }
}
 override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
    movableNode = nil
}