Constant orbit speed around a point using SKNode

My goal is to have secondbody 'orbit' firstBody at a constant speed (500 in this case).

Based on Continuous Moving in SpriteKit I implemented the following:

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

The problem I am facing is that secondNode slows down over time. You will notice that I set all things like gravity to SKPhysicsWorld , friction linearDamping and angularDamping to SKPhysicsBody and frictionTorque to SKPhysicsJoint .

So what am I doing wrong? And how can I keep the secondNode constant constant without doing terrible calculations in -update ?

Also - I know that I can add SKAction to follow a circular path, but this is not a reasonable solution in this case.

If there is something simple that I am missing, can you also find out which of the "0" and "false" that I set can be deleted.

thanks

+6
source share
1 answer

When working with physical engines, deceleration is often expected. They are not ideal, especially in more complex scenarios, such as restrictions. You should not rely on the Sprite Kit to provide perfect simulation of continuous movement, because we do not know what the physical engine does under the hood; so many factors.

In cases where you need continuous movement (especially something like constant centripetal movement), it is always best to calculate and save these values โ€‹โ€‹yourself. It just simply fails to perform the calculations in your update method for these types of behavior.

So, I wrote a quick sample project that calculates the necessary speed necessary for the orbit of a certain point. You simply indicate the period, the position of the orbit, and the radius of the orbit. Note that since I am calculating everything, there is no need for SKJoints , so this implementation will also be easier. I highly recommend you make your orbit this way, as it gives you complete control over how you want your nodes to rotate around each other (i.e. you could have nodal orbital moving nodes, you could have oval motion paths, you could adjust the orbit while pressing the key moments in your game, etc.)

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

Below is a gif showing centripetal movement. Note that since it is fully dynamic, we can actually move the centripetal point as it rotates.

enter image description here


If you really want to use the current SKJoints implementation for some reason, then I have another solution for you. Just keep updating the linear speed of the node so that it never slows down.

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


Regardless of the solution you choose (and again I highly recommend the first solution!), You can use speed over time, rather than instantly, for a more realistic effect. I will talk more about this in my answer here.

I hope I have provided you with enough information to solve your problem. Let me know if you have any questions. And lucky with your game!

+7
source

Source: https://habr.com/ru/post/983156/


All Articles