Search code examples
iosswiftsprite-kitgame-physicsphysics-engine

How to convert Sprite Kits physics into absolute points values?


I am working on a vertical 2D platform auto-jumper game. Level generation is done procedurally using patterns and adaptive difficulty. Game will be released on iOS / OS X and uses Apples Sprite Kit framework.

Physics are implemented using Sprite Kits own physics engine. Jumping for example is done by applying a force or changing the current velocity of the player entity. Something like that:

playerEntity.physicsComponent.velocity =
    CGVectorMake(playerEntity.physicsComponent.velocity.dx, 850.0)

To keep player in it's own "flow zone" adaptive difficulty is very important. This requires the use of unique patterns which offer a unique situation of challenge.

Patterns are generated using a procedural concept. But this requires to know about the players physics behavior, especially jumping distances.

One way is to manually hard-code a few jump distances. A much more flexible way would be to get the jump distance - or jump radius and details like 'air time', etc. - from the physics system.

Unfortunately in Sprite Kit there is no real point-equivalent of the force-vector unit.

So I am asking how to convert Sprite Kits physics into absolute point values. I need a function that I can put in for example a force vector and that outputs me the radius of the possible jump area for the player.

If I had an insight on how the Sprite Kit physics work, for example a 'pixel per frame'-rate, but I guess that does not exist!? Something like the following:

/// Returns a rectangle describing an oval area in absolute points
func jumpRadius(withPhysicsBody body: SKPhysicsBody, 
                andVector vector: CGVector) -> CGRect {

    // Compute area using physics body properties like mass, friction, etc.
    // Return it ...
    return ...
}

Solution

  • Philip Dick, one of my favourite Science Fiction author, in 1978 wrote the article How to Build a Universe That Doesn't Fall Apart Two Days Later. Here's an excerpt:

    Reality is that which, when you stop believing in it, doesn't go away.

    Let's see how this is related to your question and to my answer.

    The wrong approach

    This is the short answer to you question. If you want to convert the position of your Hero sprite to the view system you can add the following property

    class Hero: SKSpriteNode {
        var globalPosition: CGPoint? {
            guard let scene = self.scene else { return nil }
            let pointInScene = self.convertPoint(self.position, toNode: scene)
            return scene.convertPointToView(pointInScene)
        }
    }
    

    Why it won't work

    Now, if your game does use physics then you game does exists into a (virtual) physics world where distances are measured in meters, not points and not pixels.

    Point and pixels are just a way to represent on a flat screen the much more complex reality you are simulating inside the physics world. The visual representation should depend on the physics world but what happen in the physics world should be totally agnostic about the visual representation. And most importantly should not be affected by that.

    Otherwise the moment you change the way a scene does fit the device screen, let's say replacing this

    scene.scaleMode = .AspectFill
    

    with this

    scene.scaleMode = .AspectFit
    

    into your GameViewController you get into serious troubles because this does change the relation between points and meters (I'm sure we can find more examples like this).

    The right approach

    What I suggest is: do your calculation in meters and within your physics world. It should work even when nobody is looking at it (namely when there is no graphics representation of it at all).