问题
I've been rehearsing Sprite Kit lately and I've come across a very strange problem. When zooming (changing the scale of) the parent node, bodies that are joined together by SKPhysicsJointPin separate from each other gradually and then joints break. Let me show you the images.
This is for the normal state:
Here's when zoomed in:
And here's when zoomed out:
If you ask how I join bodies: I join the brown sticks to the blue nodes on the center of the blue nodes. Any ideas what my problem is?
EDIT: I've recently found out that joints don't break and everything works as expected when the joining bodies are not dynamic. So for example, if I use [SKPhysicsBody bodyWithEdgleLoopFromRect] instead of [SKPhysicsBody bodyWithRectangleOfSize] to create physics body for a sprite, there's no problem. But I need the bodies to be dynamic.
Here's the code that I use to attach physics to nodes. Of course it's all done dynamically. I just hard coded for brevity.
-(void)attachPhysics{
//fixedComponentLeft & fixedComponentRight are two SKSprites
fixedComponentLeft.physicsBody=[SKPhysicsBody bodyWithCircleOfRadius:fixedComponentLeft.frame.size.width];
fixedComponentRight.physicsBody=[SKPhysicsBody bodyWithCircleOfRadius:fixedComponentLeft.size.width];
beam1.physicsBody=[SKPhysicsBody bodyWithRectangleOfSize:beam1.size];
joiningBody.physicsBody=[SKPhysicsBody bodyWithCircleOfRadius:joiningBody1.size.width];
[self.scene.physicsWorld addJoint:[SKPhysicsJointPin jointWithBodyA:fixedComponentLeft.physicsBody bodyB:beam1.physicsBody anchor:fixedComponentLeft.position]];
[self.scene.physicsWorld addJoint:[SKPhysicsJointPin jointWithBodyA:joiningBody.physicsBody bodyB:beam1.physicsBody anchor:beam1.endPoint]];
beam2.physicsBody=[SKPhysicsBody bodyWithRectangleOfSize:beam2.size];
[self.scene.physicsWorld addJoint:[SKPhysicsJointPin jointWithBodyA:joiningBody.physicsBody bodyB:beam2.physicsBody anchor:beam2.position]];
[self.scene.physicsWorld addJoint:[SKPhysicsJointPin jointWithBodyA:fixedComponentRight.physicsBody bodyB:beam2.physicsBody anchor:beam2.endPoint]];
}
On the above code, beam1 and beam2 are instances of subclass of SKSpriteNode. By default the anchor point is (0,0.5) and I've added a property called endPoint which serves as the rightmost edge point on the sprite.
回答1:
In fact this is not an answer but I thought I'd not use the comment area for this, as the issue is big enough to not accommodate there. I should have guessed that the problem was caused by the fact that the node where the elements are drawn and the physics world their bodies are added to have different coordinate systems. It's because physics world belong to the scene and this means there's typically only one physics world no matter how many child nodes are added to the scene, all the bodies attached to different nodes on different child nodes share the same world. I don't know whether it's good or not. So here are the things we can try:
Scale the scene itself instead of the child nodes: When I tried it, not surprisingly I had a very big problem. As scene is typically the root node for all the nodes in a game, this means everything will be scaled up or down accordingly. This includes the control hud too. So, not a good idea
You can try to subclass SKNode so that it implements its own SKPhysicsWrold instance and add everything to that world: Unfortunately, for some reasons Apple made it impossible to have your own physics world. Yes, you can add your joints to any world you want. But joints need bodies to share the same world. From pure, unwrapped Box2D I remember bodies also needed adding to a physics world. So I assume, apple has made this automatic, when you set the physics body, it gets added to the scene.physicsWorld. And if you try to add joints to a custom created physics world, you're going to get a Box2D error stating that joint capacity is less than the joint count. :))
The only realistic way seems to be to rebuild the physics bodies and joints. But this is a real pain and I have not been successful with it either. Besides a potential performance impact, this approach needs a very accurate order of recreation of bodies and joints, setting their previous velocities and etc.
So, having searched the net for 20+ days and not found a real working solution I can say that this is a real problem that I think Apple needs to consider revisiting, or at least offer good solutions for.
回答2:
I'm not sure what your code looks like, but I ran into a similar problem, where I had a rope in a platformer game (think super mario), using tile maps and if I added the rope at the beginning of the level everything worked ok and the bond between the joints was holding, but when I placed that rope in the middle or at the end of the level, the joints would become more elastic and the rope would break apart, while at the same time sending the physics world into an endless loop, grinding everything to a halt.
The fix turned out to be simple, as you said the physics world belongs to the scene and only the scene itself and I was adding my rope at a position that was based on the node's position, which actually caused the issue.
When I used
CGPoint positionInScene = [self convertPoint:nodeB.position fromNode:self.map];
the following code to convert the node's position to the scene's coordinates and later used that positionInScene variable to attach the joint to as an anchor like so:
[SKPhysicsJointFixed jointWithBodyA: nodeA.physicsBody
bodyB: nodeB.physicsBody
anchor: CGPointMake(positionInScene.x, positionInScene.y)];
everything started working correctly.
So in your case, if you could show the code that you used, maybe we could work out something similar. Hope this helps.
回答3:
Have encounter similar problems with a panning world.
The world coordinates no longer match the physics coordinates.
So you have to use calls to [scene convertPoint:pt fromNode:pannedWorld].
I've overridden applyForce:atPoint: and applyTorque:atPoint: to abstract this and ensure the code remains legible.
I also found that if you add objects to the scene whilst in a didBeginContact: (shrapnel from explosions for example) then any objects created need to be given coordinates in the absolute physics world coordinate system, rather than the relative panned/zoomed world.
In effect everything behaves as though the PhysicsWorld is an absolute & immutable coordinate system, whilst the SKNode views are exactly that, views on the physics world, and can be panned etc. Where it gets confusing is that coordinates are in most cases transparently converted from one system to the other, but not always.
来源:https://stackoverflow.com/questions/21890345/joints-break-when-zooming