2
votes

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
You may need to add a "presentation" node to each of the rope segments. A presentation node is a visual representation of another node that you can change (e.g., rotate) without affecting the physics of the actual node.0x141E
So basically add nodes with no physicsBody and only a texture in place of the existing ropeNodes?Lahav
Yes, and set their positions to the same as the rope segments in didSimulatePhysics and make the rope node with the body invisible.0x141E
That may work, however that would require that I keep moving the textures to their correct position every frame, since they don't actually have any physics. This would be very inefficientLahav
Another way to do this is to add a child node that rotates, but you will need to counter the rotation of the parent and then rotate the child to the desired angle.0x141E

1 Answers

0
votes

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
    }
}