|
5 | 5 | * http://stregasgate.com |
6 | 6 | */ |
7 | 7 |
|
| 8 | +/// Assits in creating fixed step deltaTime values |
8 | 9 | internal struct DeltaTimeHelper { |
9 | 10 | internal let name: String |
10 | | - private var accumulator: Double = 0 |
11 | 11 | private var previousTime: Double = .nan |
12 | 12 |
|
13 | 13 | private static let preferredFrameRate: Int = Platform.current.prefferedFrameRate() |
14 | | - private static let simulationStepDuration: Double = 1 / Double(preferredFrameRate) / 2 |
| 14 | + private static let simulationStepDuration: Double = 1 / Double(preferredFrameRate) / 4.10 |
15 | 15 | private static let maxSimulationSteps: Int = 4 |
16 | 16 |
|
17 | 17 | internal init(name: String) { |
18 | 18 | self.name = name |
19 | 19 | self.reset() |
20 | 20 | } |
21 | 21 |
|
22 | | - mutating func reset() { |
23 | | - self.accumulator = 0 |
| 22 | + /** |
| 23 | + Revert to initial state. |
| 24 | + |
| 25 | + `DeltaTimeHelper` stores time to compute deltaTime. |
| 26 | + Use this function to clear the stored time when it is known to be stale. |
| 27 | + */ |
| 28 | + @inlinable mutating func reset() { |
24 | 29 | self.previousTime = .nan |
25 | 30 | } |
26 | 31 |
|
| 32 | + /** |
| 33 | + Computes a fixed step deltaTime based on the current platfo |
| 34 | + - returns A fixed step deltaTime, or nil if the number of steps was zero. |
| 35 | + */ |
27 | 36 | @MainActor |
28 | | - @inlinable |
29 | | - mutating func getDeltaTime() -> Double? { |
| 37 | + @inlinable mutating func getFixedStepDeltaTime() -> (steps: Int, deltaTime: Double)? { |
30 | 38 | let currentTime = Platform.current.systemTime() |
31 | 39 |
|
32 | 40 | // Determine the new accumulator value |
33 | | - let newAccumulator: Double = self.accumulator + (currentTime - self.previousTime) |
| 41 | + let literalDeltaTime: Double = currentTime - self.previousTime |
34 | 42 |
|
35 | 43 | // If our accumulated time is less than a single step, bail |
36 | | - if newAccumulator < Self.simulationStepDuration { |
| 44 | + if literalDeltaTime < Self.simulationStepDuration { |
37 | 45 | return nil |
38 | 46 | } |
39 | 47 |
|
40 | 48 | // Update previousTime if we get this far |
41 | 49 | self.previousTime = currentTime |
42 | 50 |
|
43 | 51 | // If our accumulated time is not a number, bail |
44 | | - if newAccumulator.isFinite == false { |
| 52 | + if literalDeltaTime.isFinite == false { |
45 | 53 | return nil |
46 | 54 | } |
47 | 55 |
|
48 | | - // Update the accumulator |
49 | | - self.accumulator = newAccumulator |
50 | | - |
51 | 56 | // The number of steps that currently fit in the accumulator |
52 | | - let stepsRequired = Int(accumulator / Self.simulationStepDuration) |
| 57 | + let stepsRequired = Int(literalDeltaTime / Self.simulationStepDuration) |
53 | 58 |
|
54 | | - // If stepsRequired is greater than maxSimulationSteps, return only that |
55 | | - // Erase the accumulator as this is likely a spike from a CPU hang |
| 59 | + // If stepsRequired is greater than maxSimulationSteps, then truncate. |
56 | 60 | if stepsRequired > Self.maxSimulationSteps { |
57 | | - // Anytime we return from here, the simulation will run slower. |
| 61 | + #if !DEBUG && !DISTRIBUTE |
| 62 | + // Anytime we return from here, the simulation is running slower. |
58 | 63 | // This is similar in severity to a dropped frame and will happen if the simulation is lagging. |
59 | 64 | // To reduce occurances of this, do less work. |
60 | | - // Try spreading complex tasks out across multiple updates. |
61 | | - let message = "\(name) deltaTime attempted to use \(stepsRequired) steps, and was truncated to \(Self.maxSimulationSteps)." |
62 | | - if stepsRequired > 15 { |
| 65 | + // Try spreading work out across multiple updates. |
| 66 | + let message = "\(name) needed \(stepsRequired) steps, and was truncated to \(Self.maxSimulationSteps)." |
| 67 | + if stepsRequired > Self.maxSimulationSteps * 2 { |
63 | 68 | Log.warn(message, "This is a \(String(format: "%0.3f", Self.simulationStepDuration * Double(stepsRequired))) second hang!") |
64 | | - }else{ |
65 | | - Log.debug(message, "This is a minor performance issue.") |
66 | 69 | } |
67 | | - |
68 | | - let maxSimulationSteps = Double(Self.maxSimulationSteps) |
69 | | - |
70 | | - // Zero the accumulator to give the next update the maximum time, becuase we just had a hang |
71 | | - self.accumulator = 0 |
| 70 | + #endif |
72 | 71 |
|
73 | 72 | // Return the maximum allowed deltaTime |
74 | | - return Self.simulationStepDuration * maxSimulationSteps |
| 73 | + return (Self.maxSimulationSteps, Self.simulationStepDuration) |
75 | 74 | } |
76 | | - |
77 | | - // Calculate fixed step deltaTime |
78 | | - let deltaTime = Self.simulationStepDuration * Double(stepsRequired) |
79 | | - |
80 | | - // Remove the used time from the accumulator |
81 | | - self.accumulator -= deltaTime |
82 | | - |
83 | | - return deltaTime |
| 75 | + |
| 76 | + return (stepsRequired, Self.simulationStepDuration) |
| 77 | + } |
| 78 | + |
| 79 | + /** |
| 80 | + Computes a fixed step deltaTime based on the current platfo |
| 81 | + - returns A fixed step deltaTime, or nil if the number of steps was zero. |
| 82 | + */ |
| 83 | + @MainActor |
| 84 | + @inlinable mutating func getDeltaTime() -> Double? { |
| 85 | + guard let fixedStepDeltaTime = self.getFixedStepDeltaTime() else {return nil} |
| 86 | + return fixedStepDeltaTime.deltaTime * Double(fixedStepDeltaTime.steps) |
84 | 87 | } |
85 | 88 | } |
0 commit comments