如何使用SpriteKit使精灵跳到特定高度?

8
我正在尝试使用applyImpulse使精灵跳到特定高度。在下面的示例代码中,动态黑色圆圈跳到与静态红色圆圈相同的高度,但只能通过巧妙的方法实现。
如果我的物理学正确,将一个抛射物发射到高度Y所需的初始竖直动量由mass * sqrt(2 * gravity * Y)给出。然而,这个公式导致黑色圆圈几乎没有移动。
经过反复试验,我发现可以通过将冲量向量的竖直分量乘以12.3来使红色圆圈跳得更加准确,如下面的代码所示。
这似乎完全是任意的,让我抓狂了。显然,我做错了什么。有什么正确的方法吗?
以下是我认为相关的代码:
let dy = mass * sqrt( 
    2 * -self.physicsWorld.gravity.dy 
    * (fixedCircle.position.y-jumpingCircle.position.y)) 
    * 12.3

jumpingCircle.physicsBody?.applyImpulse(CGVectorMake(0, CGFloat(dy)))

以下是GameScene.swift类的全部内容,如果您想复制和粘贴...
import SpriteKit

class GameScene: SKScene {

var jumpingCircle = SKShapeNode()
var fixedCircle = SKShapeNode()

override func didMoveToView(view: SKView) {

    self.scaleMode = .AspectFit

    // Create an exterior physical boundary
    self.physicsBody = SKPhysicsBody(edgeLoopFromRect:self.frame)
    self.physicsWorld.gravity = CGVectorMake(0, -5);


    // Create the jumping circle
    jumpingCircle = SKShapeNode(circleOfRadius: 50)
    jumpingCircle.position = CGPoint(x: 100,y: 0)
    jumpingCircle.strokeColor = .blackColor()
    jumpingCircle.physicsBody = SKPhysicsBody(circleOfRadius: 50)
    jumpingCircle.physicsBody?.linearDamping = 0.0
    self.addChild(jumpingCircle)


    // Create the fixed circle
    fixedCircle = SKShapeNode(circleOfRadius: 50)
    fixedCircle.position = CGPoint(x: 100,y: 384)
    fixedCircle.strokeColor = .redColor()
    self.addChild(fixedCircle)

}

override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {

    // to reach certain height, initial y velocity should by sqrt(2 * gravity * Y)
    // momentum required to reach this velocity is mass * velocity
    // therefore, vertical impulse should be given by mass * sqrt(2 * gravity * Y)

    if let mass = jumpingCircle.physicsBody?.mass{
        // calculate vertical impulse

        // this formula "should work" but does not
        // let dy = mass * sqrt(2 * -self.physicsWorld.gravity.dy * (fixedCircle.position.y-jumpingCircle.position.y))

        // this formula "works", but has the arbitrary multiplier
        let dy = mass * sqrt(2 * -self.physicsWorld.gravity.dy * (fixedCircle.position.y-jumpingCircle.position.y)) * 12.3

        // apply the Impulse
        jumpingCircle.physicsBody?.applyImpulse(CGVectorMake(0, CGFloat(dy)))
     }
     }
 }

你的方程看起来没问题。冲量等于质量乘以速度变化。由于你想从零加速到某个速度(v),那么你的冲量应该是 i = (m * sqrt(2(9.81)(y2-y1))),只要你只在y轴上工作。编辑:不能在平方根内使用负数。 - Micrified
@Owatch:重力向量本身是负的,所以在dy公式中sqrt是正的。我很高兴方程看起来没问题。但它仍然不起作用。红色圆圈只是跳了一点点。 - Michael Ames
你是否正在使用SpriteKit内置的重力功能?编辑:好的,我明白了。 - Micrified
是的,在代码示例的第6行左右:self.physicsWorld.gravity = CGVectorMake(0,-5)。 - Michael Ames
为什么重力是-5,尽管我认为这不应该有影响,因为您继续使用该常数。即使您在某个地方意外地使用了-9.81,它也只会使其飞得太高(或者反过来,如果物体使用-5而真实重力为-9.81,则会飞得太低)。 - Micrified
因为这是一个很好的圆整数,我可以用来进行测试,并确定重力向量是否是问题的一部分。它不是。您可以运行上面的代码,将世界重力改为-9.8或其他值,并获得完全相同的行为和结果。您仍然需要12.3倍增量。实际上,该公式无论a)世界重力或b)物体质量如何,都能正常工作,这是我正确基本物理公式的最大线索。但好像有一个12.3的单位到像素比率必须加以考虑。 - Michael Ames
2个回答

10

问题得到解决。

SpriteKit显然有一个未经记录的像素到“米”的比率,为150。

当我意识到SpriteKit自动为我的圆形计算的质量不是50*pi*r^2时,我发现了这一点。我从质量逆推计算出SpriteKit正在使用的半径,它是7500,刚好是50*150。

而12.3呢?它恰好是150的平方根(近似值)。

因此,要使这些物理模拟工作,您必须考虑这个比率。我称之为“像素单位”(PTU),因为它与米无关,尽管苹果坚称SpriteKit使用国际单位制。但由于它未经记录,所以可能会更改,因此我正在使用以下代码行启动我的模拟来确定真正的PTU:

let ptu = 1.0 / sqrt(SKPhysicsBody(rectangleOfSize: CGSize(width:1,height:1)).mass)

这是一项昂贵的操作,因此应该仅调用一次。我在设置初始场景时调用它。

现在我的脉冲计算如下:

let dy = mass * sqrt(2 
    * -self.physicsWorld.gravity.dy 
    * (fixedCircle.position.y-jumpingCircle.position.y 
    * ptu))

现在,黑色圆圈完美地对齐红色圆圈,我可以安心睡觉了。


是150像素还是点?我需要一种视网膜和非视网膜的常数,谢谢。 - Juan Boero
1
这太棒了!!谢谢!!!我相信这花了很多功夫。这是ptu,使用最新版本的内容:let ptu = 1.0 / sqrt(SKPhysicsBody(rectangleOf:CGSize(width: 1.0, height: 1.0)).mass) - brilliantairic
我们如何在水平移动方面解决这个问题?我发现在较小的屏幕上,玩家以相同的速度移动得更快。然而,水平重力为零,使得这个计算毫无用处。 - hotdogsoup.nl
这个方程中的括号是否准确?仅在跳跃圆位置上乘以ptu似乎有些奇怪,而不是两者之差。 - danylo.net

-2

使用速度而不是applyImpulse。速度会更具体。 jumpingCircle.physicsBody?.velocity = CGVectorMake(jumpingCircle.physicsBody.velocity.dx, 300.0f); 代码未经测试。


谢谢,但它并没有以正确的方式解决问题。applyForce()会“推”圆形向上加速,因为力在一段时间内是恒定的。我需要模拟跳跃,垂直地抵抗重力。这正是applyImpulse所要做的(事实上也确实做到了——只是通过这个奇怪的修补来使其正常工作)。 - Michael Ames

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接