A common thing in game development is "do something after X seconds". Most game engines make this task trivial:
bool canGetHit = true;
void OnGetHit() {
StartCoroutine(BecomeInvincible(5.0f));
}
IEnumerator BecomeInvincible(float time) {
canGetHit = false;
yield return new WaitForSeconds(time);
canGetHit = true;
}
var can_get_hit: bool = true
func on_get_hit():
become_invincible(5.0)
func become_invincible(time: float):
can_get_hit = false
var timer = get_tree().create_timer(time)
timer.timeout.connect(_on_timer_timeout)
func _on_invicible_timer_timeout():
can_get_hit = true
void MyActor::OnGetHit() {
BecomeInvincible(5.0f);
}
void MyActor::BecomeInvincible(float time) {
canGetHit = false;
GetWorldTimerManager().SetTimer(
invincibilityTimer, this, &YourClass::OnGetHit, time, false
);
}
void MyActor::OnInvincibilityTimerEnd() {
canGetHit = true;
}
The problem is that in all of these approaches you can
only create the timer, but their
It's not a user-friendly API. It gets in your way
far too often and you still end up with tons of
Wouldn't it be much better if the timer updating logic was right there in your code, invoked exactly when you need it to?
Timer invincibility_timer = Timer(5.0f);
bool can_get_hit = true;
void update() {
if (invincibility_timer.update(Game::delta_time)) {
can_get_hit = true;
}
}
void on_get_hit() {
can_get_hit = false;
invincibility_timer.reset();
}
So, you explicitly tick the timer using
update(), and the function returns
true only on the exact frame it reaches 0.
See below for an implementation of the timer in a C++-like pseudocode:
struct Timer {
float current, start;
Timer(float start_seconds) {
start = start_seconds;
current = start;
}
bool update(float dt) {
current -= dt;
if current <= 0 {
current = 0;
return true;
}
return false;
}
void reset() {
current = start;
}
void reset_with(float new_start_seconds) {
start = new_start_seconds;
reset();
}
};
The reason it's called an
You can use this timer struct for arbitrary
countdowns (like ammo, so each update would decrement by
1) etc. Creating a timer that loops every N seconds is
easy:
Timer spawn_enemy_timer;
void update() {
if (spawn_enemy_timer.update(dt)) {
spawn_enemy_timer.reset();
// spawn enemy
}
}
The order of operations in your game logic very important, so by letting you decide when and where to update the timers, you can be sure that event A won't accidentally "override" event B just because A's timer was started first. This may seem like a trivial thing, but whenever you're doing something like complex interaction between entities, it's crucial to get your logic in order, or else the enemy will incorrectly kill the player first before the player gets a chance!
Nothing about this is novel, in fact many game programmers discover this pattern themselves. So having a formal data type ready comes in handy. Game engines should support these timers out of the box due to how ubiquitous they are.