game-developmentgodotgodot3

High resolution (eg. 1/200 sec) timer in Godot?


I am trying to do audio panning in Godot using AudioEffectPanner and when I update the pan of it the volume levels are updated right away, resulting in an audible pop.

I am calculating the target pan value in _process(), but even when smoothing it is quite noticeable.

I tried to move the smoothing to _physics_process(), tried to update the Physics FPS to a high value (200 FPS) but apparently it is only called more times before _process() is called (tested with timestamps, got the calls in groups).

I tried to set up a Timer as well as a last resort but it is also evaluated before the _process() is called.

So any of the methods I tried result in updating at a maximum of 60 times per seconds (my display's update rate, it will vary on player's setups).

Is there a way in Godot to have a timer with greater resolution? Or even just having a separate thread in a loop of just updating the volume and sleeping? Maybe there is a method to update the AudioEffectPanner and it will take care of smoothing it out?

(I am currently using Godot 3.5 and in the middle of a game jam, I will switch to 4.x for my next project.)


Solution

  • Timers only run on the engine's proccess cycles as you have seen. However with OS.delay_msec()/OS.delay_usec() you can make a thread that calls your function at faster intervals.

    Note: this is likely still not 100% accurate to the desired time as it is still limited by the CPU. Additionally it will also deviate more the longer your processing function takes.

    Sample Script:

    extends Node
    
    var thread: Thread
    var quitting := false
    
    func _ready() -> void:
        thread = Thread.new()
        thread.start(self, "thread_func", null, Thread.PRIORITY_HIGH)
    
    
    func stop_thread() -> void:
        # Setting quitting to true stops the thread, 
        # prevents a dead lock when cleaning up the thread
        quitting = true
        thread.wait_to_finish()
    
    
    func do_thing() -> void:
        print(OS.get_system_time_msecs())
    
    
    func thread_func(args = null) -> void:
        while not quitting:
            OS.delay_usec(1000000/200)
            do_thing()