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)
}
}
}
您所做的是:
- 使用 physicsBody 创建了一个名为
subject
的对象并将其放置
在屏幕上以初始速度显示。
- 以低于该速度的物体绘制的预测位置
通过
inaccuratePosition
节点的重力,使用牛顿定律
运动 (v = ut + 1/2at²)
- 以低于该速度的物体绘制的预测位置
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 点之间。
我正在尝试使用 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)
}
}
}
您所做的是:
- 使用 physicsBody 创建了一个名为
subject
的对象并将其放置 在屏幕上以初始速度显示。 - 以低于该速度的物体绘制的预测位置
通过
inaccuratePosition
节点的重力,使用牛顿定律 运动 (v = ut + 1/2at²) - 以低于该速度的物体绘制的预测位置
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 点之间。