Custom tick (i.e. redstone tick in Minecraft)

Think: redstone tick in minecraft (not exactly what I’m after btw, but the concept of separate tick is the same)

The ideal goal is to have another override function that act like _process / _physics_process, where it doesn’t need to trigger precisely at certain amount of time, it just need to trigger at exactly x amount every second.

Some forum that I’ve read suggested to use Timer. But instead of triggering a function every, say, 0.2 seconds, I want the function to have the leeway of triggering the first ‘tick’ after 0.1 seconds, the next after 0.3 seconds, so forth, as long as there are 5 ticks every second. Using a timer also means, if my understanding is correct, I need to connect the timer delegate and the function on each class one by one, which I would like to avoid, since ALL of the class will be using them.

I have also considered connecting the Timer and the function on a parent class and I just need to derive from that, but that will remove the ability to use the various existing. Since I’ll be stuck with one class, say, Node class.

I’m currently looking into using a custom MainLoop class, but I don’t find a way to add an override to the base Node class and call that function from the custom MainLoop.

Possible solution that I can think of is to use one of the _physics_process / _process and somekind of counter. Once the counter hit zero → trigger the custom tick function and reset the counter. But again, I would need to implement this one by one to every class.

Is there a better way to go about this? Thanks in advance.

Seems like you will need a global timer of some sort, otherwise your custome tickers will be desynced. Make a scene with a Timer as the root node and make it an Autoload, name it something nice like “RestoneTimer”. Now all of your custom tickers can connect to it easily

func _ready() -> void:
    RedstoneTimer.timeout.connect(_on_custom_tick)

func _on_custom_tick() -> void:
    print("tock!")

Though redstone itself is a tricky, there is a flow and heirarchy to each circuit that needs to evaluate in set order. You may run into situations like this, sometimes called a race condition (some will argue it is only for multithreaded) where the code has a implicit order you need it to follow but it isn’t explicitly set.

Speaking of desynced and race condition, does Timer always wait for the current timeout to finish execute before restarting?
If that’s the case, then there’s a chance that, setting the Timer wait_time as 0.2 seconds, will NOT result in 5 timeout / second, since the execution itself need time to execute.
If no, then, there will definitely race condition when the timeout function that supposed to run in the first 0.2 second not done executed even on second 0.4.

Based on the source code the timer will roll over any extra delay and not desync, but it still only sends one signal per process/physics frame

// from timer.cpp
time_left -= get_process_delta_time();

if (time_left < 0) {
	if (!one_shot) {
		time_left += wait_time;
	} else {
		stop();
	}
	emit_signal(SNAME("timeout"));
}

Now I have even more questions, lol.

Does process and physics_process run on the same thread? I was under the assumption that they’re not.

Does Timer run in the same thread as process and / or physics_process?

Depends on the timer’s process callback, set to idle or physics. Generally all scripts are going to be on the same thread, unless using a Thread. Some physics calculations will use threads in-engine but _physics_process is synced with the main thread.

I see… I think I have just enough knowledge to make a decision now.

My specific use case it this:
My game consists of multiple scenes that represents part of the map. I’m going to use this custom tick to simulate NPC movement that is not in the current loaded scene. Since I’m using physics_process to calculate movement, when the physics_process slowed down, for whatever reason, I want the custom tick to slowed down to, matching the physics_process.
My solution would be: singleton as a handler for those simulated objects, call the ‘custom tick’ function every x interval of physics_process, implement that ‘custom tick’ function one by one to all objects that needs it.

@gertkeno thanks for the assist! I know more about Timer now, and Thread, too. Which might be useful in the future.

Now that I said that… That solution won’t get the benefit of having a different ‘custom tick’, that is to have a longer time window to execute more complex calculation. If my understanding of your explanation of Timer is correct, that the Timer executed on the main thread, on physics / physics_process tick, Timer won’t benefit from that either. Those two solutions will just trigger the ‘custom tick’ at the same tick rate as the physics / physics_process. Meaning I would end up with lag every x tick since it also need to execute that custom tick logic.

Yeah if you are looking to accumulate simulation and reduce lag, then running every unloaded scene at the same time will create lag spikes. You need to both accumulate, and spread out the updates.

Here’s a very in-depth video on such a subject. their solution is unlikely to be relevant unless you are using a power of 2 cells up to 32 bits.

1 Like