1. Introduzione
Godot 4 ha introdotto grandi cambiamenti nel movimento dei personaggi. I vecchi nodi KinematicBody2D e KinematicBody3D sono stati rinominati in CharacterBody2D e CharacterBody3D, e l'API move_and_slide() è stata completamente riprogettata. Che tu stia migrando da Godot 3 o partendo da zero — questa guida copre tutto ciò che devi sapere.
CharacterBody2D e CharacterBody3D sono physics body pensati per personaggi controllati tramite codice. A differenza di RigidBody, non reagiscono automaticamente alle forze — ne controlli il movimento interamente tramite script. Questo li rende ideali per personaggi giocanti, NPC e tutto ciò che richiede un movimento preciso e deterministico.
2. Cosa è cambiato rispetto a Godot 3
Ecco i cambiamenti più importanti nel movimento dei personaggi tra Godot 3 e Godot 4:
-
KinematicBody2Dora èCharacterBody2D -
KinematicBody3Dora èCharacterBody3D -
velocityora è una proprietà integrata — non la passi più amove_and_slide() -
move_and_slide()non accetta più argomenti — legge direttamente dalla proprietàvelocity -
move_and_slide_with_snap()è stato rimosso — usa invece la proprietàfloor_snap_length -
is_on_floor(),is_on_wall(),is_on_ceiling()funzionano ancora allo stesso modo -
get_gravity()è stato aggiunto in Godot 4.4 per leggere la gravità predefinita del progetto come vettore — molto comodo
In Godot 3, move_and_slide() restituiva la velocità risultante. In Godot 4 restituisce un bool (se si è verificata una collisione), e la velocità viene aggiornata direttamente nella proprietà velocity.
3. Controller platformer 2D di base
Questo è il classico controller platformer a scorrimento laterale. Il personaggio può muoversi a sinistra/destra e saltare quando è a terra. È il template che Godot genera quando crei un nuovo script CharacterBody2D.
extends CharacterBody2D
const SPEED = 300.0
const JUMP_VELOCITY = -400.0
func _physics_process(delta: float) -> void:
# Apply gravity when not on floor
if not is_on_floor():
velocity += get_gravity() * delta
# Jump when on floor and jump pressed
if Input.is_action_just_pressed("ui_accept") and is_on_floor():
velocity.y = JUMP_VELOCITY
# Horizontal movement
var direction := Input.get_axis("ui_left", "ui_right")
if direction:
velocity.x = direction * SPEED
else:
velocity.x = move_toward(velocity.x, 0, SPEED)
move_and_slide()
Spiegazione riga per riga
-
extends CharacterBody2D— Lo script eredita da CharacterBody2D, dandoci accesso avelocity,move_and_slide(),is_on_floor()ecc. -
SPEED = 300.0— Velocità di movimento orizzontale in pixel al secondo. -
JUMP_VELOCITY = -400.0— Negativo perché in 2D l'asse Y punta verso il basso. Un valore negativo sposta il personaggio verso l'alto. -
get_gravity() * delta— Applica la gravità del progetto a ogni frame.get_gravity()restituisce unVector2rivolto verso il basso (es.Vector2(0, 980)). La moltiplicazione perdeltalo rende indipendente dal frame rate. -
Input.get_axis("ui_left", "ui_right")— Restituisce un float da -1.0 a 1.0 a seconda del tasto direzionale premuto. Supporta l'input analogico. -
move_toward(velocity.x, 0, SPEED)— Decelera dolcemente fino a zero quando non c'è input. Il terzo argomento è la variazione massima per chiamata. -
move_and_slide()— Muove il body usando lavelocityattuale, gestisce le collisioni, scivola lungo le superfici e aggiorna automaticamentevelocity.
Il tuo CharacterBody2D ha bisogno di almeno un nodo figlio CollisionShape2D con una forma assegnata (es. RectangleShape2D o CapsuleShape2D). Senza di essa, move_and_slide() non rileverà alcuna collisione.
4. Controller top-down 2D di base
Per i giochi top-down (RPG, twin-stick shooter ecc.) ti muovi in tutte e quattro le direzioni senza gravità. Il codice è più semplice rispetto a un platformer.
extends CharacterBody2D
const SPEED = 200.0
func _physics_process(delta: float) -> void:
var input_direction := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
velocity = input_direction * SPEED
move_and_slide()
Come funziona
-
Input.get_vector()restituisce unVector2normalizzato a partire da quattro azioni di input. Gestisce correttamente il movimento diagonale — la lunghezza del vettore è sempre 1.0 o 0.0, quindi il movimento diagonale non è più veloce del movimento lungo gli assi. -
Non viene applicata alcuna gravità perché si tratta di una vista top-down. Il personaggio si ferma immediatamente quando non c'è input, perché impostiamo
velocitydirettamente (nessuna inerzia).
Aggiungere accelerazione e attrito
Per una sensazione più fluida con accelerazione e decelerazione:
extends CharacterBody2D
const SPEED = 200.0
const ACCELERATION = 1200.0
const FRICTION = 1000.0
func _physics_process(delta: float) -> void:
var input_direction := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
if input_direction != Vector2.ZERO:
velocity = velocity.move_toward(input_direction * SPEED, ACCELERATION * delta)
else:
velocity = velocity.move_toward(Vector2.ZERO, FRICTION * delta)
move_and_slide()
5. Controller 3D in terza persona
La variante 3D segue lo stesso schema. La differenza principale è lavorare con Vector3 e usare transform.basis per convertire l'input 2D in movimento nello spazio 3D.
extends CharacterBody3D
const SPEED = 5.0
const JUMP_VELOCITY = 4.5
func _physics_process(delta: float) -> void:
# Apply gravity
if not is_on_floor():
velocity += get_gravity() * delta
# Jump
if Input.is_action_just_pressed("ui_accept") and is_on_floor():
velocity.y = JUMP_VELOCITY
# Get input and convert to 3D direction
var input_dir := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
if direction:
velocity.x = direction.x * SPEED
velocity.z = direction.z * SPEED
else:
velocity.x = move_toward(velocity.x, 0, SPEED)
velocity.z = move_toward(velocity.z, 0, SPEED)
move_and_slide()
Differenze principali rispetto al 2D
-
Unità: In 3D, 1 unità = 1 metro (convenzione). Quindi
SPEED = 5.0significa 5 metri al secondo — numeri molto più piccoli rispetto ai valori in pixel del 2D. -
La velocità di salto è positiva: In 3D l'asse Y punta verso l'alto (a differenza del 2D), quindi
JUMP_VELOCITY = 4.5è positivo. -
Trasformazione della direzione:
transform.basis * Vector3(input_dir.x, 0, input_dir.y)converte l'input 2D in una direzione 3D relativa all'orientamento del personaggio. La Y dell'input viene mappata sull'asse Z (avanti/indietro in 3D). -
Vengono controllati solo X e Z: Impostiamo
velocity.xevelocity.zseparatamente, lasciandovelocity.yper la gravità e il salto.
Il metodo get_gravity() è stato aggiunto in Godot 4.4. Per le versioni più vecchie usa Vector2(0, ProjectSettings.get_setting("physics/2d/default_gravity")) per il 2D, oppure Vector3(0, -ProjectSettings.get_setting("physics/3d/default_gravity"), 0) per il 3D.
6. Riferimento delle proprietà principali
Sia CharacterBody2D che CharacterBody3D condividono queste importanti proprietà. Regolale nell'Inspector o tramite codice.
| Proprietà | Tipo | Predefinito | Descrizione |
|---|---|---|---|
velocity |
Vector2 / Vector3 | Vector2.ZERO |
La velocità del personaggio. Impostala prima di chiamare move_and_slide(). Dopo la chiamata viene aggiornata con la velocità risultante.
|
floor_snap_length |
float | 1.0 |
Sostituisce move_and_slide_with_snap(). Distanza con cui il personaggio viene agganciato al pavimento. Imposta a 0 per disattivarlo. Utile per pendii e scale.
|
up_direction |
Vector2 / Vector3 | Vector2.UP |
Definisce quale direzione è "su". Questo determina cosa viene considerato pavimento, muro o soffitto. Il valore predefinito è (0, -1) in 2D, (0, 1, 0) in 3D.
|
floor_stop_on_slope |
bool | true |
Se true, il personaggio non scivola giù per i pendii quando è fermo. Essenziale per i platformer. |
floor_max_angle |
float | 0.785 (45°) |
Angolo massimo di pendenza percorribile in radianti. Le superfici più ripide vengono trattate come muri. Usa deg_to_rad(60) per impostare 60 gradi.
|
max_slides |
int | 6 |
Numero massimo di iterazioni di collisione per chiamata a move_and_slide(). Valori più alti sono più precisi ma più lenti.
|
wall_min_slide_angle |
float | 0.262 (15°) |
Angolo minimo per lo scivolamento sui muri. Impedisce al personaggio di rimanere bloccato su muri quasi paralleli. |
platform_on_leave |
PlatformOnLeave | ADD_VELOCITY |
Comportamento quando si lascia una piattaforma mobile. ADD_VELOCITY preserva il momento, ADD_UPWARD_VELOCITY aggiunge solo la componente verso l'alto, DO_NOTHING ignora la velocità della piattaforma.
|
slide_on_ceiling |
bool | true |
Se true, consente lo scivolamento sui soffitti. Se false, arresta la velocità orizzontale al contatto con il soffitto. |
7. Rilevamento delle collisioni dopo move_and_slide()
Dopo aver chiamato move_and_slide(), puoi ispezionare le collisioni verificatesi. È utile per reagire a determinati tipi di collider, riprodurre effetti sonori all'atterraggio o implementare meccaniche di wall-jump.
func _physics_process(delta: float) -> void:
# ... set velocity here ...
move_and_slide()
# Check all collisions from this frame
for i in get_slide_collision_count():
var collision := get_slide_collision(i)
var collider := collision.get_collider()
print("Collided with: ", collider.name)
print("Normal: ", collision.get_normal())
print("Position: ", collision.get_position())
# Practical example: bounce off enemies
for i in get_slide_collision_count():
var collision := get_slide_collision(i)
if collision.get_collider().is_in_group("enemies"):
velocity = collision.get_normal() * 300
break
Metodi utili di KinematicCollision
get_collider()— L'oggetto che è stato colpito.get_normal()— La normale della superficie di collisione. Punta lontano dalla superficie.get_position()— Il punto nello spazio in cui si è verificata la collisione.get_travel()— Quanto il body si è spostato prima della collisione.get_remainder()— Il movimento rimanente che non è stato applicato.get_collider_velocity()— Velocità del collider (utile per le piattaforme mobili).
8. Cheat sheet di migrazione
Riferimento rapido per riscrivere il codice di Godot 3 in Godot 4:
| Godot 3 | Godot 4 |
|---|---|
KinematicBody2D |
CharacterBody2D |
KinematicBody3D |
CharacterBody3D |
velocity = move_and_slide(velocity, Vector2.UP) |
velocity = ... |
move_and_slide_with_snap(vel, snap, up) |
floor_snap_length = 4.0 |
var vel = move_and_slide(...)restituisce velocity |
move_and_slide()la proprietà velocity viene aggiornata direttamente |
var gravity = ProjectSettings.get("physics/2d/default_gravity") |
get_gravity() (4.4+) |
move_and_slide(..., up_direction, ...) |
up_direction = Vector2.UP (impostata come proprietà) |
move_and_slide(..., stop_on_slope, ...) |
floor_stop_on_slope = true (impostata come proprietà) |
Quando apri un progetto Godot 3 in Godot 4, l'engine propone di convertire il tuo progetto. Rinomina i nodi e tenta di aggiornare gli script, ma non gestisce sempre correttamente le chiamate a move_and_slide(). Probabilmente dovrai correggere manualmente il tuo codice di movimento.
9. Errori comuni
Errore 1: passare argomenti a move_and_slide()
# ERROR: move_and_slide() takes no arguments in Godot 4
velocity = move_and_slide(velocity, Vector2.UP)
# Set velocity, then call move_and_slide() with no arguments
velocity.x = direction * SPEED
move_and_slide()
Errore 2: dimenticare di impostare velocity prima di move_and_slide()
func _physics_process(delta: float) -> void:
move_and_slide() # velocity is Vector2.ZERO — nothing happens
Errore 3: usare _process() invece di _physics_process()
move_and_slide() deve essere chiamato in _physics_process(), che gira a una frequenza fissa (60 Hz per impostazione predefinita). Usare _process() lega la fisica al frame rate di rendering e provoca un comportamento incoerente.
Errore 4: non configurare CollisionShape
Un CharacterBody2D/CharacterBody3D senza un figlio CollisionShape attraversa tutto. Godot mostra un'icona di avviso nell'editor se la forma di collisione manca.
Errore 5: sovrascrivere velocity.y con la gravità a ogni frame
# This overwrites any jump velocity!
velocity.y = gravity * delta # should be +=, not =
# Gravity accumulates over time
velocity += get_gravity() * delta
Errore 6: applicare la gravità mentre si è a terra
Racchiudi sempre la gravità in if not is_on_floor(). Senza questo controllo, la gravità continua ad accumularsi mentre si è a terra. Quando il personaggio cammina oltre un bordo, precipita a una velocità estrema invece di cadere in modo naturale.