6. Time System
Every smooth animation you've seen in a game, health bars filling up, characters sliding into position, even day transitioning to night, relies on simple mathematical formulas. These formulas require some basic inputs to work.
For example, we probably want to increment the time of day every update. That means we must know how much time passed between each update.
If we do that on a per-frame basis, we can use what's commonly called "Delta Time".
The Fractal Code Cycle Applied to the Time System
Recall our fundamental pattern:
Input → Processing → Output
For our time system:
- Input: Timing information from the OS (or Raylib, in our case)
- Processing: Set delta time, update time of date, update any timers
- Output: Time state changed, timers fired events if completed
What's Time Good For? Some Examples
Interpolation
Interpolation can be thought of as sliding something between two values. The most common form of interpolation, that you may have used yourself, is linear interpolation.
Lerp, or linear interpolation is a commonly used algorithm to smoothly transition between two values. The formula is simple:
// t == time between 0.0 and 1.0
// a == start value
// b == end value
x = a + (b - a) * t
For example, let's say we want a button to move up and left slightly when the cursor hovers over it. We can fill in these parameters and determine the position the button should be at.
get_button_position :: proc(start, end: Vec2, time: f32) {
return start + (end - start) * t
}
start :: Vec2{0.0, 0.0}
end :: Vec2{-5.0, -5.0}
get_button_position(start, end, 0.0) // => {0.0, 0.0}
get_button_position(start, end, 1.0) // => {-5.0, -5.0}
get_button_position(start, end, 0.5) // => {-2.5, -2.5}
In order to get the t value between 0.0 and 1.0, we must know the animation's length, and either how long it's been running, or what time it started as well as the current time.
Lerp has uses other than animations. A simple one is replacing
twithpercentage between- if you want the midpoint between two values, use0.5, if you want to place something 75% the way along a line between two points, use0.75.
Timers
Another example of what our time system may be responsible for are Timers.
We can create a simple Timer struct, such as this:
Timer :: struct {
remaining_seconds: f32,
event: Event,
}
When the timer expires, the event is fired (added to the global event queue - we'll cover this in the next lesson).
Day/Night Cycle
Another example of something we want time for: the day/night cycle and NPC schedules. If they NPCs can't tell what time it is, then we can't have a schedule system - at least not a time-based one.
And, many more...
Time System in the System Stack
Recall our System Stack organisation:
- Base Layer: Entities, Input, Time, Assets, Save/Load
- Presentation: Camera, Shaders, Animation, UI, Particles
- Interaction: Player, Collisions, AI, Scenes
- Game Systems: Stats, Combat, Inventory, Abilities
- Content: Dialogue, Quests, NPC Schedules
The Time system sits in the base layer because:
- It has minimal dependencies (just Raylib or OS calls)
- Many higher-level systems depend on it
- A lot of systems depend on timing
Time System Design Considerations
What period should time be stored in? Seconds? Nanoseconds?
I've chosen seconds to keep things simple, and because a f64 worth of seconds is such a long time (over 200 million years) that we don't need to worry about it.
The secondary reason is because we'll be doing a lot of maths with time, and being able to easily cast to f32 is a nice-to-have.
Try It Yourself
- What other types and formats could be used for storing time?
- How would you implement your own timing system if not using Raylib?
The Big Idea
Time is the force allowing us to witness change in the universe. The same is true for our game, being a part of our universe.
Keeping a simple, yet robust set of variables around time allows us to perform any required calculations to "make things happen".
- Animations
- Timed events
- Time of Day
Even things the player won't see:
- Invisible timers
- Task scheduling (performance)
Think of the Time System like a driver that moves everything forward