-1

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

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

示例:通过射弹运动的方程计算身体的轨迹会导致身体实际上比计算的更快/更快地下降。

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

4

2 回答 2

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.

于 2020-01-17T20:44:57.063 回答
1

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

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


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


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

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

文档说:

此属性的组成部分以米/秒为单位。默认值为(0.0,-9.8),代表地球的重力。

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

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

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

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

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

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

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

我发现在游戏中使用物理世界的重力属性时,必须考虑到~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)

导致了准确的计算。

我希望这对将来可能遇到同样问题的人有用。

于 2020-01-17T11:14:37.270 回答