Problem calculating time in bullet hell pattern

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Gerboroso

Hi there fellow redditers and godot people.
I find myself in times of trouble. I was trying to program a general purpose spiral shooting function for my bullet hell game. The problem is that if I encrease the angular velocity, it takes longer for the function to end and it keeps shooting for a while longer thaat it is told in time. But the weird thing is that if a tell it to add all of the intervals into a variable and then give it to me it allways prints the correct time value (5 since I called the function with shoot_spiral_V03(5,PI/16,4*PI)) but it clearly isn’t the time it is shooting as when I counted it reached times as long as 14 seconds. Here I leave the function. Help would be trully apreciated.

func shoot_spiral_V03(time, angle_step, angular_velocity):
	var shot_interval = angle_step/float(angular_velocity)
	var bullets = time/shot_interval
	var time_t = 0
	var angle = 0
	print(OS.get_time())
	for counter in range(0,bullets,1):
		angle = counter * angle_step
		emit_signal("shoot_signal", position, Vector2(1,1).rotated(angle), false)
		yield(get_tree().create_timer(shot_interval, false), "timeout")
		time_t += shot_interval
	print(time_t)
	print(OS.get_time())

Ty. Have a good day.

:bust_in_silhouette: Reply From: Bernard Cloutier

5 / (PI/16 / 4*PI) gives 320 bullets over 5 seconds, meaning 64 bullets per seconds. This means you are creating a timer that lasts less than 1/60th of a second, which is the default frequency of the physics engine. So you are asking the engine to create a timer, process it and react to it in less time than one of its physics frames. Since at the end of the first frame, you’ll still have a tiny bit of time left on your timer, the “timeout” signal will only be processed on the next physics frame. Without analyzing the code further, you can see how the total time will be about double what you expected.

ex:
your current interval is 0.015625‬ sec. A physics frame is processed every 0.016667 sec. You start your timer in a physics frame. On the next physics frame, your timer still has 0.016667 - 0.015625‬ = 0.001042 sec left. On the next physics frame, you finally have a value below or equal to 0. Meaning you expected the interval to last 0.015625‬, but it took 2 full physics frames to process so it actually lasted 2 * 0.016667 = 0.033334‬ sec.

5 * (0.033334‬ / 0.015625‬) = 10.66688‬ sec for the total time. I’m not sure where the other 3.33312 sec to get 14 sec come from, but it’s probably some more processing time in the engine.

In brief, it’s a precision error since your interval is so small. You have a couple options. You could increase the physics engine’s framerate to increase its precision. Or you could batch some bullets together if the interval is low enough.

ex:

var MIN_INTERVAL = 0.02 # at least higher than 1/60th to avoid your problem.
                        # Would need to be tuned by playtesting to find a value that feels good to play.

func shoot_spiral_V03(time, angle_step, angular_velocity):
    var shot_interval = angle_step/float(angular_velocity)
    var bullets = time/shot_interval
    var time_t = 0
    var angle = 0
    print(OS.get_time())
    var batch_size = 0
    for counter in range(0,bullets,1):
        batch_size += 1
        if batch_size * shot_interval > MIN_INTERVAL:
            for batch_index in range(0, batch_size):
                angle = (counter + batch_index) * angle_step
                emit_signal("shoot_signal", position, Vector2(1,1).rotated(angle), false)
            batch_size = 0
            yield(get_tree().create_timer(batch_size  * shot_interval, false), "timeout")
        time_t += shot_interval
    print(time_t)
    print(OS.get_time())

The inner loop I added makes the shoot_spiral_V03 method’s body quite a bit more ugly and unreadable. It would probably be wise to extract it to another function.

I haven’t tested it so I’m not sure you’ll get exactly 5 seconds, but you should have an actual time closer to what you expect.