How to make a Global wait() function without instancing a class?

Godot Version

4.6

Question

Hello, I’m trying to make a function where in any code I can just do utils.wait(1.0) anywheres. I have this utils script registered as an autoload. The problem is that Godot throws the error “Cannot call non-static function wait() on the class “utils” directly. Make an instance instead.”

The problem is that if I DO make it a static function, then Godot gives me the error that I cannot call non-static function “get_tree()”. Is there a way to make this a singleton, or do I have to instantiate the utils class in every script I want to wait()?

class_name utils extends Node2D

#works, but I have to instantiate utils. Setting this to static errors because
#get_tree() is non-static
func wait(secs) -> void:
	await get_tree().create_timer(secs).timeout

You can use Engine.get_main_loop() to get the scene tree from a static function. And you don’t need an autoload to access a static function, or a static function if you use an autoload.

That’s said, having a wait() function like this is a really bad idea.

2 Likes

Using the autoload instead of the class should be enough to get rid of the error. (It’s probably “Utils” with a capitalized “U”, check the autoload’s name.)

Thank you, and why is it a bad idea?

Just don’t do it. Trust me bro :smiley:

For start, calling wait() won’t make the caller “wait” unless you await for that call too. Direct awaiting on a signal is bad enough but here you introduce an additional useless abstraction level to it that just makes the code more complicated to mentally follow and coroutine bugs even harder to catch. And you WILL have coroutine bugs when playing with coroutines.

1 Like

I don’t see the issue, what I’m just trying to do is have a macro instead of typing out all that code (where in other game engines its just wait(1)). How else would you wait x amount of time to do things, ex. an AI patrolling or flashing a ui element on screen, etc?

Have you tested it? Try this:

func _ready():
	wait(10.0)
	print("Time flies...")
	
func wait(t) -> void:
	await get_tree().create_timer(t).timeout

Did _ready() wait for 10 seconds before printing the message?

1 Like

Yes, it works perfectly when I do await wait(10.0). I get what you mean in that its abstracting a bit but I’m really just trying to use it like a macro so its less typing.

Looks quite awkward though - awaiting for waiting. If your goal is to avoid typing the get_tree()... every time, better to call this function timer() instead.

That said you should stay away from awaits and use regular signal handlers instead. Unless you enjoy hunting for hard to catch bugs.

2 Likes

So I assume for you, you would just use an animation or create a timer node if you had to delay x amount of seconds?

1 Like

It really depends on what exactly needs to be delayed. The problem is not in using a scene tree timer per se, it’s awaiting for it that’s bug prone. You’ll find that soon enough if you start extensively using this approach.

You can execute the code on timer timeout in a less cryptic way - by connecting a signal handling function.

Adding onto what @normalized said:

We had a discussion about this in another thread recently and I was talked into using it less. Using await get_tree().create_timer(x).timeout seems useful, but is not the best solution for a number of reasons that you will discover when you start using it. In most cases you’re better off just making a timer and using it if you want asynchronous code.

Also, depending on what you are doing, like playing an animation, playing a sound, etc, there are already signals to wait for like animation_finished.

As you’re learning Godot, I’d recommend asking yourself if there’s a signal you can wait for instead of a time interval.

3 Likes