Timers in video game development are tools or mechanisms that allow measuring and controlling time within the game. They are used to manage events, actions, or behaviors that depend on time, whether in real-time or in game update cycles (frames).
They are a fundamental tool for controlling temporal logic in video games, from basic mechanics to complex systems, and are therefore essential in many aspects of game design and development.
There are many uses and cases for timers. Here are some of the most notable examples:
- Control of timed events: For example, executing actions after a specific time, such as triggering an animation, spawning enemies, or displaying a message on screen.
- Time-based game mechanics: For instance, implementing time limits to complete a mission, solve a puzzle, or survive a wave of enemies.
- Synchronization of animations and effects: To coordinate animations to occur at the right moment, such as explosions, transitions, or visual effects.
- Cooldown management: To control the cooldown time of abilities, special attacks, weapon reloads, or player actions.
- System update frequency: To regulate how often certain systems (like enemy AI or item generation) update to optimize performance.
- Cyclic or repetitive events: To create patterns that repeat at regular intervals, such as enemy waves, weather changes, or day/night cycles.
- Multiplayer synchronization: Used to ensure that events in multiplayer games are synchronized across all clients, such as match start or state updates.
- Delay or wait effects: Introduce intentional pauses to improve gameplay or narrative, such as waiting before showing dialogue or triggering an event.
Based on the above, we can distinguish the following types of timers:
- Game-time based: Progresses at the same rate as game time. For example, if the game is slowed down (e.g., bullet time), the timer slows down accordingly.
- Real-time based: Uses the system clock and progresses uniformly regardless of game time speed.
- Countdown: Starts with a value and decreases to zero, triggering an event when finished.
- Cyclic timers: Automatically restart for repetitive events, like automatic shooting or health regeneration.
General Implementation
All engines offer a function that executes periodically. Unreal calls it Tick(), Unity uses Update(), and Godot C# uses _Process(). These functions usually receive a value called deltaTime, which is the time elapsed since the last call.
Assuming we call this function Update(), we could use it to create a timer with the following pseudocode:
Pseudocode to implement a timer |
Many engines offer two versions of this periodic function: one that runs when a new frame is rendered (idle frame), and another that runs at fixed time intervals (physics frame). The latter is better for implementing timers, as graphics cards do not render frames at a constant rate.
Implementation in Godot
Following its "a node for each task" philosophy, Godot has a specialized timer node called Timer. This node is ideal for using timers without implementing them from scratch.
The node offers the following configuration fields:
![]() |
Timer node fields |
- Process Callback: Defines whether the timer progresses with idle frames or physics frames.
- Wait Time: Duration of the timer in seconds.
- One Shot: If checked, the timer runs once and stops. Otherwise, it restarts automatically.
- AutoStart: If checked, the timer starts when added to the scene tree. Otherwise, it must be started manually.
- Ignore Time Scale: If checked, the timer uses real time; otherwise, it uses game time.
The node emits the timeout() signal when the timer reaches the set Wait Time.
![]() |
timeout signal |
By configuring the Timer node in the inspector and connecting its timeout() signal to a method, most use cases are covered.
This node can also be manipulated from code. Let’s look at an example of how to use it. Imagine we want to program a cooldown, such that once a certain method is executed, it cannot be executed again until the timer has finished. Let’s suppose this method is called GetSteering() and it’s called from the _Process() method of another node.
To start the cooldown, we could call an activation function right at the end of the GetSteering() method.
![]() |
Example of starting a cooldown |
The cooldown activation function is called StartAvoidanceTimer() in this case, and it simply starts the Timer node’s countdown and sets a flag to true:
![]() |
Implementation of the cooldown start method |
If a Timer does not have the AutoStart field enabled, we’ll need to call its Start() method for it to work, as seen on line 235.
The flag set to true on line 236 can be used as a guard at the beginning of the method we want to limit execution of.
![]() |
Using the flag as a guard to control whether the method executes |
The example in the screenshot might be a bit convoluted if you’re not familiar with the development context in which the method is used, but it essentially works by calculating a path to a target (line 157). If there’s no other agent we might collide with, and no timer is active, the calculated path is followed (line 160). If, on the other hand, a timer is running, the agent continues along the escape route calculated in the previous call to GetSteering() (line 162). The method is only fully executed if a potential collision is detected and no timer is active, in which case it continues calculating an escape route to avoid the collision (from line 164 onward).
Without a cooldown, my agent would detect a possible collision ahead, calculate an escape route, and turn to avoid the collision. The problem is that in the next frame, the obstacle would no longer be in front of it, so it would see the shortest path to its target and turn to face it again, ending up right back in front of the obstacle we were trying to avoid. This cycle would repeat. To prevent this, an escape route is calculated and followed for a set time (the cooldown) so the agent moves significantly away from the obstacle, avoiding the collision; only then is the path to the target reevaluated.
The Timer configuration can be done from the _Ready() method of the node that owns the Timer.
![]() |
Configuring a Timer in the _Ready method |
Ignore the lines before line 83, they have nothing to do with the Timer, but I’ve left them so you can see a real example of Timer usage.
In line 83, I load a reference to the Timer node that is a child of the node executing this script. With that reference, we could configure all the fields available in the Timer’s inspector. In my case, it wasn’t necessary because I didn’t need to change the Timer’s configuration already set in the inspector, but I did manually connect a method to the Timer’s signal (line 84). In this case, the connected method is OnAvoidanceTimeout(). I could have connected it using the "Node" tab in the inspector, but I preferred to do it via code.
The OnAvoidanceTimeout() method couldn’t be simpler, as it just sets the flag to false, so that in the next frame the GetSteering() method knows there are no active timers.
![]() |
Callback connected to the Timer signal |
Implementation in Unity
Unity doesn’t have a specialized component like Godot’s Timer node, but creating one is very easy using coroutines.
Let’s see how we could create a component that emulates the functionality provided by Godot’s node. We could call our component CustomTimer:
![]() |
Start of our Timer class for Unity |
The fields this component could offer would be the same as those of the aforementioned node.
![]() |
Class fields exposed to the inspector |
With this, what we’d see in the inspector would be the following:
![]() |
Inspector view of our CustomTimer class |
If autoStart is set to true, the component would simply call the StartTimer() method from Awake(), as soon as the script starts.
![]() |
Timer start |
The method called on line 50 of the Awake method simply starts a coroutine and stores a reference to it so it can be controlled externally (line 55).
The body of the launched coroutine is very simple:
![]() |
Coroutine body of the Timer |
The coroutine simply waits for the number of seconds set in waitTime. Depending on the type of time we want to use (game time or real time), it waits for game seconds (line 65) or real-time seconds (line 68).
After that time, the timeout event is triggered (line 72).
If the timer is single-use, the coroutine ends (line 74); otherwise, the cycle repeats (line 60).
What’s the purpose of the reference we stored on line 55 of the StartTimer() method? It allows us to stop the coroutine early if needed, using the StopCoroutine method.
![]() |
Code for early stopping of the timer |
A GameObject with a CustomTimer component like the one described could use it from another GameObject’s script by getting a reference to the component:
![]() |
Getting a reference to CustomTimer |
From that reference, using the Timer would be identical to what we saw with Godot’s node.
![]() |
Using the CustomTimer component |
Since this example is the Unity version of what we already saw for Godot, I won’t go into more detail.
Implementation in C#
What do Godot C# and Unity have in common? Exactly, their programming language: C#. This is a general-purpose language and comes “batteries included,” meaning it has lots of modules and libraries for doing many things. One of those modules offers a timer with very similar functionality to what we’ve already seen.
If C# already offers a timer, why is it so common to see implementations like the ones above in Godot and Unity? Why not use the C# timer directly?
I think the simplest answer is for the case of Godot. Godot’s default language is GDScript, and it doesn’t come “batteries included,” or rather, its “batteries” are the thousands of nodes Godot offers. That’s why Godot C# inherits access to the Timer node thanks to its availability in GDScript code.
The Unity case is harder to answer, and I think it’s due to a lack of awareness of what C# offers on its own. Also, we’ve seen that creating a custom timer is very easy using a mechanism as familiar to Unity developers as coroutines. I think that ease of creating your own timer has led many to never explore what C# offers in this regard.
The timer C# offers is System.Timers.Timer, and its usage is very similar to what we’ve seen in Unity and Godot.
![]() |
Using C#’s native timer |
In line 103 of the screenshot, you can see that you need to pass the desired duration to its constructor.
The AutoReset field on line 105 is completely equivalent to Godot’s Autostart.
In line 106, you can see that the event this timer triggers is Elapsed (equivalent to Godot’s timeout signal). You can subscribe callbacks (like OnTimerTimeout) to this event just like we did in the previous versions for Godot and Unity.
Finally, this C# timer also has Start() (line 117) and Stop() methods to start and stop it.
The only precaution to take with this timer is that you should never call its Start() method unless you’re sure the previous countdown has finished. If you need to start a new countdown before the previous one ends, you must first call Stop() and then Start(). The issue is that this timer is optimized for multithreaded environments, so when you call multiple Start()s in a row, it doesn’t finish the previous countdowns but accumulates them in different threads, causing the countdowns to run in parallel and the events to trigger as those countdowns finish (which may appear as irregular timing due to being from different countdowns).
Conclusion
If you program in C# in both Godot and Unity, there are very few reasons not to use the C# timer. It’s lightweight, efficient, and being cross-platform, it allows you to reuse code between Unity and Godot more easily.
The only reason I can think of to keep using Godot’s node is for manipulation from the inspector when integrating with other game elements; but beyond that, I don’t see much use in continuing to use either Godot’s node or Unity’s coroutine-based timers.