0 votes

I'm using _physics_process() to check for player input every frame.

func _physics_process(delta):
    check_for_keypresses(delta)
    collision_check()
    if shield <= 0:
        emit_signal("player_died")
        explode()

One of the keypresses I check for is related to firing weapons.

func check_for_keypresses(delta):
    movement_check(delta)
    if not cutscene_started:
        fire_check()
        charge_check()
    debug_level_check()

I use a timer to flip a boolean that can be used to check if the player is able to fire weapons at the moment or not (the player's delay between shots is measured in milliseconds):

func fire_check():
    # fire button, isn't during heat sink failure, firerate timer
    if Input.is_action_pressed("fire") and fire_conditions():
        create_missile()
        play_surreal_drive_sound()
        play_gunshot_sound()
        advance_fire_pattern()
        generate_heat()
        firerate_cooldown()
    if Input.is_action_just_released("fire"):
        stop_surreal_drive_sound()
        $t_pattern_reset.start()

The problem shows up when I try to play the game at lower framerates (30 fps). I don't actually believe that anyone will be able to play this game at a lower framerate than 60 fps, considering it's rather small, but even then, with this method, the player's firerate cannot exceed maximum fps - you cannot check for player input more than once per frame, which means your firerate caps at 60 bullets per second for 60 fps, 30 bullets per second for 30 fps, etc.

There are a few things I considered in this situation:
- searching up if Godot supports substepping (or something of the sort, the first result redirects me to the Unreal Engine page), but I haven't got any results on that matter (and I don't even know how substepping works, so it could be overkill for this game anyway)
- somehow putting user input (or the bullet creation functions themselves) outside of _(physics)_process, but I'm not exactly sure how, and when I googled the _input function, I learned that _process input is used because it has less latency for stuff like action games
- if none of those are possible, increasing bullet damage to compensate for the fact that firerate is tied to framerate, but that still doesn't help the fact that this game's firing mechanics will run differently at 30 or 15 fps (though reaching such a low framerate would probably mean you shouldn't even be playing this game)

I'm kind of stuck on this one. I would appreciate some help on the matter.

Godot version 3.3.3 mono
in Engine by (23 points)

1 Answer

+1 vote
Best answer

physicsprocess compensates for lag, such that if the game is designed to run at 60fps and the user's computer cannot keep up with that and runs at 30fps for example, the code will run twice every frame. You can set the target frame rate and design your game around it, or you can use the delta parameter to explicitly control the rate of bullets fired per second. The delta parameter holds the time passed since the last call, which is variable in process but constant in _physicsprocess.

As an example (untested) of firing 100 bullets per second no matter what physics fps the game is designed to run at:

var bullet_hz = 100
var bullet_time = 1 / bullet_hz
var delta_sum = 0
func _physics_process(delta):
    delta_sum += delta
    if delta_sum > bullet_time:
        var remainder = fmod(delta_sum, bullet_time)
        num_bullets = round((delta_sum - remainder) / bullet_time)
        delta_sum = remainder
        #fire num_bullets
by (773 points)
selected by

Hey, that's exactly what I need! I don't know how I didn't figure out that I could use deltas for this.

This has to be modified pretty hard for it to work appropriately, though (e.g. in this example, delta_sum makes it so that the function creates X bullets since the last time you fired instead of creating X bullets depending on how long the previous frame lasted, and also you'd have to offset the projectiles' starting positions depending on the delta, and also you have to make bullet_time a floating point variable if you don't want a zero division error), but I can get somewhere from here because the idea is pretty good. Thanks.

Is there any way one could use a timer node for this, though? I originally used a timer node that, once timed out, would let the player fire again. I could simply not use it, but I don't really feel like changing that. If I have to, I will, though.

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.