This issue is likely related to this issue. However I do not have sufficient reputation to add to that discussion.
When the zRotation of a pinned node reaches (or perhaps becomes close to) positive or negative PI in the scene space, and node.physicsBody.allowRotation = false, the node's local zRotation instantly jumps to negative or positive 5.497... radians, depending on the direction of rotation.
If the nodes are moving, the visual result is the chain of nodes flips around, jitters and/or generally misbehaves.
If the nodes are not moving, the results still happens but without noticeable visual effect.
I have recreated the issue in a small test case below.
Edit: This problem seems limited to where bodies are pinned in a series. I've seen both here where nodes are nested, and where nodes have the same parent and pinned in a series manually.
Edit 2: Changed test case to output node zRotation values over the course of the update sequence.
Edit 3: Confirmed with Apple this is a bug.
import SpriteKit
class GameScene: SKScene {
var nodes :[SKNode]
required init?(coder aDecoder: NSCoder) {
nodes = Array<SKNode>()
super.init(coder: aDecoder)
}
override func didMoveToView(view: SKView) {
NSLog("Create nodes")
physicsWorld.speed = 0.5
var prev = self as SKNode
var newNode:SKShapeNode
for n in 0...5{
newNode = SKShapeNode(circleOfRadius: 20)
newNode.name = String(n)
newNode.position = (n==0 ? CGPoint(x: 400, y: 400):CGPoint(x: 50, y: 0))
newNode.zRotation = CGFloat(M_PI/4)
prev.addChild(newNode)
prev = newNode
newNode.physicsBody = SKPhysicsBody(circleOfRadius: 20, center: CGPoint(x: 0, y: 0))
newNode.physicsBody?.affectedByGravity = true
newNode.physicsBody?.dynamic = (n==0 ? false : true)
newNode.physicsBody?.pinned = true
newNode.physicsBody?.affectedByGravity = false
newNode.physicsBody?.allowsRotation = (n==1 ? true : false) // allowing rotation removes issue
newNode.physicsBody?.collisionBitMask = 0x00000000
nodes.append(newNode)
NSLog("node \(newNode.name):\(newNode.zRotation)")
}
}
override func update(currentTime: NSTimeInterval) {
NSLog("update")
for node in nodes{
NSLog("node \(node.name):\(node.zRotation)")
}
}
override func didEvaluateActions() {
NSLog("didEvaluateActions")
for node in nodes{
NSLog("node \(node.name):\(node.zRotation)")
}
}
override func didSimulatePhysics() {
NSLog("didSimulatePhysics")
for node in nodes{
NSLog("node \(node.name):\(node.zRotation)")
}
}
override func didApplyConstraints() {
NSLog("didApplyConstraints")
for node in nodes{
NSLog("node \(node.name):\(node.zRotation)")
}
}
}
Output:
Create nodes
node Optional("0"):0.785398185253143
node Optional("1"):0.785398185253143
node Optional("2"):0.785398185253143
node Optional("3"):0.785398185253143
node Optional("4"):0.785398185253143
node Optional("5"):0.785398185253143
update
node Optional("0"):0.785398185253143
node Optional("1"):0.785398185253143
node Optional("2"):0.785398185253143
node Optional("3"):0.785398185253143
node Optional("4"):0.785398185253143
node Optional("5"):0.785398185253143
didEvaluateActions
node Optional("0"):0.785398185253143
node Optional("1"):0.785398185253143
node Optional("2"):0.785398185253143
node Optional("3"):0.785398185253143
node Optional("4"):0.785398185253143
node Optional("5"):0.785398185253143
didSimulatePhysics
node Optional("0"):0.785398185253143
node Optional("1"):0.785398185253143
node Optional("2"):0.785398185253143
node Optional("3"):-5.49778699874878 <<<<<
node Optional("4"):0.785398006439209
node Optional("5"):0.785398006439209