SKPhysicsJoint makes rotation not work properly

206 views Asked by At

Using Swift and Sprite-Kit, I am trying to create a rope with realistic physics between the location of a static SKSpriteNode called "pin", and wherever the user touches the screen. I am doing this by adding individual SKSpriteNodes called ropeNodes, and linking them up with a series of SKPhysicsJointPins. The physics works just fine, however when I try to rotate each individual piece so that they are oriented properly, the ropeNodes no longer form a straight line, nor do they rotate to the correct angle. When I remove the SKPhysicsJoints however, the rotation works as intended for each separate Node. Moving around the anchorPoint for each individual ropeNode only seemed to jumble things up worse. Why does this happen, and how could I go about fixing it? Thanks in advance (:

override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
    /* Called when a touch begins */
    
    for touch in (touches as! Set<UITouch>) {
        let location = touch.locationInNode(self)
        dx = pin.position.x - location.x
        dy = pin.position.y - location.y
        let length = sqrt(pow(dx!, 2) + pow(dy!, 2))
        let distanceBetweenRopeNodes = 40
        let numberOfPieces = Int(length)/distanceBetweenRopeNodes
        var ropeNodes = [SKSpriteNode]()
        
        //adds the pieces to the array and the scene at respective locations
        for var index = 0; index < numberOfPieces; ++index{
            let point = CGPoint(x: pin.position.x + CGFloat((index) * distanceBetweenRopeNodes) * sin(atan2(dy!, -dx!) + 1.5707), y: pin.position.y + CGFloat((index) * distanceBetweenRopeNodes) * cos(atan2(dy!, -dx!) + 1.5707))
            let piece = createRopeNode(point)
            piece.runAction(SKAction.rotateByAngle(atan2(-dx!, dy!), duration: 0))
            ropeNodes.append(piece)
            self.addChild(ropeNodes[index])
        }

        //Adds an SKPhysicsJointPin between each pair of ropeNodes
        self.physicsWorld.addJoint(SKPhysicsJointPin.jointWithBodyA(ropeNodes[0].physicsBody, bodyB: pin.physicsBody, anchor:
            CGPoint(x: (ropeNodes[0].position.x + pin.position.x)/2, y: (ropeNodes[0].position.y + pin.position.y)/2)))
        for var i = 1; i < ropeNodes.count; ++i{
            let nodeA = ropeNodes[i - 1]
            let nodeB = ropeNodes[i]
            let middlePoint = CGPoint(x: (nodeA.position.x + nodeB.position.x)/2, y: (nodeA.position.y + nodeB.position.y)/2)
            let joint = SKPhysicsJointPin.jointWithBodyA(nodeA.physicsBody, bodyB: nodeB.physicsBody, anchor: middlePoint)
            self.physicsWorld.addJoint(joint)
        }
    }
}

func createRopeNode(location: CGPoint) -> SKSpriteNode{
    let ropeNode = SKSpriteNode(imageNamed: "RopeTexture")
    ropeNode.physicsBody = SKPhysicsBody(rectangleOfSize: ropeNode.size)
    ropeNode.physicsBody?.affectedByGravity = false
    ropeNode.physicsBody?.collisionBitMask = 0
    ropeNode.position = location
    ropeNode.name = "RopePiece"
    return ropeNode
}

This is an image of what happens when I try to rotate each individual ropeNode

enter image description here

1

There are 1 answers

1
0x141E On BEST ANSWER

After some thought, I think adding the presentation node as child is a better idea, since you don't need to keep track of the extra nodes in a separate array. To set the rotation of the child, simply unwind the parent's rotation and then add the new rotation angle:

node.zRotation = -node.parent!.zRotation + newRotation

If the rope segments are in a container SKNode, you can iterate over them by

for rope in ropes.children {
    if let node = rope.children.first {
        let newRotation = ...
        node.zRotation = -rope.zRotation + newRotation
    }
}