Olivier Pons
Olivier Pons

Reputation: 15796

Godot: _process() vs _physics_process(): I need a valuable example

There are lot of links on the web, but no one answers correctly to my question... I copy/paste my "official" code just after my reflexion, because I dont understand when/why/how to properly use _process() vs _physics_process().

I see that the principle is the same as in Unity: _process() is called as soon as it can and not consistently (sometimes more, sometimes less), unlike _physics_process(). And what surprised me were the officially "recommended best practices" in Unity, which correspond to my code here on Godot, whereas in the official Godot documentation (see the link here), they read the inputs in _physics_process()!

With a trace(print("...")) in both methods, I see that _process() is called 5 times at the beginning before _physics_process(). This means, in practice, that, for example, if the player has pressed + released the "up" button in the meantime, nothing happens (which is logical). But conversely, if there is a big lag, my code ignores player inputs (because _process() is no longer called), but continues/finishes/physical movements, which seems identical to the behavior of all online games (LoL are you there?). So... I don't understand... because if they had wanted, they would have made a single routine, and they wouldn't have differentiated _process() vs _physics_process() ...

So I've come up with this "theoretically" good code which is much longer than in any example you can find on the Web. To make it work, create a 3D project, add a KinematicBody and in this node, add a Mesh and a CollisionShape (a sphere). And add this script to the KinematicBody.

extends KinematicBody

export var SPEED:int = 6
export var ROTATION:int = 6
const DIR_N:int = 1
const DIR_E:int = 2
const DIR_S:int = 4
const DIR_W:int = 8
const DIR_EW:int = DIR_E | DIR_W
const DIR_NS:int = DIR_N | DIR_S
const DIR_NESW:int = DIR_N | DIR_E | DIR_S | DIR_W
const DIR_WITHOUT_WE:int = DIR_NESW & (DIR_N | DIR_S)
const DIR_WITHOUT_NS:int = DIR_NESW & (DIR_E | DIR_W)
var ask_to_move:int = 0
var velocity = Vector3(0, 0, 0)

func _ready()->void:
    pass
    
func _process(delta):
    if Input.is_action_pressed("ui_right"):
        ask_to_move |= DIR_E
    if Input.is_action_pressed("ui_left"):
        ask_to_move |= DIR_W
    if (ask_to_move & DIR_EW) == DIR_EW:  # both pressed -> remove:
        ask_to_move &= DIR_WITHOUT_WE

    if Input.is_action_pressed("ui_up"):
        ask_to_move |= DIR_N
    if Input.is_action_pressed("ui_down"):
        ask_to_move |= DIR_S
    if (ask_to_move & DIR_NS) == DIR_NS:  # both pressed -> remove:
        ask_to_move &= DIR_WITHOUT_NS

func _physics_process(delta):
    if ask_to_move & DIR_E:
        velocity.x = SPEED
        $mesh.rotate_z(deg2rad(-ROTATION))
    elif ask_to_move & DIR_W:
        velocity.x = -SPEED
        $mesh.rotate_z(deg2rad(ROTATION))
    else:
        velocity.x = lerp(velocity.x, 0, 0.1)

    if ask_to_move & DIR_N:
        velocity.z = -SPEED
        $mesh.rotate_x(deg2rad(-ROTATION))
    elif ask_to_move & DIR_S:
        velocity.z = SPEED
        $mesh.rotate_x(deg2rad(ROTATION))
    else:
        velocity.z = lerp(velocity.z, 0, 0.1)

    move_and_slide(velocity)

Upvotes: 2

Views: 5621

Answers (2)

Bgie
Bgie

Reputation: 513

Depends on the type of input. My take on this:

For movement input, you would want to process those in _physics_process(delta), because that is where the movement happens. Pressing a movement key shorter than the duration of a movement frame is ignored. This is usually acceptable.

For attacking/jumping/etc, you either use _input(event) or Input.is_action_just_pressed inside _physics_process(delta). Though I just learned from Theraot that this is bugged in Godot < 4.1

If you really want your movements to consider short keypresses (< physics frame update) you should use events and track the actual delta time the key was down. Then do a proportional movement. This will become complex. You would still do the moving in _physics_process(delta).

As for _process, I honestly do not see why you would use it for input if you use physics. Only non-physics games could use it I guess, if you make all movements compatible with a variable framerate.

The 'lag' you reference in online games is related to networking, not the graphical framerate of the client.

Upvotes: 0

Theraot
Theraot

Reputation: 40295

But conversely, if there is a big lag, my code ignores player inputs (because _process() is no longer called), but continues/finishes/physical movements

You are correct that with Input.is_action_pressed if a key is pressed and released between frames※ it will be ignored.

※: graphics frame if you are using it in _process or physics frames if you are using it in _physics_process. Yes, Input is aware of the context in which you use it.

Fun fact: Before Godot 4.1, if a key is pressed and released between frames, Input.is_action_just_released will return true but Input.is_action_just_pressed will return false. Starting with Godot 4.1 you will get both returning true, so the code that just checks for is_action_just_pressed won't miss the input.


if they had wanted, they would have made a single routine

I'm guessing that with "a single routine" you mean get_input and _physics_process in the linked example. And to that I agree.

and they wouldn't have differentiated _process() vs _physics_process()

I suppose you are not asking why we have these two, since, as you have noted, it is the same logic as in Unity. Look, I've made my best effort on explaining it at Make it clear when to use _process and _physics_process.


I see that a lot of your concern comes from handling input. So I would suggest to use _input for it instead. I believe there is not Unity equivalent.

I'm aware that some people want to use _process so they "do not miss the player input"… But it is theoretically possible… Except monitors are quicker than humans and some input devices.

In general I would start by - and I would suggest people to to start by - using Input in _physics_process because we often want to check both physics and input (e.g. is the character on the floor and is the jump button pressed). Then, if you want to make sure you don't miss input, use _input instead. Also use _input if you need to handle pointing input, or precise input timing is important.

Upvotes: 1

Related Questions