Difference between _process() and _physics_process()

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

Hello! I’ve been using Godot for quite a while, but I have a beginner question still:

I know that _process() is called every frame while _physics_process() is called every physics frame, which is determined by “Physics Fps” property.

But the thing is: I’ve noticed that my character moves more smoothly when I put the movement code inside _process() rather than _physics_process()

Is there any downsides on doing that?

_process() is called as often as the CPU speed allows, whereas _physics_process() is always called at a fixed rate which may not be as often as when _process() is called, which may explain why it’s smoother.

Ideally you want to put any processing that requires the physics values to be correct in the _physics_process() call.

SteveSmith | 2022-10-10 19:38

Thanks for the answer! I will be using _process() for now, as I don’t see any downsides.

Rubin | 2022-10-10 20:40

2 Likes
:bust_in_silhouette: Reply From: stan.wick.52

Your character moves smoothly in _process() because calculations are made at the frame rate the game is playing at.
Where things start to break is when someone is not playing at the frame rate you intended (As a test, limit your FPS in the engine, you’ll see what I mean).
With slower fps, you’d get slower calculations,
With higher fps, you’d get calculations so fast your physics would be liable to break (think GTA SA with uncapped frame limiter).
That’s why you have _physics_process() to help when you need consistent results irrespective of frame rate.
But a problem arises when your physics fps doesn’t match your game fps, you get jitters and stutters, hence the movement does not appear smooth, and increasing the physics FPS doesn’t really solve that issue. To combat that, Physics interpolation was introduced in Godot 3.5. It helps eliminate jitter and stutter

We can use delta parameter to solve these FPS issues in both _process() and _physics_process(), and as far as I’ve seen, I don’t really see an issue with using _process() (since I always multiply physics calculations with delta).

Rubin | 2022-10-11 14:13

Yes, that’s true to an extent. You won’t notice any problems if your frame rate is consistently above the physics frame rate.
Where you’ll notice an issue is at lower frame rates. The _process() function is generally called after physics calculations have taken place, so anything physics related would jitter if the frame rate is low enough and you could experience clipping, unregistered collisions, stuff like that.
Granted, you can alleviate that by increasing the physics fps, but that’s more strain on hardware, and at that point, you may as well have put the code in physics process.
Either way it all boils down to what’s best for your project.

stan.wick.52 | 2022-10-11 17:26

Ohhh I didn’t knew that. I will do some testing with my project. Thanks for the answer!

Rubin | 2022-10-11 20:50

7 Likes

Hi, despite is quite an old post, I am not completely sure to having well understood got differences between using _physics_process and process …which are pro cons of each one..in what cases i should use them?.. :thinking:

Anything related to physics (collisions etc.) = _physics_process()

Everything else that should processed every frame (visual updates etc.) = _process()

I believe the game-loop chapter in gameprogrammingpatterns can shed light on importance of _process() v/s _physics_process()

ref: Game Loop · Sequencing Patterns · Game Programming Patterns

TL;DR:

_process

  • Regular _process has variable delta per call ( delta corelated to CPU power & processing time )
  • This makes calls to successive _process non-deterministic
    ex: delta could be [10ms, 30ms, 20ms, 15ms, 15ms, 20ms, 15ms, 25ms … )
  • This is fine for games which don’t rely on in-world clocks for simulations/calculations
    ex: some turn based games, card games, point and click, grid based games, puzzle games … etc
  • This causes issues when you need reliable in-world time keeping.
    ex:
    • assume in-world time units is tick and 1 tick = 10 ms
    • assume a person is running 1m / tick
    • assume there is a wall at 3m
    • if process was called for [10ms, 10ms, 10ms, 10ms, …] => [1tick, 1tick, 1tick, 1tick, … ], we’d check the total distance v/s wall for [1m, 2m, 3m, 4m, … ] and detect collision at 3m.
    • if process was called for [10ms, 10ms, 20ms, 10ms, … ] => [1tick, 1tick, 2tick, 1tick, …], we’d check the total distance v/s wall for [1m, 2m, 4m, 5m, … ] and never detect collision at 3m, since we skip passed it.
    • there is also the headache that the deltas are not strict multiples of 10ms so it could come as [9ms, 14ms, 11ms, 17ms, 24ms …], which means you get[0.9tick, 1.4tick, … etc] and is very difficult to keep track of all rounding, carry overs, … etc as decimals are not accurate in computers.

_physics_process

  • _physics_process gurantees constant steps for calculations. this means our calculations are done at constant increments. ( I’m not sure how this delta is tuned or configured ).
  • this give deterministic & constant progress of in world time for reliable simulations/calculations.
  • One caveat to note is, rendering is not coupled with calls of physics process ( ex: there could be 3 calls to physics process and one render, and 1 call to physics process and one render … so on )

NOTE:

I don’t know the internals of _physics_process & _process, and is purely based on what I understand for the game-loop chapter I read.

CODE:

Try creating a scene with following code an trigger it, you’ll observe that delta for process is variable and for physics process is fixed.

# node_2d.gd ( node_2d.tscn )
extends Node2D

var regular_delta = 0.0
var regular_warmup = 1000
var regular_hits = 0
var regular_misses = 0
var regular_total = 0
var physics_delta = 0.0
var physics_warmup = 1000
var physics_hits = 0
var physics_misses = 0
var physics_total = 0


func _process(delta): # delta is variable
	if regular_warmup > 0:
		regular_warmup -= 1
		regular_delta = delta
	else:
		regular_total += 1
		if regular_delta == delta:
			regular_hits += 1
		else:
			regular_misses += 1
		print_debug("regular: ", regular_delta == delta, " | ", regular_hits, " | ", regular_misses, " | ", regular_total)

func _physics_process(delta): # delta is same
	if physics_warmup > 0:
		physics_warmup -= 1
		physics_delta = delta
	else:
		physics_total += 1
		if physics_delta == delta:
			physics_hits += 1
		else:
			physics_misses += 1
		print_debug("physics: ", physics_delta == delta, " | ", physics_hits, " | ", physics_misses, " | ", physics_total)
---
regular: false | 3019 | 29 | 3048
   At: res://node_2d.gd:25:_process()
physics: true | 473 | 0 | 473
   At: res://node_2d.gd:37:_physics_process()

1 Like