Anthony Mattox
Anthony Mattox

Reputation: 7118

SKPhysicsWorld Simulate Physics in Scaled Nodes

I'm attempting to build a SpriteKit game which involves levels with physics bodies which can be zoomed in and out on. The physics world is not behaving as I would expect and causing strange things to happen when the zoom level is changed.

The nodes with physics bodies animate and behave as expected. Zooming around the level is handled with a pinch gesture which is adjusting the scale of a single 'world node' which contains all of the game elements. This is a convenient way to handle it as scaling the scene seems to do horrible things, and it will make it easy to add a separate unscaled node for any game interface elements on top of the world node.

The zooming works something like this:

- (void) handlePinchGesture:(UIPinchGestureRecognizer *) gesture {
    if (gesture.state == UIGestureRecognizerStateChanged) {
        [self.worldNode runAction:[SKAction scaleBy:gesture.scale duration:0.0]];
        gesture.scale = 1;
    }
}

When at the normal zoom level, everything works great, and visually, everything looks fine at different zoom levels as well. The problem is that when the zoom changes, the physics bodies continue to move at the same speed across the speed not at the same speed relative to other objects. The result is that everything moves much faster when zooming out to see more of the scene.

This seems to be a side effect of the fact that the physics world is connected only to the scene and is not aware of the adjusted scale of the 'world node'. Shouldn't the physics bodies continue moving the nodes at the same speed in their relative spaces though?

Can anyone provide any insight into why SpriteKit behaves the way it does, or how to get around this?

Upvotes: 3

Views: 282

Answers (2)

prototypical
prototypical

Reputation: 6751

This answer is based on @TheisEgeberg's suggested approach in the comments of his answer. Made this answer as I think it needs to be an answer and not a comment. If he updates his answer with this approach, I'll delete this answer. Will save people lots of headaches to have this approach as an answer for the question.

1.Create a SKScene that will contain your physics nodes.

2.Create a SKNode that will act as your mimmic of the SKScene

3.For each node you create and add to the SKScene, you create another mimic node - but without a physicsBody - and add it to the mimic SKNode;

4.Create a method called updateMimicNodes in your SKScene that iterates through all nodes and updates the corresponding node's position, zRotation in the mimic node. Call that method from your update method, so it gets updated each frame.

5.Scale the mimic node however you like.

6.Hide the SKScene nodes that have the physics bodies using the hidden property. If you put all the nodes on a SKNode layer you add to the SKScene, you can just set the hidden property of that SKNode to YES;

To make it easier to manage I created a "mimic" property on all my nodes, and set that property to my the corresponding node on the mimic node. You can then just update each node in the update like this :

node.mimic.position = node.position
node.mimic.zRotation = node.zRotation

I might post some example code for this approach later, but if you follow those steps, you should get to the promised land.

Also keep in mind that any touches will need to have locations converted to the coordinate system of the SKScene with the physics bodies.

Upvotes: 1

Theis Egeberg
Theis Egeberg

Reputation: 2576

The reason it behaves this way is obvious. Imagine you want to scale a ball to a smaller size, it should still move the same way though right?

The problem occurs because you scale "A whole system of balls" at the same time. Which doesn't have any really sensible outcome besides what is happening. SpriteKit would literally need to guess at what you're trying to do and either scale physics speeds with it or without it based on how much it is scaling to make it work.

The solution is to scale the entire scene instead of subnodes.

Edit: To scale the scene you set the size. You need to set the scaleMode of the scene to SKSceneScaleModeAspectFill.

Edit2: The subscenes solution only work if you add them to dedicated second SKView, and it's fairly far fetched I admit. You probably need to go with a single scene, scale that up and down, move subnodes accordingly and keep your interface elements hidden (or "counter scale them").

Upvotes: 2

Related Questions