0
votes

I'm looking for help on a problem I've been stuck on for a while but cannot find an answer to.

I have a SpriteKit kids game app in which the user can drag objects (SKspriteNodes) around the screen. This is controlled in the "touches" events like so:

    override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
    super.touchesBegan(touches, withEvent: event)

    let touch = touches.first as! UITouch
    let touchLocation = touch.locationInNode(self)

    if piece01.containsPoint(touchLocation) && piece01Placed == false && aPieceIsBeingGrabbed == false {
        piece01Grabbed = true
        aPieceIsBeingGrabbed = true
        self.piece01.zPosition = 4.0
        }
    }

   override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
    super.touchesMoved(touches, withEvent: event)

    let touch = touches.first as! UITouch
    let touchLocation = touch.locationInNode(self)

    if piece01Grabbed && piece01Placed == false {
        self.piece01.position = CGPointMake(touchLocation.x + worldScale * 120, touchLocation.y - worldScale * 80)

    }
}

My problem is that if the user touches another part of the screen while dragging a sprite, a glitch occurs and the Sprite bounces back and forth between the two touch locations and can't decide which one to stay with.

Does anyone know how I can prevent this so that the Sprite always follows the touch that it was initially grabbed by? Thanks in advance!

2
I can't provide you with a full answer at the moment, however I'd like to point you in the right direction. Start by iterating over all of the touches in the set i.e. for touch in touches { let thisTouch = touch as! UITouch;}. UITouch objects are persistent with multi-touch sequences. You can read about UITouch objects more here - Mason
Thanks alot MaceDev, I'll look into it now. - jwade502

2 Answers

1
votes

MaceDevs above answer is correct based on my question and works with the touchesMoved event like how I initially asked. However, I discovered that it is easier to use a gesture recognizer to handle dragging events.

So to solve this, I ended up scrapping my "touches" methods and implemented a UIPanGestureRecognizer instead.

All you have to do is set up a UIPanGestureRecognizer in didMoveToView and set its maximum number of touches to 1. If you do not set this to 1, then any additional touches will move the piece in an average direction of the touches.

//Declare "draggingPiece" variable at top of swift file
var draggingPiece: UIPanGestureRecognizer!

//...init functions...

override func didMoveToView(view: SKView) {
    //Enable the gesture recognizer
    draggingPiece = UIPanGestureRecognizer(target: self, action: Selector("dragPiece:"))
    draggingPiece.maximumNumberOfTouches = 1

    self.view?.addGestureRecognizer(draggingPiece)

}

func dragPiece(recognizer: UIPanGestureRecognizer) {
    if recognizer.state == UIGestureRecognizerState.Began {

    }else if recognizer.state == UIGestureRecognizerState.Changed {

    }else if recognizer.state == UIGestureRecognizerState.Ended {

    }
}

And then remove the gesture recognizer in willMoveFromView:

self.view?.removeGestureRecognizer(draggingPiece)
0
votes

One of the simplest ways to achieve this would be to use this (no need to implement touchesBegan):

override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent)
{
    for touch in (touches as! Set<UITouch>)
    {
        let location = touch.locationInNode(self)

        for childNode in children
        {
            if (childNode is SKSpriteNode)
            {
                let node = childNode as! SKSpriteNode

                if (CGRectContainsPoint(node.frame.expand(50.0), location))
                {
                    node.position = location
                }
            }
        }
    }
}

Where the expand extension is simply used to increase the boundaries where the touch is allowed and is defined as:

extension CGRect
{
    func expand(percent: CGFloat) -> CGRect
    {
        let deltaWidth = (percent / 100.0) * self.width
        let deltaHeight = (percent / 100.0) * self.height
        return CGRect(x: self.origin.x, y: self.origin.y, width: self.width + deltaWidth, height: self.height + deltaHeight)
    }
}

You'll probably want to add more logic to handle overlaps et cetera, but this should be a good foundation from where to start.

Hope this helps