I'm trying to create a spinable node similar to the "prize wheel" in this question. So far I have the flinging capability, adding angular impulses on a physics body using a UIPanGestureRecognizer that works really well. I can also stop the spinning using a touch.
Now I'm trying to allow fine adjustment of the wheel using a drag or swipe gesture so if the player isn't happy with what they end up with they can manually spin/drag/rotate it to their favoured rotation.
Currently I save the location of the touch in the touchesBegan and try to increment the zRotation of my node in the update loop.
The rotation doesn't follow my finger and is jerky. I'm not sure if I'm getting an accurate enough read on the finger movement or if the change position of the finger isn't being accurately translated into radians. I suspect detecting the touch and then dealing with it in the update isn't a great solution.
Here's 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've spread some of the maths across multiple lines in the update to make debugging a bit easier.
I can add the PanGestureRecognizer code if needed but I'll try to keep it short for now.
EDIT Here is my latest code based on GilderMan's recommendation. I think it's working better but the rotation is far from smooth. It's jumping in large increments and not following the finger well. Does this mean there's something 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
}
}
}
The following code simulates a prize wheel that spins based on touch. As the user's finger moves, the wheel rotates proportionately to the speed of the finger. When the user swipes on the wheel, the wheel will spin proportionately to the velocity of the swipe. You can change the angularDamping
property of the physics body to slow or increase the rate at which the wheel comes to a stop.
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
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With