Introduction: Why Is Distinguishing Between These Two Important?
When starting development with Godot Engine, everyone first encounters two functions: _process and _physics_process. However, have you ever faced problems like "characters move faster depending on PC performance," "movement looks choppy," or "sometimes passing through walls"?
Most of these problems are caused by not properly understanding the roles of _process and _physics_process, and writing processing in inappropriate places. This article thoroughly explains the differences between these two functions—the heart of Godot's game loop.
_process vs _physics_process: Basic Differences
The most important difference between these two functions is "when" and "how frequently" they are called. This directly relates to the concepts of "variable timestep" and "fixed timestep" in game development.
| Feature | _process(delta) | _physics_process(delta) |
|---|---|---|
| Call timing | Every frame (per render) | At fixed intervals (per physics calculation) |
| Timestep | Variable Timestep | Fixed Timestep |
| Call frequency | Depends on PC performance and load (e.g., 60FPS, 144FPS) | Depends on project settings (default: 60 times/second) |
delta argument | Time elapsed since previous frame (varies) | Time between physics steps (fixed value) |
| Primary uses | UI updates, input handling, visual effects, non-physics animations | Physics calculations, collision detection, time-critical logic |
_process: Called every time the screen is updated, making it suitable for visual processing. However, execution intervals are unstable, so it can't be used for precise calculations like physics._physics_process: Called at constant intervals regardless of frame rate, guaranteeing reproducibility of physics-based movement (same results regardless of when or who runs it).
Common Mistakes and Best Practices
Even understanding the theory, it's common to make mistakes in practice. Let's look at specific mistakes and best practices to solve them in comparison format.
| Common Mistake | Best Practice |
|---|---|
Running physics (move_and_slide) in _process.→Behavior becomes unstable depending on frame rate. | Always run physics in _physics_process.→Ensures reproducible, stable physics behavior. |
Adding values without using delta in _process.→Game speed changes based on PC performance. | Always multiply delta for time-based processing in _process.→Achieves uniform speed independent of frame rate. |
Getting input (Input.is_action_pressed) in _physics_process.→Input may be missed, making response feel sluggish. | Get input in _process or _input and save results to member variables.→Detects input every frame for smooth controls. |
Running visual updates (UI, effects) in _physics_process.→Doesn't sync with rendering, causing jitter. | Run visual processing in _process.→Syncs with render frames for smooth visuals. |
Practical Code Example: Character Movement
Let's look at character movement code based on best practices. The key is properly separating input, physics, and animation.
extends CharacterBody2D
const SPEED = 300.0
const JUMP_VELOCITY = -400.0
# Gravity calculated by physics engine
var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")
var input_direction = Vector2.ZERO
# 1. Input processing: Run every frame and save input to variables
# Could also use _input() event, but _process is more intuitive for continuous input
func _process(delta):
# Get horizontal input
input_direction.x = Input.get_axis("move_left", "move_right")
# Jump input (detect single press)
if Input.is_action_just_pressed("jump") and is_on_floor():
velocity.y = JUMP_VELOCITY
# Visual processing (e.g., animation updates)
update_animation()
# 2. Physics processing: Run at fixed timestep
func _physics_process(delta):
# Apply gravity
if not is_on_floor():
velocity.y += gravity * delta
# Determine horizontal velocity based on input direction
velocity.x = input_direction.x * SPEED
# Execute physics
move_and_slide()
func update_animation():
# Write visual code here like animation tree updates
pass
In this code, _process handles smooth input detection and animation updates, while _physics_process focuses on stable physics calculations (gravity, movement). This achieves consistent, comfortable controls in any environment.
Performance and Advanced Topics
Smoothing Movement with Physics Interpolation
When execution frequencies differ—like physics at 60/second and rendering at 144/second—a "jitter" phenomenon occurs where object movement appears choppy. Physics interpolation solves this.
When enabled, the engine automatically interpolates positions between physics steps, displaying them smoothly in render frames.
- Enable: Turn on
Project Settings > Physics > Common > Physics Interpolation.
This setting is essential, especially for games where the camera follows a character.
_input() vs Input in _process()
Input processing can use Input.get_axis() within _process(), or use the _input(event) callback.
_input(event): Called the moment an event occurs, like mouse clicks or key presses. Suitable for one-time actions (e.g., opening inventory, firing)._process(): Good for checking continuous states. Movement processing usingis_action_pressed()to confirm "key is being held" is more intuitive here.
Both are valid, but using them according to their roles makes code intentions clearer.
Summary and Next Steps
Proper differentiation of _process and _physics_process is one of the first and most important steps in Godot development. Always make a habit of asking yourself: "Does this processing need physical reproducibility, or does it need visual smoothness?"
Now that you've mastered the basics in this article, here are topics to tackle next:
- Signals: A powerful system for keeping node coordination loosely coupled.
AnimationTree: Blend multiple animations and manage complex character states.- Custom Resources: Create reusable datasets like weapon data and character stats.
Learning these concepts will make your Godot projects more scalable and manageable.