1. Wprowadzenie
Godot 4 wprowadził poważne zmiany w ruchu postaci. Stare węzły KinematicBody2D i KinematicBody3D zostały przemianowane na CharacterBody2D i CharacterBody3D, a API move_and_slide() zostało całkowicie przeprojektowane. Niezależnie od tego, czy migrujesz z Godot 3, czy zaczynasz od zera, ten poradnik obejmuje wszystko, co musisz wiedzieć.
CharacterBody2D i CharacterBody3D to ciała fizyczne przeznaczone dla postaci kontrolowanych przez kod. W przeciwieństwie do RigidBody nie reagują automatycznie na siły — ich ruchem sterujesz w całości za pomocą skryptu. Sprawia to, że są idealne dla postaci gracza, NPC i wszystkiego, co wymaga precyzyjnego, deterministycznego ruchu.
2. Co się zmieniło względem Godot 3
Oto najważniejsze zmiany w ruchu postaci między Godot 3 a Godot 4:
-
KinematicBody2Dto terazCharacterBody2D -
KinematicBody3Dto terazCharacterBody3D -
velocityjest teraz wbudowaną właściwością — nie przekazujesz jej już domove_and_slide() -
move_and_slide()nie przyjmuje żadnych argumentów — odczytuje bezpośrednio z właściwościvelocity -
move_and_slide_with_snap()zostało usunięte — zamiast tego użyj właściwościfloor_snap_length -
is_on_floor(),is_on_wall(),is_on_ceiling()działają tak samo jak wcześniej -
get_gravity()zostało dodane w Godot 4.4 do odczytu domyślnej grawitacji projektu jako wektora — bardzo wygodne
W Godot 3 move_and_slide() zwracało wynikową prędkość. W Godot 4 zwraca bool (czy wystąpiła kolizja), a prędkość jest aktualizowana w miejscu na właściwości velocity.
3. Podstawowy kontroler platformówki 2D
To standardowy kontroler platformówki z przewijaniem bocznym. Postać może poruszać się w lewo/prawo i skakać, gdy jest na ziemi. To szablon, który Godot generuje, gdy tworzysz nowy skrypt 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()
Analiza wiersz po wierszu
-
extends CharacterBody2D— Skrypt dziedziczy po CharacterBody2D, dając nam dostęp dovelocity,move_and_slide(),is_on_floor()itd. -
SPEED = 300.0— Prędkość ruchu poziomego w pikselach na sekundę. -
JUMP_VELOCITY = -400.0— Ujemna, ponieważ oś Y w 2D wskazuje w dół. Wartość ujemna przesuwa postać do góry. -
get_gravity() * delta— Stosuje grawitację projektu w każdej klatce.get_gravity()zwracaVector2skierowany w dół (np.Vector2(0, 980)). Mnożenie przezdeltauniezależnia to od liczby klatek na sekundę. -
Input.get_axis("ui_left", "ui_right")— Zwraca liczbę zmiennoprzecinkową od -1.0 do 1.0 w zależności od wciśniętego klawisza kierunku. Obsługuje wejście analogowe. -
move_toward(velocity.x, 0, SPEED)— Płynnie zwalnia do zera, gdy nie jest wciśnięty żaden klawisz. Trzeci argument to maksymalna zmiana na wywołanie. -
move_and_slide()— Porusza ciałem, używając bieżącejvelocity, obsługuje kolizje, ślizga się wzdłuż powierzchni i automatycznie aktualizujevelocity.
Twój CharacterBody2D potrzebuje co najmniej jednego węzła potomnego CollisionShape2D z przypisanym kształtem (np. RectangleShape2D lub CapsuleShape2D). Bez niego move_and_slide() nie wykryje żadnych kolizji.
4. Podstawowy kontroler top-down 2D
W grach z widokiem z góry (RPG, twin-stick shootery itp.) poruszasz się we wszystkich czterech kierunkach bez grawitacji. Kod jest prostszy niż w platformówce.
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()
Jak to działa
-
Input.get_vector()zwraca znormalizowanyVector2z czterech akcji wejściowych. Poprawnie obsługuje ruch po przekątnej — długość wektora zawsze wynosi 1.0 lub 0.0, więc ruch po przekątnej nie jest szybszy niż ruch w kierunkach głównych. -
Grawitacja nie jest stosowana, ponieważ to widok z góry. Postać zatrzymuje się natychmiast, gdy nie ma wejścia, ponieważ ustawiamy
velocitybezpośrednio (brak bezwładności).
Dodawanie przyspieszenia i tarcia
Aby uzyskać płynniejsze wrażenie z przyspieszaniem i zwalnianiem:
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. Kontroler trzecioosobowy 3D
Wersja 3D podąża za tym samym wzorcem. Główna różnica polega na pracy z Vector3 i użyciu transform.basis do przekształcenia wejścia 2D w ruch w przestrzeni świata 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()
Kluczowe różnice względem 2D
-
Jednostki: W 3D 1 jednostka = 1 metr (konwencja). Więc
SPEED = 5.0oznacza 5 metrów na sekundę — znacznie mniejsze liczby niż wartości pikselowe w 2D. -
Prędkość skoku jest dodatnia: W 3D oś Y wskazuje w górę (inaczej niż w 2D), więc
JUMP_VELOCITY = 4.5jest dodatnia. -
Przekształcenie kierunku:
transform.basis * Vector3(input_dir.x, 0, input_dir.y)przekształca wejście 2D w kierunek 3D względem zwrotu postaci. Y wejścia mapuje się na oś Z (przód/tył w 3D). -
Kontrolowane są tylko X i Z: Ustawiamy
velocity.xivelocity.zosobno, pozostawiającvelocity.ydla grawitacji i skoku.
Metoda get_gravity() została dodana w Godot 4.4. W starszych wersjach użyj Vector2(0, ProjectSettings.get_setting("physics/2d/default_gravity")) dla 2D lub Vector3(0, -ProjectSettings.get_setting("physics/3d/default_gravity"), 0) dla 3D.
6. Przegląd kluczowych właściwości
Zarówno CharacterBody2D, jak i CharacterBody3D mają te ważne właściwości. Dostosuj je w Inspektorze lub za pomocą kodu.
| Właściwość | Typ | Domyślnie | Opis |
|---|---|---|---|
velocity |
Vector2 / Vector3 | Vector2.ZERO |
Prędkość postaci. Ustaw ją przed wywołaniem move_and_slide(). Aktualizowana po wywołaniu wynikową prędkością.
|
floor_snap_length |
float | 1.0 |
Zastępuje move_and_slide_with_snap(). Odległość przyciągania postaci do podłoża. Ustaw na 0, aby wyłączyć. Przydatne przy zboczach i schodach.
|
up_direction |
Vector2 / Vector3 | Vector2.UP |
Definiuje, który kierunek jest „górą". To określa, co liczy się jako podłoga, ściana lub sufit. Domyślnie (0, -1) w 2D, (0, 1, 0) w 3D.
|
floor_stop_on_slope |
bool | true |
Gdy true, postać nie ześlizguje się ze zboczy stojąc nieruchomo. Niezbędne w platformówkach. |
floor_max_angle |
float | 0.785 (45°) |
Maksymalny kąt zbocza, po którym można chodzić, w radianach. Powierzchnie bardziej strome są traktowane jak ściany. Użyj deg_to_rad(60), aby ustawić 60 stopni.
|
max_slides |
int | 6 |
Maksymalna liczba iteracji kolizji na wywołanie move_and_slide(). Wyższe wartości są dokładniejsze, ale wolniejsze.
|
wall_min_slide_angle |
float | 0.262 (15°) |
Minimalny kąt ślizgania po ścianie. Zapobiega zaklinowaniu się postaci na niemal równoległych ścianach. |
platform_on_leave |
PlatformOnLeave | ADD_VELOCITY |
Zachowanie przy opuszczaniu ruchomej platformy. ADD_VELOCITY zachowuje pęd, ADD_UPWARD_VELOCITY dodaje tylko składową w górę, DO_NOTHING ignoruje prędkość platformy.
|
slide_on_ceiling |
bool | true |
Gdy true, pozwala na ślizganie się po sufitach. Gdy false, zatrzymuje prędkość poziomą przy uderzeniu w sufit. |
7. Wykrywanie kolizji po move_and_slide()
Po wywołaniu move_and_slide() możesz zbadać kolizje, które wystąpiły. Jest to przydatne do reagowania na określone typy colliderów, odtwarzania efektów dźwiękowych przy lądowaniu lub implementacji mechaniki wall-jumpa.
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
Przydatne metody KinematicCollision
get_collider()— Obiekt, który został trafiony.get_normal()— Normalna powierzchni kolizji. Wskazuje na zewnątrz od powierzchni.get_position()— Punkt w przestrzeni świata, w którym doszło do kolizji.get_travel()— Jak daleko ciało przemieściło się przed kolizją.get_remainder()— Pozostały ruch, który nie został zastosowany.get_collider_velocity()— Prędkość collidera (przydatna dla ruchomych platform).
8. Ściąga migracyjna
Szybki punkt odniesienia do konwersji kodu z Godot 3 na 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(...)zwraca prędkość |
move_and_slide()właściwość velocity jest aktualizowana w miejscu |
var gravity = ProjectSettings.get("physics/2d/default_gravity") |
get_gravity() (4.4+) |
move_and_slide(..., up_direction, ...) |
up_direction = Vector2.UP (ustawione jako właściwość) |
move_and_slide(..., stop_on_slope, ...) |
floor_stop_on_slope = true (ustawione jako właściwość) |
Przy otwieraniu projektu z Godot 3 w Godot 4 silnik proponuje konwersję twojego projektu. Zmienia nazwy węzłów i próbuje zaktualizować skrypty, ale nie zawsze poprawnie obsługuje wywołania move_and_slide(). Prawdopodobnie będziesz musiał ręcznie poprawić swój kod ruchu.
9. Typowe błędy
Błąd 1: Przekazywanie argumentów do 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()
Błąd 2: Zapomnienie o ustawieniu velocity przed move_and_slide()
func _physics_process(delta: float) -> void:
move_and_slide() # velocity is Vector2.ZERO — nothing happens
Błąd 3: Używanie _process() zamiast _physics_process()
move_and_slide() musi być wywoływane w _physics_process(), które działa ze stałą częstotliwością (domyślnie 60 Hz). Użycie _process() wiąże fizykę z liczbą klatek renderowania, powodując niespójne zachowanie.
Błąd 4: Brak konfiguracji CollisionShape
CharacterBody2D/CharacterBody3D bez potomka CollisionShape będzie przechodzić przez wszystko. Godot pokazuje ikonę ostrzeżenia w edytorze, jeśli kształt kolizji jest brakujący.
Błąd 5: Nadpisywanie velocity.y grawitacją w każdej klatce
# This overwrites any jump velocity!
velocity.y = gravity * delta # should be +=, not =
# Gravity accumulates over time
velocity += get_gravity() * delta
Błąd 6: Stosowanie grawitacji, gdy postać jest na podłodze
Zawsze otaczaj grawitację warunkiem if not is_on_floor(). Bez tego sprawdzenia grawitacja akumuluje się dalej, gdy postać jest na podłodze. Gdy postać zejdzie z krawędzi, spadnie z ekstremalną prędkością zamiast spadać naturalnie.