Reputation: 454
I'm using Swift 3.0 and Xcode 8.2.1, testing on an iPhone 6s running iOS 10.2.
I'm simply attempting to rotate an SKCameraNode at a rate that is slowing down (I.E. it has both an angular velocity and acceleration).
This is my current solution, and yes I know it's weird:
cam.run(
SKAction.repeatForever(
SKAction.sequence([
SKAction.run
{
self.cam.run(SKAction.rotate(toAngle: 0.0, duration: 0.7, shortestUnitArc: true))
},
SKAction.wait(forDuration: 0.1)])))
The camera's zRotation starts at some non-zero place, and then this is called. It then calls the SKAction.rotate part every 0.1 seconds. It's not shown here, but I have it terminate after 3 seconds.
This provides the exact effect that I want, because every time the SKAction is called, the zRotation is closer to its 0.0, but the time it needs to take to get there stays at 0.7, so the rate at which it approaches 0.0 slows down.
HOWEVER, 1 of 3 things happen from this:
I tried reducing the waitForDuration from 0.1 to 0.01 but then it always does option 3.
Is there some sort of "rules of execution" that I'm not following when it comes to using cameras?
Thanks!
Upvotes: 0
Views: 178
Reputation: 12753
You can use a single rotate(toAngle:)
action that decelerates when the camera's zRotation
is close to the ending angle and setting the timingMode
property to .easeOut
. For example,
cam.zRotation = CGFloat.pi
let action = SKAction.rotate(toAngle: 0, duration: duration, shortestUnitArc: true)
// Slows when zRotation is near 0
action.timingMode = .easeOut
cam.run(action)
Alternatively, you can define your own timing function to customize the ease-out behavior by replacing timingMode
statement with
action.timingFunction = {
time in
// Custom ease-out, modify this as needed
return 1-pow(1-time, 5)
}
Moreover, you can use the following to calculate the action's duration so the angular velocity and acceleration are consistent regardless of the starting angle (i.e., the amount of rotation)
let duration = TimeInterval(normalizedArcFromZero(angle:cam.zRotation)) * 3
func normalizedArcFromZero(angle:CGFloat) -> CGFloat {
let diff = angle.mod(dividingBy:CGFloat.pi*2)
let arc = CGFloat.pi - (diff + CGFloat.pi).mod(dividingBy:2 * CGFloat.pi)
return abs(arc)/CGFloat.pi
}
Lastly, since the above requires a modulo function that performs a "floored" division (instead of Swift's truncatingRemainder
), you'll need to add this to your project
extension CGFloat {
func mod(dividingBy x:CGFloat) -> CGFloat {
return self - floor(self/x) * x
}
}
Upvotes: 2