1. Введение
В Godot 4 внесены серьёзные изменения в перемещение персонажей. Старые узлы KinematicBody2D и KinematicBody3D были переименованы в CharacterBody2D и CharacterBody3D, а API move_and_slide() был полностью переработан. Если вы переходите с Godot 3 или начинаете с нуля, это руководство охватывает всё необходимое.
CharacterBody2D и CharacterBody3D — это физические тела, предназначенные для персонажей, управляемых кодом. В отличие от RigidBody, они не реагируют на силы автоматически — вы полностью управляете их движением через скрипт. Это делает их идеальными для игровых персонажей, NPC и всего, что требует точного и детерминированного движения.
2. Что изменилось по сравнению с Godot 3
Вот наиболее важные изменения в перемещении персонажей между Godot 3 и Godot 4:
-
KinematicBody2DтеперьCharacterBody2D -
KinematicBody3DтеперьCharacterBody3D -
velocityтеперь встроенное свойство — вы больше не передаёте его вmove_and_slide() -
move_and_slide()принимает ноль аргументов — он читает напрямую из свойстваvelocity -
move_and_slide_with_snap()удалён — используйте вместо него свойствоfloor_snap_length -
is_on_floor(),is_on_wall(),is_on_ceiling()по-прежнему работают так же -
get_gravity()был добавлен в Godot 4.4 для чтения гравитации проекта по умолчанию в виде вектора — очень удобно
В Godot 3 move_and_slide() возвращал итоговую скорость. В Godot 4 он возвращает bool (произошло ли столкновение), а скорость обновляется на месте в свойстве velocity.
3. Базовый 2D-контроллер платформера
Это стандартный контроллер платформера с горизонтальной прокруткой. Персонаж может двигаться влево/вправо и прыгать, находясь на земле. Это шаблон, который Godot генерирует при создании нового скрипта 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()
Построчный разбор
-
extends CharacterBody2D— Скрипт наследуется от CharacterBody2D, что даёт доступ кvelocity,move_and_slide(),is_on_floor()и т. д. -
SPEED = 300.0— Горизонтальная скорость движения в пикселях в секунду. -
JUMP_VELOCITY = -400.0— Отрицательное значение, потому что в 2D ось Y направлена вниз. Отрицательное значение перемещает персонажа вверх. -
get_gravity() * delta— Применяет гравитацию проекта каждый кадр.get_gravity()возвращаетVector2, направленный вниз (например,Vector2(0, 980)). Умножение наdeltaделает движение независимым от частоты кадров. -
Input.get_axis("ui_left", "ui_right")— Возвращает число с плавающей точкой от -1.0 до 1.0 в зависимости от того, какая клавиша направления нажата. Поддерживает аналоговый ввод. -
move_toward(velocity.x, 0, SPEED)— Плавно замедляется до нуля, когда ввод отсутствует. Третий аргумент — максимальное изменение за один вызов. -
move_and_slide()— Перемещает тело, используя текущуюvelocity, обрабатывает столкновения, скользит вдоль поверхностей и автоматически обновляетvelocity.
Вашему CharacterBody2D нужен как минимум один дочерний узел CollisionShape2D с назначенной формой (например, RectangleShape2D или CapsuleShape2D). Без него move_and_slide() не будет обнаруживать никаких столкновений.
4. Базовый 2D-контроллер вида сверху
Для игр с видом сверху (RPG, twin-stick шутеры и т. д.) вы двигаетесь во всех четырёх направлениях без гравитации. Код проще, чем у платформера.
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()
Как это работает
-
Input.get_vector()возвращает нормализованныйVector2из четырёх действий ввода. Это корректно обрабатывает диагональное движение — длина вектора всегда 1.0 или 0.0, поэтому диагональное движение не быстрее движения по осям. -
Гравитация не применяется, так как это вид сверху. Персонаж мгновенно останавливается при отсутствии ввода, потому что мы задаём
velocityнапрямую (без инерции).
Добавление ускорения и трения
Для более плавных ощущений с ускорением и замедлением:
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-контроллер от третьего лица
3D-версия следует тому же паттерну. Основное отличие — работа с Vector3 и использование transform.basis для преобразования 2D-ввода в движение в 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()
Ключевые отличия от 2D
-
Единицы измерения: В 3D 1 единица = 1 метр (по соглашению). Поэтому
SPEED = 5.0означает 5 метров в секунду — гораздо меньшие числа, чем пиксельные значения в 2D. -
Скорость прыжка положительна: В 3D ось Y направлена вверх (в отличие от 2D), поэтому
JUMP_VELOCITY = 4.5положительна. -
Преобразование направления:
transform.basis * Vector3(input_dir.x, 0, input_dir.y)преобразует 2D-ввод в 3D-направление относительно ориентации персонажа. Y ввода отображается на ось Z (вперёд/назад в 3D). -
Управляются только X и Z: Мы задаём
velocity.xиvelocity.zпо отдельности, оставляяvelocity.yдля гравитации и прыжков.
Метод get_gravity() был добавлен в Godot 4.4. Для более старых версий используйте Vector2(0, ProjectSettings.get_setting("physics/2d/default_gravity")) для 2D или Vector3(0, -ProjectSettings.get_setting("physics/3d/default_gravity"), 0) для 3D.
6. Справочник основных свойств
Как CharacterBody2D, так и CharacterBody3D имеют эти важные общие свойства. Настраивайте их в инспекторе или через код.
| Свойство | Тип | По умолчанию | Описание |
|---|---|---|---|
velocity |
Vector2 / Vector3 | Vector2.ZERO |
Скорость персонажа. Задайте её перед вызовом move_and_slide(). После вызова обновляется итоговой скоростью.
|
floor_snap_length |
float | 1.0 |
Заменяет move_and_slide_with_snap(). Расстояние привязки персонажа к полу. Установите 0 для отключения. Полезно для склонов и лестниц.
|
up_direction |
Vector2 / Vector3 | Vector2.UP |
Определяет, какое направление считается «вверх». Это определяет, что считается полом, стеной или потолком. По умолчанию (0, -1) в 2D, (0, 1, 0) в 3D.
|
floor_stop_on_slope |
bool | true |
Когда true, персонаж не соскальзывает вниз по склонам, стоя на месте. Необходимо для платформеров. |
floor_max_angle |
float | 0.785 (45°) |
Максимальный проходимый угол склона в радианах. Поверхности круче этого значения считаются стенами. Используйте deg_to_rad(60), чтобы задать 60 градусов.
|
max_slides |
int | 6 |
Максимальное число итераций столкновений за один вызов move_and_slide(). Более высокие значения точнее, но медленнее.
|
wall_min_slide_angle |
float | 0.262 (15°) |
Минимальный угол для скольжения по стене. Предотвращает застревание персонажа на почти параллельных стенах. |
platform_on_leave |
PlatformOnLeave | ADD_VELOCITY |
Поведение при сходе с движущейся платформы. ADD_VELOCITY сохраняет импульс, ADD_UPWARD_VELOCITY добавляет только вертикальную составляющую, DO_NOTHING игнорирует скорость платформы.
|
slide_on_ceiling |
bool | true |
Когда true, разрешает скольжение по потолкам. Когда false, останавливает горизонтальную скорость при ударе о потолок. |
7. Обнаружение столкновений после move_and_slide()
После вызова move_and_slide() вы можете проанализировать произошедшие столкновения. Это полезно для реакции на конкретные типы коллайдеров, воспроизведения звуковых эффектов при приземлении или реализации механики прыжка от стены.
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
Полезные методы KinematicCollision
get_collider()— Объект, в который произошёл удар.get_normal()— Нормаль поверхности столкновения. Направлена от поверхности.get_position()— Точка в мировом пространстве, где произошло столкновение.get_travel()— Насколько далеко тело переместилось перед столкновением.get_remainder()— Оставшееся движение, которое не было применено.get_collider_velocity()— Скорость коллайдера (полезно для движущихся платформ).
8. Шпаргалка по миграции
Быстрая справка по преобразованию кода Godot 3 в 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(...)возвращает velocity |
move_and_slide()свойство velocity обновляется на месте |
var gravity = ProjectSettings.get("physics/2d/default_gravity") |
get_gravity() (4.4+) |
move_and_slide(..., up_direction, ...) |
up_direction = Vector2.UP (задаётся как свойство) |
move_and_slide(..., stop_on_slope, ...) |
floor_stop_on_slope = true (задаётся как свойство) |
При открытии проекта Godot 3 в Godot 4 движок предлагает конвертировать проект. Он переименовывает узлы и пытается обновить скрипты, но не всегда правильно обрабатывает вызовы move_and_slide(). Скорее всего, вам придётся вручную исправить код перемещения.
9. Типичные ошибки
Ошибка 1: Передача аргументов в 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()
Ошибка 2: Забывают задать velocity перед move_and_slide()
func _physics_process(delta: float) -> void:
move_and_slide() # velocity is Vector2.ZERO — nothing happens
Ошибка 3: Использование _process() вместо _physics_process()
move_and_slide() должен вызываться в _physics_process(), который выполняется с фиксированной частотой (по умолчанию 60 Гц). Использование _process() привязывает физику к частоте кадров рендеринга, вызывая непоследовательное поведение.
Ошибка 4: Не настроен CollisionShape
CharacterBody2D/CharacterBody3D без дочернего CollisionShape будет проходить сквозь всё. Godot показывает значок предупреждения в редакторе, если форма столкновения отсутствует.
Ошибка 5: Перезапись velocity.y гравитацией каждый кадр
# This overwrites any jump velocity!
velocity.y = gravity * delta # should be +=, not =
# Gravity accumulates over time
velocity += get_gravity() * delta
Ошибка 6: Применение гравитации, когда персонаж на полу
Всегда оборачивайте гравитацию в if not is_on_floor(). Без этой проверки гравитация продолжает накапливаться, пока персонаж на полу. Когда персонаж сходит с уступа, он будет падать с экстремальной скоростью вместо естественного падения.