How do I create a timer in Godot?
Asked Answered
E

5

11

How do I create a timer in Godot which destroys the script's object after a given amount of time? I am looking to remove bullets from a game after a while to reduce lag.

Epifaniaepifano answered 14/5, 2022 at 17:58 Comment(1)
Please provide enough code so others can better understand or reproduce the problem.Odessa
E
17

There is a Timer node that you can use. You can add it as a child, set the wait time (in seconds) - you could set it as one shot, and auto start - connect the "timeout" signal to your script, and on the method call queue_free to have the Node (and children, which includes the Timer) freed safely.


You can do that from code too, if that is what you prefer. So, let us go over what I just said, but instead of doing it from the editor, let us see the equivalent code:

Create a Timer, add it as a child:

var timer := Timer.new()
add_child(timer)

Set the wait time (in seconds):

timer.wait_time = 1.0

Set as oneshot:

timer.one_shot = true

Instead of setting it to auto start (which would be timer.autostart = true), let us start it:

timer.start()

Connect the "timeout" signal to a method. In this case, I'll call the method "_on_timer_timeout":

timer.connect("timeout", self, "_on_timer_timeout")

func _on_timer_timeout() -> void:
    pass

Then in that method _on_timer_timeout, you can call queue_free:

timer.connect("timeout", self, "_on_timer_timeout")

func _on_timer_timeout() -> void:
    queue_free()
Equiangular answered 16/5, 2022 at 5:20 Comment(1)
timer.connect("timeout",_on_Timer_timeout) works for me.Flied
K
15

Edit: I stand corrected, the best approach is to use a callback like Theraot's answer says:

func _on_timer_timeout_funcname() -> void:
    # Do stuff here...
    queue_free() # removes this node from scene

var timer := Timer.new()
timer.wait_time = 1.0 # 1 second
timer.one_shot = true # don't loop, run once
timer.autostart = true # start timer when added to a scene
timer.connect("timeout", self, "_on_timer_timeout_funcname")
return add_child(timer)

I'm leaving my answer below here for people who are more comfortable with a sequential approach, but it can be a bit more tricky to deal with.


In Godot 4, there's an easy way to do this:

# Do some action
await get_tree().create_timer(1.0).timeout # waits for 1 second
# Do something afterwards
queue_free() # Deletes this node (self) at the end of the frame

Important: if you do this in the _process() or _physics_process() functions, more timers get created every frame, which causes several runs to occur all at once before then running the following code. To handle this, simply track whether a timed event is happening.

Example in the _process() with simple attack logic:

var attack_started = false;

func _process(delta):
    if attack_started:
        print("Not attacking, attack code running in background")
        return
    else:
        attack_started = true
        prepare_attack()
        await get_tree().create_timer(1.0).timeout # wait for 1 second
        begin_attack()
        attack_started = false

This await keyword works with everything that emits signals, including collision events!

FYI: yield was replaced with await in Godot 4, and await really just waits for a signal/callback to complete:

await object.signal

get_tree().create_timer(5.0) will create a timer that runs for 5 seconds, and then has a timeout callback/signal you can tap into.

Kroeger answered 16/1, 2023 at 2:52 Comment(1)
Thank you for this. Was able to translate this to C# for my usecase. Added another answer for the C# code too.Damnedest
M
3

You may want to use the SceneTreeTimer, like in the following code:

func die(delay: float):
    yield(get_tree().create_timer(delay), "timeout")
    queue_free()

Please refer to Godot Engine's documentation.

Meatman answered 24/5, 2022 at 14:25 Comment(0)
D
0

To build off of slay's answer...

In C# you can write

await ToSignal(GetTree().CreateTimer(1.5f), "timeout");`
QueueFree();
Damnedest answered 16/3, 2023 at 14:53 Comment(0)
B
0

Incase you don't want to instance a timer, You can also use _process() if you simply need a count down:

extends Node

signal timeout

var count_down=5

func _process(delta): # or you can use _physics_process()
    count_down-=delta
    
    if(count_down<=0):
        emit_signal("timeout")
        set_process(false)

Although this might have an error of ±delta time

Bipetalous answered 11/2 at 10:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.