Skip to content

Commit

Permalink
Fixup the timer module
Browse files Browse the repository at this point in the history
As mentioned in a few commit comments and issue zacharycarter#21, the first round of
the timer module looked a little wrong (e.g. it was giving us
milliseconds for `deltaTime()` when it should have been giving us
seconds instead).  This commit fixes that and removes a little cruft.

Under the hood all of the times are measured in hard integers, but the
programmer is given a floating point interface to using the time.

On top of that, the timer module now has a `Timer` object.  The
rationale for this is two fold:

1. This makes the `Timer` less tied into the engine internals (i.e.
independant).
2. Users may need timers for other things (e.g. spell cooldowns, time
trials in racing scenarios, making a day n' night cycle; the list goes
on).  Before we only had one global timer.  Now we've got many that the
user can use (and resuse).

I've also added some documentation better explaining things.  (I should
maybe add some drawings too).

closes zacharycarter#21
  • Loading branch information
define-private-public committed Sep 9, 2017
1 parent ca3e1c0 commit 78b5d65
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 37 deletions.
4 changes: 1 addition & 3 deletions src/zengine/core.nim
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import text, logging, sdl2, sdl2.image as sdl_image, sdl2.ttf as sdl_ttf, opengl, zgl, math, glm, timer
import text, logging, sdl2, sdl2.image as sdl_image, sdl2.ttf as sdl_ttf, opengl, zgl, math, glm


var
Expand Down Expand Up @@ -69,8 +69,6 @@ proc init*(width, height: int, mainWindowTitle: string) =

currentKeyboardState = sdl2.getKeyboardState()

timer.init()

proc begin2dMode*(camera: Camera2D) =
zglDraw()

Expand Down
106 changes: 72 additions & 34 deletions src/zengine/timer.nim
Original file line number Diff line number Diff line change
@@ -1,36 +1,74 @@
import sdl2

var
startTime, currentTime, previousTime, updateTime: float64
ticks: uint
frequency: float64

# Returns total number of ticks the timer has endured
proc totalTicks*() : uint {.inline.} =
ticks

# Returns the time elapsed between the current frame
# and previous frame
proc deltaTime*() : float64 {.inline.} =
updateTime

# Returns the time elapsed since application start
proc timeElapsed*() : float64 {.inline.} =
currentTime - startTime

# Returns the current time
proc time*() : float64 {.inline.} =
float64(sdl2.getPerformanceCounter()*1000) / frequency

# Ticks the timer
proc tick*() {.inline.} =
currentTime = time()
updateTime = currentTime - previousTime
previousTime = currentTime
inc(ticks)

# Initializes the timer
proc init*() =
frequency = float64 sdl2.getPerformanceFrequency()
startTime = time()
currentTime = startTime

# The `Timer` object is what is used to measure time and differences in time.
# This is necessary for things like Animation, frame independant movement,
# lifetimes, cooldowns, and many more things. The general idea of this is that
# you create a Timer, start it up, tick it forward and then grab the difference
# in time from the previous tick to the next.
#
# Within your game (loop) there should be at least one timer. It should look
# something like this:
#
# ```
# # Create and start the clock
# var clock = Timer()
# clock.start()
# while running:
#
# # Update game & render logic
# update(clock.deltaTime())
# render(clock.deltaTime())
#
# # Tick forward
# clock.tick()
# ```
#
# I'd recommend reading the documentation for the `start()`, `tick()`, and
# `deltaTime()` procs to get a better idea how they work.
type Timer* = object
ticks: uint64 # How many times the timer has "ticked,"
timeSinceLastTick: float64 # Amount of time (in seconds) since the last tick
startTime, currentTime, previousTime: uint64 # What actually measures the time under the hood


# How many "time units," from SDL is one second (caching the function's return)
let frequency: float64 = sdl2.getPerformanceFrequency().float64


# Starts the timer. You can call this multiple times to reset it.
proc start*(self: var Timer) =
self.startTime = sdl2.getPerformanceCounter()
self.previousTime = self.startTime
self.currentTime = self.startTime


# Returns total number of ticks the Timer has endured. You will probably not
# need to use this at all.
proc totalTicks*(self: Timer) : uint64 {.inline.} =
self.ticks


# Returns the time elapsed between the current tick and the previous tick. This
# value will not chance until you call `tick` again. Say if your game is
# running at 100 logical updates per second, this would return a value around
# `0.01`. If it was logically updating at 60 FPS, it would be more like
# `0.016667`.
# Return value is in seconds.
proc deltaTime*(self: Timer) : float64 {.inline.} =
self.timeSinceLastTick


# Returns the time elapsed since `Timer.init()` has been called. Return value
# is in seconds.
proc timeElapsed*(self: Timer) : float64 {.inline.} =
(self.currentTime - self.startTime).float64 / frequency


# Ticks the timer forward.
proc tick*(self: var Timer) {.inline.} =
self.currentTime = sdl2.getPerformanceCounter()
self.timeSinceLastTick = (self.currentTime - self.previousTime).float64 / frequency
self.previousTime = self.currentTime
self.ticks.inc()

0 comments on commit 78b5d65

Please sign in to comment.