Comment faire sauter un sprite à une hauteur spécifique avec SpriteKit?

J'essaye d'employer applyImpulse pour faire sauter un sprite à une taille spécifique. Dans l'exemple de code ci-dessous, le cercle noir dynamic saute à la même hauteur que le cercle rouge statique, mais il ne fonctionne qu'avec un kludge.

Si ma physique est bonne, l'impulsion verticale initiale requirejse pour lancer un projectile à la hauteur Y est donnée par la masse * sqrt (2 * gravity * Y). Pourtant, cette formule fait que le cercle noir bouge très peu.

Par essais et erreurs, j'ai découvert que je peux faire sauter le cercle rouge plus ou less précisément en multipliant la composante verticale du vector d'impulsion par 12.3, comme illustré dans le code ci-dessous.

Cela semble complètement arbitraire et me rend fou. Je fais évidemment quelque chose de mal. Quelle est la bonne façon de faire cela?

Voici ce que je pense être le morceau de code pertinent:

let dy = mass * sqrt( 2 * -self.physicsWorld.gravity.dy * (fixedCircle.position.y-jumpingCircle.position.y)) * 12.3 jumpingCircle.physicsBody?.applyImpulse(CGVectorMake(0, CGFloat(dy))) 

Voici la class GameScene.swift dans son intégralité, si vous souhaitez copyr et coller …

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

Deviner.

SpriteKit a apparemment un ratio pixel-to-"meter" non documenté de 150.

J'ai découvert ceci quand j'ai réalisé que la masse que SpriteKit calculait automatiquement pour mes cercles n'était pas 50 * pi * r ^ 2 comme il se doit. J'ai travaillé en arrière à partir de la masse pour calculer le rayon que SpriteKit utilisait, et c'était 7500, ce qui se trouve être 50 * 150.

Et 12.3? Il arrive juste d'être (approximativement) la racine carrée de 150.

Donc, pour que ces simulations de physique fonctionnent, vous devez considérer ce ratio. Je l'appelle "pixel to unit" (PTU) parce que cela n'a rien à voir avec les counturs malgré l'insistance d'Apple pour que SpriteKit utilise des unités SI. Mais comme il n'est pas documenté, il semble possible de changer, donc je lance ma simulation en utilisant la ligne de code suivante pour déterminer le vrai PTU:

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

C'est une opération coûteuse, donc elle ne devrait être appelée qu'une seule fois. Je l'appelle lors de la mise en place de la scène initiale.

Mon calcul d'impulsion est maintenant comme suit:

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

Et maintenant le cercle noir saute pour s'aligner parfaitement sur le cercle rouge et je peux returnner dormir la nuit.

Au lieu de applyImpulse utilisez la vélocité. La vélocité sera plus spécifique. jumpingCircle.physicsBody?.velocity = CGVectorMake(jumpingCircle.physicsBody.velocity.dx, 300.0f); Code non testé.