How to Properly Calculate 1 Second with Deltatime in Swift

How to properly calculate 1 second with deltaTime in Swift

I always use SKActions for this type of thing: (written in swift 3)

let wait = SKAction.wait(forDuration: 1.0)
let spawnSomething = SKAction.run {
//code to spawn whatever you need
}

let repeatSpawnAction = SKAction.repeatForever(SKAction.sequence([wait, spawnSomething]))

self.run(repeatSpawnAction)

Calculating delta in SpriteKit using Swift

This question belongs to codereview. But I just post answer here and hope it will be migrate to the correct place along with the question.

You have some redundant code, this is my first iteration re-write

class GameScene: SKScene {
var lastUpdateTimeInterval: CFTimeInterval?

override func update(currentTime: CFTimeInterval) {

var delta: CFTimeInterval = currentTime // no reason to make it optional
if let luti = lastUpdateTimeInterval {
delta = currentTime - luti
}

lastUpdateTimeInterval = currentTime

if delta > 1.0 {
delta = minTimeInterval
// this line is redundant lastUpdateTimeInterval = currentTime
}

updateWithTimeSinceLastUpdate(delta)
}
}

and further simplified

class GameScene: SKScene {
var lastUpdateTimeInterval: CFTimeInterval = 0

override func update(currentTime: CFTimeInterval) {

var delta: CFTimeInterval = currentTime - lastUpdateTimeInterval

lastUpdateTimeInterval = currentTime

if delta > 1.0 {
delta = minTimeInterval
}

updateWithTimeSinceLastUpdate(delta)
}
}

You can replace the if with ?:, but some people just hate it for some reason

updateWithTimeSinceLastUpdate(delta > 1.0 ? minTimeInterval : delta)

Measure elapsed time in Swift

Update

With Swift 5.7, I think everything below becomes obsolete. Swift 5.7 introduces the concept of a Clock which appears to have a function designed to do exactly what is required here.

I'll update with an example as soon as I've got Swift 5.7 and have the time to rework it.


Here's a Swift function I wrote to measure Project Euler problems in Swift

As of Swift 3, there is now a version of Grand Central Dispatch that is "swiftified". So the correct answer is probably to use the DispatchTime API.

My function would look something like:

// Swift 3
func evaluateProblem(problemNumber: Int, problemBlock: () -> Int) -> Answer
{
print("Evaluating problem \(problemNumber)")

let start = DispatchTime.now() // <<<<<<<<<< Start time
let myGuess = problemBlock()
let end = DispatchTime.now() // <<<<<<<<<< end time

let theAnswer = self.checkAnswer(answerNum: "\(problemNumber)", guess: myGuess)

let nanoTime = end.uptimeNanoseconds - start.uptimeNanoseconds // <<<<< Difference in nano seconds (UInt64)
let timeInterval = Double(nanoTime) / 1_000_000_000 // Technically could overflow for long running tests

print("Time to evaluate problem \(problemNumber): \(timeInterval) seconds")
return theAnswer
}


Old answer

For Swift 1 and 2, my function uses NSDate:

// Swift 1
func evaluateProblem(problemNumber: Int, problemBlock: () -> Int) -> Answer
{
println("Evaluating problem \(problemNumber)")

let start = NSDate() // <<<<<<<<<< Start time
let myGuess = problemBlock()
let end = NSDate() // <<<<<<<<<< end time

let theAnswer = self.checkAnswer(answerNum: "\(problemNumber)", guess: myGuess)

let timeInterval: Double = end.timeIntervalSinceDate(start) // <<<<< Difference in seconds (double)

println("Time to evaluate problem \(problemNumber): \(timeInterval) seconds")
return theAnswer
}

Note that using NSdate for timing functions is discouraged: "The system time may decrease due to synchronization with external time references or due to an explicit user change of the clock.".

Can you apply delta time to an SKAction

You cannot do much about your FPS slowing down as you add more nodes to your view. Which device is running your app also determines your FPS. Newer models have a much faster CPU.

To use delta time you can do something like this:

-(void)update:(NSTimeInterval) currentTime {
NSTimeInterval delta = currentTime - self.lastUpdateTime;
self.lastUpdateTime = currentTime;
// use the delta time to determine how much your sprites need to move to stay in sync.
}

If you are looking for a Swift version of the code, look at this previous Q&A here.

You cannot slow down or speed up an SKAction in mid run. To adjust speed of movement you will have to move your node manually by either applying a physics force such as CGVectorMake or by changing its x,y positions.


Add this property:

@property (nonatomic) NSTimeInterval lastUpdateTime;

Then in your update method:

-(void)update:(CFTimeInterval)currentTime {

NSTimeInterval delta = currentTime - self.lastUpdateTime;
self.lastUpdateTime = currentTime;

// sanity check
if(delta > 1)
delta = 0;

float distanceToMovePerSecond = 5.0f;
float numberOfFramesPerSecond = 60.0f;

float xPosition = ((distanceToMovePerSecond/numberOfFramesPerSecond) * delta) * 100;

myNode0.position = CGPointMake(myNode0.position.x+xPosition, myNode0.position.y);

}

UIView Delta Time

You want to record the time you last rotated, and the difference in time between then and now, and use that to work out a factor, which you can use to adjust the rotation and x/y values.

for example:

NSDate now = [NSDate now];
timeDiff = now - lastRotateTime;
factor = timeDiff / expectedTimeDiff;

x = x + xIncrement * factor;
y = y + yIncrement * factor;

angle = angle + angleIncrement * factor;

There are many better examples on game dev forums, which explain it in more detail.

how to get the delta of swipe/ draging touch

You can detect swipe gestures using the built-in touch handlers of SpriteKit or you can implement a UISwipeGestureRecognizer. The following is an example of how to detect swipe gestures using SpriteKit's touch handlers:

First, define the variables and constants...

Define the starting point and time of the initial touch.

var touchStart: CGPoint?
var startTime : TimeInterval?

Define constants that specify the characteristics of the swipe gesture. By changing these constants, you can detect the difference between a swipe, drag, or flick.

let minSpeed:CGFloat = 1000
let maxSpeed:CGFloat = 5000
let minDistance:CGFloat = 25
let minDuration:TimeInterval = 0.1

In touchesBegan, store the starting point and time of the initial touch

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
touchStart = touches.first?.location(in: self)
startTime = touches.first?.timestamp
}

In touchesEnded, calculate the gesture's distance, duration, and speed. Compare these value against the constants to determine if the gesture was a swipe.

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touchStart = self.touchStart else {
return
}
guard let startTime = self.startTime else {
return
}
guard let location = touches.first?.location(in: self) else {
return
}
guard let time = touches.first?.timestamp else {
return
}
var dx = location.x - touchStart.x
var dy = location.y - touchStart.y
// Distance of the gesture
let distance = sqrt(dx*dx+dy*dy)
if distance >= minDistance {
// Duration of the gesture
let deltaTime = time - startTime
if deltaTime > minDuration {
// Speed of the gesture
let speed = distance / CGFloat(deltaTime)
if speed >= minSpeed && speed <= maxSpeed {
// Normalize by distance to obtain unit vector
dx /= distance
dy /= distance
// Swipe detected
print("Swipe detected with speed = \(speed) and direction (\(dx), \(dy)")
}
}
}
// Reset variables
touchStart = nil
startTime = nil
}

Swift: Checking progress on SKActions

As long as the speed of the sprite is constant (AKA no bullet time effect) I would just use a dictionary to store starting times:

var startingTimes = [SKNode:NSTimeInterval]()

Then store when a sprite starts his sequence

sprite1.run(SKAction.sequence(sequence))
startingTimes[sprite1 as! SKNode] = currentTime

Finally, sort by the startingTime

let sortedStartingTimes = startingTimes.sort(){
(time1,time2)->Bool in
return time1 <= time2
}

Then, just iterate through the dictionary, or grab the first item, whatever you need to do here.

This can be popped into playgrounds to test the above code:

var startingTimes = [String:NSTimeInterval]()
startingTimes["A"] = 1
startingTimes["C"] = 3
startingTimes["B"] = 2

let sortedStartingTimes = startingTimes.sort(){
(time1,time2)->Bool in
return time1 <= time2
}
print("start")
for time in startingTimes
{
print(time)
}
print("sort")
for time in sortedStartingTimes
{
print(time)
}

To get the percentage of completion is a little more complex. You need to take your currentTime - startingTime, then figure out based on this where you are at in your sprite.

So I have a total of 3 steps, each 1 second.

My currentTime - startingTime is 1.5 seconds.

var deltaTime = currentTime - startingTime

I take 1.5 - step 1 time, so result is .5 seconds

deltaTime -= step1Time

deltaTime > 0, I am in a new step

I take .5 - step 2 time, so result is .5 seconds

deltaTime -= step2Time

deltaTime <= 0 I am in this step

so deltaTime / step2time , that is my percentage in the step2 timeline

  let completionPercentage = deltaTime / step2Time


Related Topics



Leave a reply



Submit