Is it OK to use Engine.get_process_frames as a Timer?

Godot Version

4.5

Question

I just wanna add quick and dirty delay before a function but not sure of the downside.

func _process(delta: float) → void:
if Engine.get_process_frames() % 100 == 0:

if is_instance_valid(enemy):
queue_free()

Down side is the actual frame rate does not match real time and can be variable per device, platform, etc.

(Edit: this should be the accepted answer):

(/Edit: my original answer):

Use this in stead:

Time.get_ticks_msec

Neither using Engine.get_process_frames() nor Time.get_ticks_msec() is a good idea if you are ever going to change the Engine.time_scale. Maybe you want to slow down the game for some game specific purpose or for debug purpose. I frequently slow down my game to see what’s happening in slow motion.

If you want to make your own timer, use the delta. Most of the time _physics_process is better than _process for these kind of things.

var my_timer: float = 1.0 # 1 second

func _physics_process(delta: float) -> void:
    if my_timer > 0:
        my_timer -= delta 
        if my_timer <= 0:
            # do something

You can also use SceneTreeTimer if you want a quick one-shot timer for delay:

func some_function():
    print("Timer started.")
    await get_tree().create_timer(1.0).timeout
    print("Timer ended.")

It’s one liner and clear on what it does. But if you put it inside _process, it will proceed to next frame after create the timer and repeat. When timer timeout, then it will execute rest of the code. Can read more: SceneTreeTimer — Godot Engine (stable) documentation in English

That’s the one I use too actually. I believe that also respects slowing down execution.

Both this and @Dizzy_Caterpillar 's answer are better than mine.

Don’t do any of those things.

Instead, explain why you want to do this. Then we can give you a better answer to your problem. If something is happening you want to wait for, you should be waiting for it to complete rather than using any kind of arbitrary timer.

In this particular case I don’t mind it so much if it’s off by 1-2 seconds in real time eg. If I was expecting 1 second delay but ended up with 2-3 that would be fine.

If it ended up more, like 5+, I would assume the game is unplayable on that device.

I should also have stated that this is a web game and it is on compatibility mode so I don’t know if that matters.

@dragonforge-dev

This is a classic example of the XY Problem.

You might benefit from reading this:

Yea I know of xy problem. I’m just being lazy and I want chain children deletion without using signal or timer node.

func _process(delta: float) -> void:
	if Engine.get_process_frames() % 100 == 0:
		if is_instance_valid(a):
			pass
		else:
			if is_instance_valid(b):
				b.queue_free()
			else:
				if is_instance_valid(c):
					c.queue_free()
				else:
					if is_instance_valid(d):
						d.queue_free()

If you want to do something on a perpetual basis - use a tween:

var cleanup_tween: Tween
const CLEANUP_INTERVAL := 2.0

func _ready():
	cleanup_tween = create_tween()
	cleanup_tween.tween_interval(CLEANUP_INTERVAL)
	cleanup_tween.tween_callback(cleanup)
	cleanup_tween.set_loops()
	
func cleanup():
	print("DOING CLEANUP NOW")

Using Tween is nice! I could just kill the tween when last child is free too so thank you.

@renevanderark You actually answered my question but @normalized offered a more suitable solution.

@dragonforge-dev Thanks for reminding. When I posted I didn’t actually think of this as a “problem to solve” and more like “hey what do you guys think of this?” so it didn’t occur to me to explain furthermore. It was not until seeing the tween solution that realized I should stop processing loop when not needing it.

In anycase I benefit from seeing others answer about delay timing without a node as well.