1. Einführung
Godot 4 hat große Änderungen an der Charakterbewegung eingeführt. Die alten Nodes KinematicBody2D und KinematicBody3D wurden in CharacterBody2D und CharacterBody3D umbenannt, und die move_and_slide()-API wurde komplett neu gestaltet. Ob du von Godot 3 migrierst oder ganz neu anfängst — dieser Leitfaden deckt alles ab, was du wissen musst.
CharacterBody2D und CharacterBody3D sind Physik-Bodies, die für per Code gesteuerte Charaktere gedacht sind. Anders als RigidBody reagieren sie nicht automatisch auf Kräfte — du steuerst ihre Bewegung vollständig per Skript. Das macht sie ideal für Spielfiguren, NPCs und alles, was präzise, deterministische Bewegung braucht.
2. Was sich gegenüber Godot 3 geändert hat
Hier sind die wichtigsten Änderungen bei der Charakterbewegung zwischen Godot 3 und Godot 4:
-
Aus
KinematicBody2Dwird jetztCharacterBody2D -
Aus
KinematicBody3Dwird jetztCharacterBody3D -
velocityist jetzt eine eingebaute Eigenschaft — du übergibst sie nicht mehr anmove_and_slide() -
move_and_slide()nimmt keine Argumente mehr entgegen — es liest direkt aus dervelocity-Eigenschaft -
move_and_slide_with_snap()wurde entfernt — verwende stattdessen die Eigenschaftfloor_snap_length -
is_on_floor(),is_on_wall(),is_on_ceiling()funktionieren weiterhin genauso -
get_gravity()wurde in Godot 4.4 hinzugefügt, um die Standard-Gravitation des Projekts als Vector auszulesen — sehr praktisch
In Godot 3 gab move_and_slide() die resultierende Geschwindigkeit zurück. In Godot 4 gibt es einen bool zurück (ob eine Kollision aufgetreten ist), und die Geschwindigkeit wird direkt in der velocity-Eigenschaft aktualisiert.
3. Grundlegender 2D-Platformer-Controller
Dies ist der standardmäßige Side-Scrolling-Platformer-Controller. Der Charakter kann sich nach links/rechts bewegen und springen, wenn er am Boden ist. Das ist die Vorlage, die Godot beim Erstellen eines neuen CharacterBody2D-Skripts generiert.
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()
Zeile-für-Zeile-Erklärung
-
extends CharacterBody2D— Das Skript erbt von CharacterBody2D und gibt uns Zugriff aufvelocity,move_and_slide(),is_on_floor()usw. -
SPEED = 300.0— Horizontale Bewegungsgeschwindigkeit in Pixeln pro Sekunde. -
JUMP_VELOCITY = -400.0— Negativ, weil die Y-Achse in 2D nach unten zeigt. Ein negativer Wert bewegt den Charakter nach oben. -
get_gravity() * delta— Wendet die Projekt-Gravitation in jedem Frame an.get_gravity()gibt einen nach unten zeigendenVector2zurück (z. B.Vector2(0, 980)). Die Multiplikation mitdeltamacht das Ganze framerate-unabhängig. -
Input.get_axis("ui_left", "ui_right")— Gibt je nach gedrückter Richtungstaste einen Float von -1.0 bis 1.0 zurück. Unterstützt analoge Eingabe. -
move_toward(velocity.x, 0, SPEED)— Bremst sanft auf null ab, wenn keine Eingabe erfolgt. Das dritte Argument ist die maximale Änderung pro Aufruf. -
move_and_slide()— Bewegt den Body mit der aktuellenvelocity, behandelt Kollisionen, gleitet an Oberflächen entlang und aktualisiertvelocityautomatisch.
Dein CharacterBody2D braucht mindestens einen CollisionShape2D-Kind-Node mit einer zugewiesenen Form (z. B. RectangleShape2D oder CapsuleShape2D). Ohne diese erkennt move_and_slide() keine Kollisionen.
4. Grundlegender 2D-Top-Down-Controller
Für Top-Down-Spiele (RPGs, Twin-Stick-Shooter usw.) bewegst du dich ohne Gravitation in alle vier Richtungen. Der Code ist einfacher als bei einem 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()
So funktioniert es
-
Input.get_vector()gibt einen normalisiertenVector2aus vier Eingabe-Aktionen zurück. Damit wird die diagonale Bewegung korrekt behandelt — die Länge des Vektors ist immer 1.0 oder 0.0, sodass diagonale Bewegung nicht schneller ist als Bewegung entlang der Achsen. -
Es wird keine Gravitation angewendet, da es sich um eine Top-Down-Ansicht handelt. Der Charakter stoppt sofort, wenn keine Eingabe erfolgt, weil wir
velocitydirekt setzen (keine Trägheit).
Beschleunigung und Reibung hinzufügen
Für ein weicheres Gefühl mit Beschleunigung und Abbremsen:
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. 3D-Third-Person-Controller
Die 3D-Variante folgt demselben Muster. Der Hauptunterschied ist die Arbeit mit Vector3 und die Nutzung von transform.basis, um 2D-Eingaben in Bewegung im 3D-Weltraum umzuwandeln.
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()
Wichtige Unterschiede zu 2D
-
Einheiten: In 3D gilt 1 Einheit = 1 Meter (Konvention).
SPEED = 5.0bedeutet also 5 Meter pro Sekunde — viel kleinere Zahlen als bei 2D-Pixelwerten. -
Sprunggeschwindigkeit ist positiv: In 3D zeigt die Y-Achse nach oben (anders als in 2D), daher ist
JUMP_VELOCITY = 4.5positiv. -
Richtungs-Transformation:
transform.basis * Vector3(input_dir.x, 0, input_dir.y)wandelt 2D-Eingaben in eine 3D-Richtung relativ zur Blickrichtung des Charakters um. Das Y der Eingabe wird auf die Z-Achse (vorne/hinten in 3D) abgebildet. -
Nur X und Z werden gesteuert: Wir setzen
velocity.xundvelocity.zseparat und lassenvelocity.yfür Gravitation und Springen übrig.
Die Methode get_gravity() wurde in Godot 4.4 hinzugefügt. Verwende für ältere Versionen Vector2(0, ProjectSettings.get_setting("physics/2d/default_gravity")) für 2D oder Vector3(0, -ProjectSettings.get_setting("physics/3d/default_gravity"), 0) für 3D.
6. Referenz der wichtigsten Eigenschaften
Sowohl CharacterBody2D als auch CharacterBody3D teilen sich diese wichtigen Eigenschaften. Passe sie im Inspektor oder per Code an.
| Eigenschaft | Typ | Standard | Beschreibung |
|---|---|---|---|
velocity |
Vector2 / Vector3 | Vector2.ZERO |
Die Geschwindigkeit des Charakters. Setze sie, bevor du move_and_slide() aufrufst. Nach dem Aufruf wird sie mit der resultierenden Geschwindigkeit aktualisiert.
|
floor_snap_length |
float | 1.0 |
Ersetzt move_and_slide_with_snap(). Distanz, mit der der Charakter an den Boden gesnappt wird. Auf 0 setzen, um es zu deaktivieren. Nützlich für Hänge und Treppen.
|
up_direction |
Vector2 / Vector3 | Vector2.UP |
Legt fest, welche Richtung „oben“ ist. Das bestimmt, was als Boden, Wand oder Decke zählt. Standard ist (0, -1) in 2D, (0, 1, 0) in 3D.
|
floor_stop_on_slope |
bool | true |
Wenn true, rutscht der Charakter im Stillstand nicht an Hängen hinunter. Für Platformer unerlässlich. |
floor_max_angle |
float | 0.785 (45°) |
Maximaler begehbarer Neigungswinkel in Radiant. Steilere Oberflächen werden als Wände behandelt. Verwende deg_to_rad(60), um 60 Grad einzustellen.
|
max_slides |
int | 6 |
Maximale Anzahl an Kollisions-Iterationen pro move_and_slide()-Aufruf. Höhere Werte sind genauer, aber langsamer.
|
wall_min_slide_angle |
float | 0.262 (15°) |
Minimaler Winkel für das Gleiten an Wänden. Verhindert, dass der Charakter an nahezu parallelen Wänden hängen bleibt. |
platform_on_leave |
PlatformOnLeave | ADD_VELOCITY |
Verhalten beim Verlassen einer beweglichen Plattform. ADD_VELOCITY erhält den Impuls, ADD_UPWARD_VELOCITY fügt nur die Aufwärtskomponente hinzu, DO_NOTHING ignoriert die Plattformgeschwindigkeit.
|
slide_on_ceiling |
bool | true |
Wenn true, ist Gleiten an Decken erlaubt. Wenn false, wird die horizontale Geschwindigkeit beim Deckenkontakt gestoppt. |
7. Kollisionserkennung nach move_and_slide()
Nach dem Aufruf von move_and_slide() kannst du die aufgetretenen Kollisionen inspizieren. Das ist nützlich, um auf bestimmte Collider-Typen zu reagieren, beim Landen Soundeffekte abzuspielen oder Wall-Jump-Mechaniken umzusetzen.
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
Nützliche KinematicCollision-Methoden
get_collider()— Das Objekt, das getroffen wurde.get_normal()— Die Normale der Kollisionsfläche. Zeigt von der Oberfläche weg.get_position()— Der Punkt im Weltraum, an dem die Kollision aufgetreten ist.get_travel()— Wie weit sich der Body vor der Kollision bewegt hat.get_remainder()— Die verbleibende Bewegung, die nicht angewendet wurde.get_collider_velocity()— Geschwindigkeit des Colliders (nützlich für bewegliche Plattformen).
8. Migrations-Spickzettel
Schnelle Referenz zum Umschreiben von Godot-3-Code auf 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(...)gibt velocity zurück |
move_and_slide()velocity-Eigenschaft wird direkt aktualisiert |
var gravity = ProjectSettings.get("physics/2d/default_gravity") |
get_gravity() (4.4+) |
move_and_slide(..., up_direction, ...) |
up_direction = Vector2.UP (als Eigenschaft gesetzt) |
move_and_slide(..., stop_on_slope, ...) |
floor_stop_on_slope = true (als Eigenschaft gesetzt) |
Wenn du ein Godot-3-Projekt in Godot 4 öffnest, bietet die Engine an, dein Projekt zu konvertieren. Sie benennt Nodes um und versucht, Skripte zu aktualisieren, bekommt move_and_slide()-Aufrufe aber nicht immer richtig hin. Wahrscheinlich musst du deinen Bewegungscode manuell korrigieren.
9. Häufige Fehler
Fehler 1: Argumente an move_and_slide() übergeben
# 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()
Fehler 2: Vergessen, velocity vor move_and_slide() zu setzen
func _physics_process(delta: float) -> void:
move_and_slide() # velocity is Vector2.ZERO — nothing happens
Fehler 3: _process() statt _physics_process() verwenden
move_and_slide() muss in _physics_process() aufgerufen werden, das mit einer festen Rate läuft (standardmäßig 60 Hz). _process() zu verwenden koppelt die Physik an die Render-Framerate und führt zu inkonsistentem Verhalten.
Fehler 4: CollisionShape nicht einrichten
Ein CharacterBody2D/CharacterBody3D ohne CollisionShape-Kind bewegt sich durch alles hindurch. Godot zeigt im Editor ein Warnsymbol an, wenn die Kollisionsform fehlt.
Fehler 5: velocity.y in jedem Frame mit Gravitation überschreiben
# This overwrites any jump velocity!
velocity.y = gravity * delta # should be +=, not =
# Gravity accumulates over time
velocity += get_gravity() * delta
Fehler 6: Gravitation anwenden, während man am Boden ist
Umschließe die Gravitation immer mit if not is_on_floor(). Ohne diese Prüfung akkumuliert die Gravitation weiter, während man am Boden ist. Wenn der Charakter von einer Kante läuft, stürzt er mit extremer Geschwindigkeit ab, statt natürlich zu fallen.