SpriteKit 游戏场景中的重力值

Gravity value in SpriteKit game scene

我正在尝试使用 Apple 的 SpriteKit 游戏引擎创建游戏。

在游戏中执行一些基于物理的计算时,我注意到计算结果与实际发生在对象上的结果不同。

示例:通过 projectile motion 的方程式计算物体的轨迹导致物体实际下落比计算的 sooner/quicker 多。

在计算与重力相关的东西时,如何使物理引擎符合现实世界的物理定律?

编辑 - 我创建了一个示例项目来演示使用和不使用乘数的计算路径。

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


TL;DR - 在 SpriteKit 中计算与重力相关的东西时,将场景的重力乘以 151 以获得准确的结果.


在尝试解决这个问题时,我首先开始阅读与重力相关的 SpriteKit 文档:

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

文档说:

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.

但是,重力是在 m/s^2 中计算的,而不是在 m/s 中计算的。

认为这是在 SpriteKit 中实现重力的错误,我开始考虑也许基于现实世界的物理定律无法应用到场景中。

不过,我确实看到了另一个关于线性重力场的文档页面,该页面正确地报告了重力是在 SpriteKit 中以 m/s^2 测量的。

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

然后我设置了一个简单的自由落体场景,我将初始速度应用于物理物体,然后计算预期轨迹,同时将其与实际轨迹进行比较。

x 轴计算从一开始就是准确的,表明唯一的问题是重力值。

然后我尝试手动修改场景中的重力,直到实际轨迹与预测的轨迹匹配。

我发现有一个 "magic" 值 ~151 在使用物理世界的重力 属性 时必须考虑在内游戏。

例如修改来自

的轨迹的y轴计算

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

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

计算准确。

我希望这对以后可能遇到同样问题的任何人都有用。

我想我知道你在 GitHub 上提供的示例代码是怎么回事,我将在此处重现,因为关于 SO 的问题应该包含代码:

//
//  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)
        }

    }

}

您所做的是:

  1. 使用 physicsBody 创建了一个名为 subject 的对象并将其放置 在屏幕上以初始速度显示。
  2. 以低于该速度的物体绘制的预测位置 通过 inaccuratePosition 节点的重力,使用牛顿定律 运动 (v = ut + 1/2at²)
  3. 以低于该速度的物体绘制的预测位置 gravity * 150 通过 accuratePosition 节点,使用牛顿定律 运动

这就是didMoveTo。仿真运行时,节点subject的路径准确遵循accuratePosition路径。

认为 发生的事情是你正在使用对象的 physicsBody 的 velocity 计算预测位置,它在 m/s 中,但位置在点,所以你应该做的是先将 m/s 转换为 point/s。

那么比例因子是多少?好吧,来自 Apple 的文档 here;它是.... 150 这太巧合了,所以我认为这就是问题所在。

请记住,您将对象的垂直速度设置为 700m/s - 即 1500mph 或 105000 SK point/s。正如红色路径所预测的那样,您 期望 它会以高速从屏幕顶部消失。屏幕在 1,000 到 2,000 点之间。