Search code examples
swiftsprite-kitphysicsskphysicsbodyskphysicsworld

Gravity value in SpriteKit game scene


I'm trying to create a game using Apple's SpriteKit game engine.

While implementing some physics-based calculations in the game, I noticed that the calculated results differ from what effectively then happens to objects.

Example: calculating a body's trajectory through projectile motion's equations causes the body to actually fall down much sooner/quicker than what calculated.

How can I make the physics engine match the real-world physics laws when calculating something gravity-related?


Solution

  • I think I know what's going on with the sample code you have supplied on GitHub, which I'll reproduce here as questions on SO should contain the code:

    //
    //  GameScene.swift
    //  SpriteKitGravitySample
    //
    //  Created by Emilio Schepis on 17/01/2020.
    //  Copyright © 2020 Emilio Schepis. All rights reserved.
    //
    
    import SpriteKit
    import GameplayKit
    
    class GameScene: SKScene {
    
        private var subject: SKNode!
    
        override func didMove(to view: SKView) {
            super.didMove(to: view)
    
            // World setup (no friction, default gravity)
            // Note that this would work with any gravity set to the scene.
            physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
            physicsBody?.friction = 0
    
            subject = SKShapeNode(circleOfRadius: 10)
            subject.position = CGPoint(x: frame.midX, y: 30)
    
            subject.physicsBody = SKPhysicsBody(circleOfRadius: 10)
            subject.physicsBody?.allowsRotation = false
    
            // Free falling body (no damping)
            subject.physicsBody?.linearDamping = 0
            subject.physicsBody?.angularDamping = 0
    
            addChild(subject)
    
            // Set an arbitrary velocity to the body
            subject.physicsBody?.velocity = CGVector(dx: 30, dy: 700)
    
            // Inaccurate prediction of position over time
            for time in stride(from: CGFloat(0), to: 1, by: 0.01) {
                let inaccuratePosition = SKShapeNode(circleOfRadius: 2)
                inaccuratePosition.strokeColor = .red
    
                // These lines use the projectile motion equations as-is.
                // https://en.wikipedia.org/wiki/Projectile_motion#Displacement
                let v = subject.physicsBody?.velocity ?? .zero
                let x = v.dx * time
                let y = v.dy * time + 0.5 * physicsWorld.gravity.dy * pow(time, 2)
    
                inaccuratePosition.position = CGPoint(x: x + subject.position.x,
                                                      y: y + subject.position.y)
                addChild(inaccuratePosition)
            }
    
            // Actual prediction of position over time
            for time in stride(from: CGFloat(0), to: 1, by: 0.01) {
                let accuratePosition = SKShapeNode(circleOfRadius: 2)
                accuratePosition.strokeColor = .green
    
                // These lines use the projectile motion equations
                // as if the gravity was 150 times stronger.
                // The subject follows this curve perfectly.
                let v = subject.physicsBody?.velocity ?? .zero
                let x = v.dx * time
                let y = v.dy * time + 0.5 * physicsWorld.gravity.dy * 150 * pow(time, 2)
    
                accuratePosition.position = CGPoint(x: x + subject.position.x,
                                                    y: y + subject.position.y)
                addChild(accuratePosition)
            }
    
        }
    
    }
    

    What you've done is to:

    1. Created an object called subject with a physicsBody and placed it on screen with a initial velocity.
    2. Plotted predicted positions for an object with that velocity under gravity via the inaccuratePosition node, using Newton's laws of motion (v = ut + 1/2at²)
    3. Plotted predicted positions for an object with that velocity under gravity * 150 via the accuratePosition node, using Newton's laws of motion

    All this is is didMoveTo. When the simulation runs, the path of the node subject follows the accuratePosition path accurately.

    I think what's happening is that you are calculating the predicted position using subject's physicsBody's velocity, which is in m/s, but the position is in points, so what you should do is convert m/s into point/s first.

    So what's the scale factor? Well from Apple's documentation here; it's.... 150 which is too much of a coincidence 😀, so I think that's the problem.

    Bear in mind that you set the vertical velocity of your object to 700m/s - that's 1500mph or 105000 SK point/s. You'd expect it to simply disappear out through the top of the screen at high speed, as predicted by your red path. The screen is somewhere between 1,000 and 2,000 points.