-1

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?

Emilio Schepis
  • 1,677
  • 2
  • 9
  • 11
  • Giving a downvote as it would be nice to see what problem you're actually having. No-one else has reported any real problems with SK's simulation of gravity, let alone one that requires you to use 151 times the default value for accurate results. – Steve Ives Jan 17 '20 at 12:47
  • Actually an interesting question and I can see where the confusion arises. If you put your code into the question and explain what you're doing, I'll remove my downvote. – Steve Ives Jan 17 '20 at 21:01

2 Answers2

4

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.

Steve Ives
  • 7,894
  • 3
  • 24
  • 55
  • Yup, in Sprite Kit, 150points = 1 meter and 150* 150points per second * second = 1kg meter per second * second= 1 Newton – Knight0fDragon Jan 17 '20 at 20:58
  • I thought it was a bit of a coincidence that he had to use a scale factor of 150, and the 'metres to points' conversion also involves scaling by 150. – Steve Ives Jan 17 '20 at 21:08
  • That makes a lot of sense, thank you. I hadn't considered that the position and the velocity could use different units. This will be incredibly useful for anyone that comes across this problem. – Emilio Schepis Jan 18 '20 at 08:49
  • @EmilioSchepis You effectively did the metres to points conversion in your formula by multiplying by 150. The fact that the ball followed your predicted path exactly tells you that SK’s calculations can be trusted in this area. – Steve Ives Jan 19 '20 at 00:19
1

Edit - I created a sample project to demonstrate the calculated paths with and without the multiplier.

https://github.com/emilioschepis/spritekit-gravity-sample


TL;DR - When calculating something gravity-related in SpriteKit multiply the gravity of the scene by 151 to obtain an accurate result.


When trying to solve this issue I first started reading the SpriteKit documentation related to gravity:

https://developer.apple.com/documentation/spritekit/skphysicsworld/1449623-gravity

The documentation says:

The components of this property are measured in meters per second. The default value is (0.0,-9.8), which represent’s Earth’s gravity.

Gravity, however is calculated in m/s^2 and not in m/s.

Thinking that was an error in the implementation of gravity in SpriteKit I began thinking that maybe real-world-based physics laws could not be applied in the scene.

I did, however, come across another documentation page about the linear gravity field that correctly reported that gravity is measured in m/s^2 in SpriteKit.

https://developer.apple.com/documentation/spritekit/skfieldnode/1520145-lineargravityfield

I then setup a simple free falling scene where I applied an initial velocity to a physics body and then calculated the expected trajectory, while comparing it to the actual trajectory.

The x-axis calculations were accurate from the start, suggesting that the only problem was with the gravity's value.

I then tried manually modified the gravity in the scene until the actual trajectory matched the predicted one.

What I found is that there is a "magic" value of ~151 that has to be factored in when using the physics world's gravity property in the game.

Modifying, for example, the y-axis calculations for the trajectory from

let dy = velocity.dy * time + 0.5 * gravity * pow(time, 2)

to

let dy = velocity.dy * time + 0.5 * 151 * gravity * pow(time, 2)

resulted in accurate calculations.

I hope this is useful to anyone who might encounter the same problem in the future.

Emilio Schepis
  • 1,677
  • 2
  • 9
  • 11
  • 1
    Do you have some sample code we could use to test this? What results were you getting? People here have used the standard gravity to calculate the 'points per metre' of an SK scene on the assumption the the value for G was correct. If we use a much (much!) higher value of G, then those figures would be wrong. https://stackoverflow.com/a/37523818/1430420 – Steve Ives Jan 17 '20 at 11:34
  • I've never seen any information on SK physics (game tutorial, answer here on SO etc) that states thet SK's default gravity property is 150 times too low. Can you please elaborate on the incorrect results you think you are getting? – Steve Ives Jan 17 '20 at 16:22
  • Absolutely. @SteveIves I have created a simple playground that demonstrates how equations work when adding a 150x factor to the actual gravity. https://github.com/emilioschepis/spritekit-gravity-sample – Emilio Schepis Jan 17 '20 at 18:09
  • You need to account for your mass...… 150points = 1newton (not sure where you are getting 151 from, probably you are creating floating point errors) – Knight0fDragon Jan 17 '20 at 20:35
  • BTW, 1^2 = 1, which is why you are getting a simplified formula. There is nothing wrong with the gravity formula – Knight0fDragon Jan 17 '20 at 20:38
  • @Knight0fDragon - In his code he has no impulses; the body is just given an initial velocity so the trajectory is ballistic so mass doesn't come into this problem. – Steve Ives Jan 17 '20 at 21:03
  • @SteveIves I am actually referring to the density of the point, as in how many points are in 1unit the physics engine uses, not the actual weight of the object. – Knight0fDragon Jan 17 '20 at 21:12
  • @Knight0fDragon ah - OK. Sorry. – Steve Ives Jan 17 '20 at 21:15