0

I'm making a game with swift and spritekit and I have a function where I have multiple sprites falling from the top of the screen. I also made it so I can be able to detect a touch on the sprite using nodeAtPoint, and made it possible to flick the sprites. My problem is that due to nodeAtPoint dragging the deepest node in the tree, when i click and drag on a sprite, the newest sprite in the scene gets pulled toward my touch instead of the one i originally touched. If anyone has some suggestions on how to only affect the node I touched I'd really appreciate it.

    class GameScene: SKScene {

    var ball = SKSpriteNode(imagedNamed: "blueBlue")

    var touchedNode: SKNode? = SKNode()

     override func didMoveToView(view: SKView) {

       var create = SKAction.runBlock({() in self.createTargets()})
            var wait = SKAction.waitForDuration(2)
            var waitAndCreateForever = SKAction.repeatActionForever(SKAction.sequence([create, wait]))
            self.runAction(waitAndCreateForever)
    }


        func createTargets() {
             ball = SKSpriteNode(imageNamed: "blueBlue")
             let randomx = Int(arc4random_uniform(170) + 290)
             ball.zPosition = 0
            ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.size.width / 11)

            ball.physicsBody?.dynamic = true
            ball.size = CGSize(width: ball.size.width / 1.5, height: ball.size.height / 1.5)
            let random = Int(arc4random_uniform(35))

            let textures = [texture1, texture2, texture3, texture4, texture5, texture6, texture7, texture8, texture9, texture10, texture11, texture12, texture13, texture14, texture15, texture16, texture17, texture18, texture19, texture20, texture21, texture22, texture23, texture24, texture25, texture1, texture7, texture18, texture24, texture25, texture1, texture7, texture18, texture24, texture25]

            ball.texture = textures[random] 
            ball.position = CGPoint(x: randomx, y: 1400) 
            addChild(ball)
    }
        override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
            let touch = touches.first as! UITouch
            let touchLocation = touch.locationInNode(self)
           touchedNode = self.nodeAtPoint(touchLocation)
            if touchedNode.frame.contains(touchLocation) {
            touching = true
            touchPoint = touchLocation
            }
       }

        override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
            let touch = touches.first as! UITouch
            let touchLocation = touch.locationInNode(self)
            touching = true
            touchPoint = touchLocation

        }
     override func update(currentTime: NSTimeInterval) {

            if touching {

                if touchPoint != touchedNode.position {

                    let dt: CGFloat = 0.1

                    let distance = CGVector(dx: touchPoint.x - touchedNode.position.x, dy: touchPoint.y - touchedNode.position.y)

                    let vel = CGVector(dx: distance.dx / dt, dy: distance.dy / dt)

                    if touchedNode.parent != nil {

                    touchedNode.physicsBody?.velocity = vel

                }    
             }
            }  
        }
    }
Daniel Mihaila
  • 585
  • 1
  • 6
  • 13
  • Not sure if this will help you but see here: http://stackoverflow.com/a/28259980/2158465 – Epic Byte Jul 07 '15 at 15:14
  • @EpicByte looks like the OP's code is based on your post. I found it interesting that the code sets the velocity to move the physics body instead of using a force or impulse – 0x141E Jul 07 '15 at 17:48
  • yup I got the code from someone else who probably got it from your post. – Daniel Mihaila Jul 07 '15 at 22:35
  • @0x141E I always choose to set the velocity directly instead of indirectly through the applyImpulse/force methods, especially if i'm performing a custom real-time calculation such as moving to a point, simulating buoyancy, etc; It's also slightly faster and gives me complete control over the calculation. Only time I apply impulse and forces is if i'm not doing the calculations myself. For example maybe I want to apply an impulse at a point, or maybe I want to account for the mass of the physics body etc. In these cases I simply let the physics engine determine the velocity for me. – Epic Byte Jul 08 '15 at 03:01
  • @DanielMihaila Is your issue resolved or do you still have a problem? – Epic Byte Jul 08 '15 at 03:03
  • @EpicByte still have the problem, dragging the wrong node. – Daniel Mihaila Jul 08 '15 at 04:15
  • @DanielMihaila Ah I misread the question. Node at point will just return the first node during the tree traversal. That node may be drawn at the top or bottom, it does not take into account the node's zPosition. What you can do is use nodesAtPoint, then search the array for the node with the greatest zPosition (the node on top.) See this http://stackoverflow.com/a/31237482/2158465 – Epic Byte Jul 08 '15 at 04:27
  • @DanielMihaila Another solution is to manage your own array of nodes in order of zPosition and when touches began check the nodes in the array from top to bottom. – Epic Byte Jul 08 '15 at 04:30
  • Thanks for the suggestions – Daniel Mihaila Jul 09 '15 at 01:02

1 Answers1

0

I suggest you make the following changes to the code:

  1. Add a check to see if a sprite is already being moved (if yes, do nothing)
  2. Turn off the affectedByGravity property for the sprite being moved
  3. Reset touchedNode and touching variables when the touch ends

Here's an implementation with the above changes...

Replace touchesBegan and touchesMoved with the following

override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
    let touch = touches.first as! UITouch
    let touchLocation = touch.locationInNode(self)
    if (!touching) {
        let node = self.nodeAtPoint(touchLocation)
        if node is SKSpriteNode {
            touchedNode = node
            touching = true
            touchPoint = touchLocation
            touchedNode!.physicsBody?.affectedByGravity = false
        }
    }
}

override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
    if (touching) {
        let touch = touches.first as! UITouch
        let touchLocation = touch.locationInNode(self)
        touchPoint = touchLocation
    }
}

And add this method

override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
    if (touchedNode != nil) {
        touchedNode!.physicsBody!.affectedByGravity = true
    }
    touchedNode = nil
    touching = false
}
0x141E
  • 12,613
  • 2
  • 41
  • 54
  • Hey, thanks for the suggestion. I used your code and unfortunately I still had the same issue. I did notice though, that if i let the nodes fall a little bit deeper, that i could successfully drag the node i click. Maybe this could help with what im trying to do? – Daniel Mihaila Jul 07 '15 at 10:00
  • I'm not experiencing the issue you described, and I don't see how that's happening given the code. I can correctly select and drag the sprites as they fall from the top of the screen. – 0x141E Jul 10 '15 at 08:34
  • well I didn't post my full code so maybe something else in my code interfering with it. It's all good anyways, since I changed my game to not require dragging those nodes. Thanks again – Daniel Mihaila Jul 10 '15 at 18:30