2015-02-28 12 views
6

Il mio obiettivo è quello di avere "orbita" di FirstBody prima con una velocità costante (500 in questo caso).Velocità orbita costante intorno al punto con SKNode

Sulla base di Constant movement in SpriteKit Ho implementato il seguente:

override func didMoveToView(view: SKView) { 

    physicsWorld.gravity = CGVector(dx: 0, dy: 0) 

    let firstNode = SKShapeNode(circleOfRadius: 10) 
    addChild(firstNode) 
    firstNode.position = CGPointMake(CGRectGetWidth(frame)/2.0, CGRectGetHeight(frame)/2.0) 
    let firstPhysicsBody = SKPhysicsBody(circleOfRadius: 10) 
    firstPhysicsBody.dynamic = false 
    firstNode.physicsBody = firstPhysicsBody 

    let secondNode = SKShapeNode(circleOfRadius: 10) 
    addChild(secondNode) 
    secondNode.position = CGPointMake(CGRectGetWidth(frame)/2.0 + 40, CGRectGetHeight(frame)/2.0 + 40) 
    let secondPhysicsBody = SKPhysicsBody(circleOfRadius: 10) 
    secondPhysicsBody.friction = 0 
    secondPhysicsBody.linearDamping = 0 
    secondPhysicsBody.angularDamping = 0 
    secondPhysicsBody.affectedByGravity = false 
    secondNode.physicsBody = secondPhysicsBody 

    let joint = SKPhysicsJointPin.jointWithBodyA(firstPhysicsBody, bodyB: secondPhysicsBody, anchor: firstNode.position) 
    joint.frictionTorque = 0 
    physicsWorld.addJoint(joint) 

    secondPhysicsBody.velocity = CGVector(dx: 0, dy: 500) 
} 

Il problema che sto avendo è che secondNode sta rallentando col passare del tempo. Si noterà che mi pongo ogni sorta di cose come gravity su SKPhysicsWorld, frictionlinearDamping & angularDamping su SKPhysicsBody e frictionTorque su SKPhysicsJoint.

Quindi, cosa sto sbagliando? E come posso mantenere costante la velocità del secondo nodo senza fare calcoli orribili in -update?

Inoltre, sono consapevole che posso aggiungere SKAction per seguire un percorso circolare, ma in questo caso non è una soluzione ragionevole.

Se c'è qualcosa di semplice che mi sfugge, potresti anche consigliare quali degli "0" e "falsi" che sto impostando possono essere rimossi.

Grazie

+1

Non ho familiarità con Swift ma perché non controllate e regolate, se necessario, la velocità di dy nel metodo di aggiornamento? – sangony

+0

Sembra che tutti quelli con cui parlo di questo suggeriscano lo stesso. Sentivo che questo era un po 'un trucco e dovevo fare affidamento sul motore fisico per fare il sollevamento pesante ma sembra che non dovrei fidarmi di quanto ero disposto a ... –

risposta

7

Il comportamento rallentamento è spesso da aspettarsi quando si tratta di motori fisici. Non sono perfetti, specialmente in scenari più complessi come i vincoli. Non dovresti affidarti a Sprite Kit per fornire una perfetta simulazione del movimento continuo, perché non abbiamo idea di cosa stia facendo il motore della fisica; ci sono così tanti fattori coinvolti.

Nei casi in cui è necessario il movimento continuo, (in particolare qualcosa come il movimento centripeto costante) è sempre meglio calcolare e conservare questi valori da soli. Semplicemente non è necessario eseguire calcoli di scrittura nel metodo di aggiornamento per comportamenti come questo.

Così ho scritto un progetto di esempio rapido che calcola la velocità necessaria per orbitare un punto particolare. Basta specificare un punto, la posizione dell'orbita e il raggio dell'orbita. Si noti che poiché sto calcolando tutto, non è necessario alcun SKJoints, in modo da rendere questa implementazione anche più leggera. Vi raccomando caldamente di fare la vostra orbita in questo modo, poiché vi dà il controllo totale su come volete che i vostri nodi orbitano l'un l'altro (cioè potreste avere nodi in orbita di nodi in movimento, potreste avere percorsi ovali in orbita, potreste regolare l'orbita durante la chiave momenti nel vostro gioco, ecc)

import SpriteKit 

class GameScene: SKScene { 
    var node1: SKShapeNode! 
    var node2: SKShapeNode! 
    var node2AngularDistance: CGFloat = 0 

    override func didMoveToView(view: SKView) { 
     physicsWorld.gravity = CGVector(dx: 0, dy: 0) 
     node1 = SKShapeNode(circleOfRadius: 10) 
     node1.position = CGPoint(x: self.size.width/2.0, y: self.size.height/2.0) 
     node1.physicsBody = SKPhysicsBody(circleOfRadius: 10) 
     node2 = SKShapeNode(circleOfRadius: 10) 
     node2.position = CGPoint(x: self.size.width/2.0+50, y: self.size.height/2.0) 
     node2.physicsBody = SKPhysicsBody(circleOfRadius: 10) 
     self.addChild(node1) 
     self.addChild(node2) 
    } 
    override func update(currentTime: NSTimeInterval) { 
     let dt: CGFloat = 1.0/60.0 //Delta Time 
     let period: CGFloat = 3 //Number of seconds it takes to complete 1 orbit. 
     let orbitPosition = node1.position //Point to orbit. 
     let orbitRadius = CGPoint(x: 50, y: 50) //Radius of orbit. 

     let normal = CGVector(dx:orbitPosition.x + CGFloat(cos(self.node2AngularDistance))*orbitRadius.x ,dy:orbitPosition.y + CGFloat(sin(self.node2AngularDistance))*orbitRadius.y); 
     self.node2AngularDistance += (CGFloat(M_PI)*2.0)/period*dt; 
     if (fabs(self.node2AngularDistance)>CGFloat(M_PI)*2) 
     { 
      self.node2AngularDistance = 0 
     } 
     node2.physicsBody!.velocity = CGVector(dx:(normal.dx-node2.position.x)/dt ,dy:(normal.dy-node2.position.y)/dt); 
    } 
    override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) { 
     node1.position = (touches.first! as! UITouch).locationInNode(self) 
    } 
} 

di seguito una GIF che mostra il moto centripeto. Si noti che poiché è completamente dinamico, possiamo effettivamente spostare il punto centripeto mentre è in orbita.

enter image description here


Se invece si vuole veramente utilizzare il SKJoints implementazione corrente per qualche ragione, allora ho un'altra soluzione per voi. Semplicemente continua ad aggiornare la velocità lineare del tuo nodo in modo che non rallenti mai.

override func update(currentTime: NSTimeInterval) { 
     let magnitude = sqrt(secondNode.physicsBody!.velocity.dx*secondNode.physicsBody!.velocity.dx+secondNode.physicsBody!.velocity.dy*secondNode.physicsBody!.velocity.dy) 
     let angle = Float(atan2(secondNode.physicsBody!.velocity.dy, secondNode.physicsBody!.velocity.dx)) 
     if magnitude < 500 { 
      secondNode.physicsBody!.velocity = CGVector(dx: CGFloat(cosf(angle)*500), dy: CGFloat(sinf(angle)*500)) 
     } 
    } 


Indipendentemente dalla soluzione si sceglie (e ancora una volta altamente consigliamo la prima soluzione!), È possibile scegliere di applicare la velocità nel corso del tempo, piuttosto che istantaneamente per un effetto più realistico. Ne parlo di più nella mia risposta here

Spero di averti fornito sufficienti informazioni per risolvere il tuo problema. Fatemi sapere se avete domande. E buona fortuna con il tuo gioco!

+0

Praticamente la risposta che mi aspettavo TBH, ma ho deliberatamente omesso qualsiasi concetto di questo sperando che ci fosse qualcosa di ovvio nella simulazione fisica che mi era sfuggita. Grazie! Hai qualche citazione o riferimento a suggerimenti di persone o consigli di non confidare troppo nei calcoli della fisica in quanto questa è una notizia per me e sembra troppo importante per non essere adeguatamente documentata ... –

+2

Ho avuto qualche problema con questo, fino a quando Mi sono reso conto che il dt, stava assumendo il frame rate di 60 FPS, se la frequenza del frame cambia, questi calcoli saranno spenti. Puoi risolvere questo problema rintracciando l'ora corrente e utilizzandola per calcolare la differenza: let deltaTime = currentTime - lastUpdateTime; let currentFPS = 1/deltaTime; lastUpdateTime = currentTime; let dt: CGFloat = 1.0/CGFloat (currentFPS) // Delta Time; – Jbryson

Problemi correlati