Drag Turn a Node around a fixed point

I am trying to create a spableable node similar to the "drive" in this question . So far I have the opportunity by adding angular pulses in the physical body using the UIPanGestureRecognizer, which works very well. I can also stop the rotation by touch.

Now I’m trying to enable fine tuning of the wheel with drag and swipe gestures, so if a player is not happy with what he’s doing, they can manually rotate / drag / rotate it to their favorite rotation.

I am currently saving the touch location in touchhesBegan and trying to increase the zRotation of my node in the update loop.

The rotation does not follow my finger and twitches. I'm not sure if I get enough accurate data about the movement of the finger or if the position of the finger changes will not be accurately converted to radians. I suspect that I discovered a touch, and then contacting him in an update is not a great solution.

Here is my code.

override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent?) { if let touch = touches.first as? UITouch { var location = touch.locationInView(self.view) location = self.convertPointFromView(location) mostRecentTouchLocation = location let node = nodeAtPoint(location) if node.name == Optional("left") && node.physicsBody?.angularVelocity != 0 { node.physicsBody = SKPhysicsBody(circleOfRadius:150) node.physicsBody?.applyAngularImpulse(0) node.physicsBody?.pinned = true } } } override func update(currentTime: CFTimeInterval) { /* Called before each frame is rendered */ if mostRecentTouchLocation != CGPointZero{ let node = nodeAtPoint(mostRecentTouchLocation) if node.name == Optional("left") { var positionInScene:CGPoint = mostRecentTouchLocation let deltaX:Float = Float(positionInScene.x) - Float(node.position.x) let deltaY:Float = Float(positionInScene.y) - Float(node.position.y) let angle:CGFloat = CGFloat(atan2f(deltaY, deltaX)) let maths:CGFloat = angle - (CGFloat(90) * (CGFloat(M_PI) / 180.0)) node.zRotation += maths mostRecentTouchLocation = CGPointZero } } } 

I have distributed some math data across multiple lines in the update to facilitate debugging.

I can add the PanGestureRecognizer code if necessary, but I will try to make it short for now.

EDIT Here is my latest code based on the GilderMan recommendation. I think it works better, but the rotation is far from smooth. He jumps in large increments and does not follow his finger well. Does this mean that something is wrong with my angle calculation?

  override func didSimulatePhysics() { if mostRecentTouchLocation != CGPointZero { let node = nodeAtPoint(mostRecentTouchLocation) if node.name == Optional("left") { var positionInScene:CGPoint = mostRecentTouchLocation let deltaX:Float = Float(positionInScene.x) - Float(node.position.x) let deltaY:Float = Float(positionInScene.y) - Float(node.position.y) let angle:CGFloat = CGFloat(atan2f(deltaY, deltaX)) node.zRotation += angle println(angle) mostRecentTouchLocation = CGPointZero } } } 
+7
swift sprite-kit
source share
2 answers

The following code simulates a prize wheel that rotates based on a touch. When the user's finger moves, the wheel rotates in proportion to the speed of the finger. When the user clicks on the wheel, the wheel will rotate in proportion to the speed of the swipe. You can change the angularDamping property of the physical body to slow down or increase the speed at which the wheel stops.

 class GameScene: SKScene { var startingAngle:CGFloat? var startingTime:TimeInterval? override func didMove(to view: SKView) { let wheel = SKSpriteNode(imageNamed: "Spaceship") wheel.name = "wheel" wheel.setScale(0.5) wheel.physicsBody = SKPhysicsBody(circleOfRadius: wheel.size.width/2) // Change this property as needed (increase it to slow faster) wheel.physicsBody!.angularDamping = 0.25 wheel.physicsBody?.pinned = true wheel.physicsBody?.affectedByGravity = false addChild(wheel) } override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { for touch in touches { let location = touch.location(in:self) let node = atPoint(location) if node.name == "wheel" { let dx = location.x - node.position.x let dy = location.y - node.position.y // Store angle and current time startingAngle = atan2(dy, dx) startingTime = touch.timestamp node.physicsBody?.angularVelocity = 0 } } } override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { for touch in touches{ let location = touch.location(in:self) let node = atPoint(location) if node.name == "wheel" { let dx = location.x - node.position.x let dy = location.y - node.position.y let angle = atan2(dy, dx) // Calculate angular velocity; handle wrap at pi/-pi var deltaAngle = angle - startingAngle! if abs(deltaAngle) > CGFloat.pi { if (deltaAngle > 0) { deltaAngle = deltaAngle - CGFloat.pi * 2 } else { deltaAngle = deltaAngle + CGFloat.pi * 2 } } let dt = CGFloat(touch.timestamp - startingTime!) let velocity = deltaAngle / dt node.physicsBody?.angularVelocity = velocity // Update angle and time startingAngle = angle startingTime = touch.timestamp } } } override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { startingAngle = nil startingTime = nil } } 
+7
source share

zRotation of SKNode is in radians. You can remove your conversion to degrees.

You can adjust the angle in didSimulatePhysics to calculate zRotation after applying physics. (This may not apply directly to this situation, but it is good practice down the line.)

0
source share

All Articles