Incorrect behaviour with SKPhysicsJointPin when shouldEnableLimits is true and lowerAngleLimit and upperAngleLimit set
Asked Answered
T

2

7

I currently have a ragdoll figure, which consists of a parent node (with no physics bodies attached), and lots of child nodes which each consist of a circle body. The circles are connected to each other using an SKPhysicsJointPin between each circle, as so:

Ragdoll figure

Each SKPhysicsJointPin has shouldEnableLimits = true, and I currently use a value of -0.05 and 0.05 for lowerAngleLimit and upperAngleLimit respectively.

This works pretty well and prevents the shape from deforming too much, except when the figure goes upside-down as a whole, in which case all the joints suddenly try to contract, like so:

Broken ragdoll figure

The joints contract very quickly about when the figure is completely upside down. When he rotates back again, all the joints return to normal. Why does this happen? How do I maintain the correct angles on the joints?

Edit 1:

I just tried rewriting the project in Objective-C in case it was due to some weird Swift bug; turns out this problem still manifests itself in Objective-C, so I've removed the Swift tag.

My best guess at the moment is that the angle of the joints, relative to the world, are incorrectly calculated by SpriteKit when the bodies they are attached to are rotated more than 180° in either direction, and so wrong angles are being passed to the underlying Box2D joints.

I wasn't sure if I was supposed to continuously update the lower- and upper-angle limits to match their bodies' world angles, but it doesn't seem I have to as when the body is very close to being upside-down do the joints stop working properly. I'm going to keep experimenting, anyway…

Edit 2:

I'm now fairly sure that the problem occurs because SpriteKit is (I guess with good reason) modifying the zRotation value to keep it between -180° and 180°. If for example the node is rotating clockwise and it hits -180°, SpriteKit automatically wraps its value back to +180°. I'm fairly confident this wrap-around is causing the joints to behave erratically, I just need to figure out how to counteract it…

Edit 3:

I've uploaded the sample application (including video) which demonstrates this problem, as originally asked for by Apple: Xcode Project / Video

Trainbearer answered 28/8, 2014 at 17:56 Comment(13)
You need to use longer physicsBodies instead of circles. Each physicsbody should represent either an arm, leg, foot, torso, etc. Is your own body made up of a bone structure that you have created here?Blend
I realise it's not an accurate representation of the human body, in this case it's not supposed to be! I'm mostly just concerned with the odd behaviour of the joints at the moment.Trainbearer
did you try to disable gravity for the individual bodies?Eachelle
Although I plan on using gravity eventually, gravity as a whole is disabled at the moment while I figure out the problem with the joints. Even with gravity enabled, the problem still manifests itself.Trainbearer
How are you turning the character upside down?Tambourin
I apply forces to the head node, which effectively drags the rest of the body. If the head moves to the side then down, the body becomes upside down.Trainbearer
I've added some further findings to my question.Trainbearer
I'm having the exact same problem. It's a shame that this isn't fixed in the iOS 8 GM. Perhaps in 8.0.1… Have you had any luck with trying to limit the rotation manually? I'm thinking to try and apply a counter-torque to the bodies in the update loop as they approach/hit/exceed my desired limits.Rumpus
For my part, i've exactly the same issue for a moving chain (my chain links go through each other). Did you manage to fix this issue and how please ?Airedale
I'm afraid not, I did consider manually limiting the joints as Andy suggested, but decided it probably wouldn't work, and I don't have sufficient knowledge of physics engines to make it work. I've submitted a bug to Apple and my project's essentially on hold until they fix it!Trainbearer
This same bug is causing me to reconsider my project as well. No good workaround for it.Ejaculate
I may have run into the same problem. Based on my tests problems arise when the physics engine rotates a body in a chain such that its scene rotation crosses the PI barrier. Check out the log included at the end here: #28640519Cariecaries
If this is related to the behavior I experienced (^), Apple may not have plans to address it.Cariecaries
P
0

"I currently use a value of -0.05 and 0.05 for lowerAngleLimit and upperAngleLimit respectively"

According to the docs...

upperAngleLimit - The largest angle allowed for the pin joint, in radians.

...the limits are in radians.

0.05 radians = 2.86478898 degrees

So, you are only allowing the bodies to move about 5 degrees total. This is an extremely small range. From my experience, SpriteKit starts this weird snapping behavior when the body is forced out of range.

I don't think this behavior is specific to upside down bodies. My best guess is that the physics engine doesn't have the frame rate to apply the force of gravity in one frame and catch the body before its moved out of range in the next.

You should increase the range. I prefer to specify it in degrees. For example...

func degreesToRadians(degrees: CGFloat) -> CGFloat {
    return CGFloat(M_PI) * degrees / 180.0
}

pinJoint.shouldEnableLimits = true
pinJoint.lowerAngleLimit = degreesToRadians(-30.0)
pinJoint.upperAngleLimit = degreesToRadians(180.0)

You'll have to tinker with the angles, mass and gravity until you find a sweet spot where things move realistically.

If you really want a range of motion that small, I'd consider just disabling it altogether.

circle.physicsBody?.allowsRotation = false
Propinquity answered 5/3, 2015 at 6:57 Comment(6)
Thanks for your answer, but I've tried lots of different angles; a small angle isn't the cause. I'm fairly certain the problem is caused by SpriteKit's normalisation of body rotation angles when they wrap past their limit and confuse the underlying Box2D joints. In any case, I don't think a ~5º is particularly too small for a physics engine to calculate, and it does a perfectly good job with them until the character goes upside down!Trainbearer
Well all I can tell you is that I've implemented a ragdoll in SpriteKit and I don't have any issues when it goes upside down. What you are trying to do is 100% possible.Propinquity
When I submitted the bug report, Apple asked for a sample application/video—I just fixed it for modern Swift and re-uploaded (still fails in same way), feel free to take a look (added link to original post); I'd be interested in any insight you had!Trainbearer
I'll see if I can get it working when I get some free time. If not, I'm interested in tracking the bug so thanks.Propinquity
I wasn't able to get it working with the body sizes and angles you set. I did notice that the larger the bodies and angles the less and less it happened until not at all. I also noticed that it only seems to happen in "chains" of bodies, which leads me to my new theory that in the process of applying the limit to the first joint on the body, it is pulling the other joint on the same body out of range. Sounds like a bug. An interesting experiment would be seeing if this issue occurs in box2d without SpriteKit. If so, it could be a limitation of the engine. If not, its a Spritekit bugPropinquity
Interesting, thanks—I'm sure I've seen similar chains and stuff working before in Box2D. I might have to implement something similar in it to find out!Trainbearer
C
0

I can confirm, that this is a bug in SpriteKit. In my case I'm using the simplest scenario of two sprites joined by pin joint. Unfortunately it's still happening on iOS 9 :(

Cotonou answered 15/11, 2015 at 7:29 Comment(1)
Also it's the reason I had to use chipmunk in my project.Cotonou

© 2022 - 2024 — McMap. All rights reserved.