Overview
When starting game development with Godot Engine, everyone first encounters the two main callback functions for controlling node behavior: _process(delta) and _physics_process(delta).
However, if you don't correctly understand the difference between these two functions and when to use each one, you'll face issues like unstable game behavior, inaccurate physics calculations, or degraded performance.
This article carefully explains the basics of Godot's game loop, points out common beginner mistakes, and introduces best practices for stable game development with concrete code examples.
Core Concepts: Variable vs Fixed Frames
The most important difference between _process and _physics_process is the frequency and timing of their calls. This is based on the fundamental concepts of "variable timestep" and "fixed timestep" in game development.
_process(delta): Variable Timestep
- Call frequency: Called as frequently as possible, every frame.
- Use cases:
- Processing directly related to rendering.
- User interface (UI) updates.
- Input handling (
Input.is_action_pressed(), etc.). - Smooth animations and visual effects that don't affect physics.
deltaargument: Contains the elapsed time (in seconds) since the last frame. Since the frame rate fluctuates, thisdeltavalue also varies.
_physics_process(delta): Fixed Timestep
- Call frequency: Called at a fixed interval regardless of game frame rate (default: 60 times per second, or every 0.0166 seconds).
- Use cases:
- Physics calculations (movement with
move_and_slide(), etc., forCharacterBody2D/3D). - Processing requiring accurate simulation, like collision detection and raycasts.
- Game logic that needs to be time-strict (timers, cooldowns, etc.).
- Physics calculations (movement with
deltaargument: Always contains a fixed value (default: 1/60 second).
| Feature | _process(delta) | _physics_process(delta) |
|---|---|---|
| Call frequency | Every frame (variable) | Fixed interval (default 60/sec) |
| Timestep | Variable timestep | Fixed timestep |
delta value | Varies | Fixed (default 0.0166... sec) |
| Main uses | Rendering, UI, input, non-physics animation | Physics, collision detection, strict game logic |
Common Pitfalls and Mistakes
The most common mistake beginners make with these two functions is writing all processing in _process.
Mistake 1: Doing Physics in _process
If you write character movement processing (like move_and_slide()) in _process, character movement speed and behavior may change depending on the player's PC performance or frame rate.
- Low frame rate: Fewer
_processcalls result in jerky character movement and increased risk of collision detection failures (tunneling). - High frame rate: More
_processcalls may cause characters to move faster than intended (even withdeltacompensation, physics stability is inferior to fixed timestep).
Furthermore, doing physics calculations in _process breaks the "fixed step" assumption that the physics server expects. Even if you multiply by delta to compensate speed, collision detection and raycast results become frame rate dependent, causing behavior differences between test and production environments.
For example, the same code might have different "hit chance" or "pass-through likelihood" between 144Hz and 60Hz monitor environments.
Mistake 2: Calculating Movement Without Using delta
If you calculate movement with fixed values in _process without using the delta argument, character speed will change significantly when frame rate fluctuates.
# Wrong example: not using delta in _process
func _process(delta):
# Higher frame rate means more executions per second, making the player move faster
position.x += 5
When using _process, you must always multiply by delta to guarantee frame rate independent speed.
Best Practices and Examples
Let's learn the correct way to use _process and _physics_process to achieve stable game behavior.
Separating Physics and Input Processing
A common pattern is to do physics calculations in _physics_process and capture input in _process, storing it in a variable. For simple games, you can get input directly in _physics_process, but separating concerns makes refactoring and debugging easier, so we'll show the separated approach here.
Best Practice for Player Movement
extends CharacterBody2D
const SPEED = 300.0
var direction = Vector2.ZERO # Variable to store input direction
# 1. Input processing: get user input every frame
func _process(delta):
# UI and animation, processing directly related to rendering goes here
# Get input and store in variable
direction.x = Input.get_axis("move_left", "move_right")
direction.y = Input.get_axis("move_up", "move_down")
# 2. Physics processing: execute physics calculations at fixed step
func _physics_process(delta):
# Calculate velocity using stored input direction
velocity = direction * SPEED
# move_and_slide() is physics, so always call it in _physics_process
move_and_slide()
This approach ensures input is captured smoothly (every frame) while physics calculations run at a stable fixed step, guaranteeing frame rate independent consistent behavior.
Interpolation for Visual Smoothness
Physics calculations run at a fixed step (e.g., 60 TPS) while rendering runs at a variable step (e.g., 120 FPS), so there are moments between render frames where physics object positions aren't updated, which can look jerky.
Starting with Godot 4.3, Physics Interpolation is built-in to automatically eliminate this jitter.
How to enable:
- Open Project > Project Settings
- Enable Physics > Common > Physics Interpolation (check the box)
With this setting enabled, positions calculated in _physics_process are automatically interpolated between render frames, achieving smooth movement.
Note: 2D physics interpolation is available from Godot 4.3 onwards. Versions 4.0-4.2 only supported 3D, and 2D required external addons (like Smoother).
Summary
Properly using _process and _physics_process is the key to stability and performance in Godot Engine.
| Function | Timestep | Use for | Avoid for |
|---|---|---|---|
_process | Variable | UI updates, input capture, non-physics animation, rendering | Physics calculations, collision detection, strict time management |
_physics_process | Fixed | Physics (move_and_slide), collision detection, timers, game logic | Processing directly tied to rendering (for performance reasons) |
Next time you write code in Godot, ask yourself whether the processing is rendering-dependent or physics-dependent, and choose the appropriate function.