Движение персонажа: CharacterBody2D
CharacterBody2D — это узел для управляемого персонажа: задаёшь скорость, а движок сам двигает и обрабатывает столкновения.
Суть: ты выставляешь velocity (скорость со знаком направления) и зовёшь move_and_slide() — остальное движок делает сам.
Для главного героя в Godot 4 берут узел CharacterBody2D. Он специально создан под персонажа, которым управляет игрок: понимает столкновения со стенами, умеет скользить вдоль препятствий, но при этом полностью слушается твоего кода (в отличие от полностью физического тела, которое живёт само). У CharacterBody2D есть встроенное свойство velocity типа Vector2 — это текущая скорость персонажа по x и y. Ты её задаёшь, а потом вызываешь move_and_slide().
Важная особенность Godot 4: move_and_slide() вызывается без аргументов. В старом Godot 3 в неё передавали скорость, теперь же она сама берёт значение из свойства velocity и сама умножает на delta. Тебе остаётся лишь правильно задать velocity.
extends CharacterBody2D
@export var speed: float = 200.0
func _physics_process(delta: float) -> void:
var direction: Vector2 = Input.get_vector(
"move_left", "move_right", "move_up", "move_down")
velocity = direction * speed
move_and_slide()Всего три строки логики — и персонаж бегает во все стороны, скользит вдоль стен и не проходит сквозь препятствия. Это и есть сила специализированного узла: тебе не нужно вручную считать столкновения.
Полезно понимать, почему для персонажа берут именно CharacterBody2D, а не полноценное физическое тело RigidBody2D. RigidBody2D живёт по законам физики сам: толкни его — он покатится, отскочит, закрутится. Для управляемого героя это неудобно: игрок ждёт точного отклика на клавиши, а не «корабль на льду». CharacterBody2D даёт золотую середину — он чувствует стены и скользит вдоль них, но двигается строго так, как велит твой код. Поэтому именно его выбирают для героев и часто для врагов: предсказуемое управление плюс честные столкновения.
Как работает под капотом
move_and_slide() берёт velocity, умножает на delta и пытается сдвинуть тело. Если на пути стена, движок не вжимает персонажа в неё, а «скользит» вдоль: раскладывает движение на составляющую вдоль стены (её оставляет) и составляющую в стену (её гасит). Поэтому персонаж плавно проходит вдоль препятствий, а не залипает.
Смоделируем кадр движения на Python: направление умножаем на скорость, получаем смещение за кадр и обновляем позицию.
def move_step(pos, direction, speed, delta):
vx = direction[0] * speed
vy = direction[1] * speed
new_x = pos[0] + vx * delta
new_y = pos[1] + vy * delta
return (round(new_x, 1), round(new_y, 1))
pos = (0.0, 0.0)
speed = 200.0
delta = 1 / 60 # один кадр
# Игрок держит "вправо": direction = (1, 0)
for frame in range(3):
pos = move_step(pos, (1, 0), speed, delta)
print(f"Кадр {frame + 1}: позиция {pos}")
print("velocity = direction * speed, смещение = velocity * delta")Та же логика на Python ▶. Именно это move_and_slide делает за кулисами: velocity = направление * скорость, а смещение = velocity * delta. Движок лишь добавляет обработку стен.
Частые ошибки
Главная ошибка переходящих с Godot 3 — писать move_and_slide(velocity): в Godot 4 функция без аргументов, скорость берётся из свойства. Вторая — забывать умножать direction на speed: тогда velocity будет длиной 1 пиксель в секунду, и персонаж почти не двигается. Третья — двигать персонажа, меняя position напрямую вместо velocity: так теряются столкновения, и герой проходит сквозь стены. Четвёртая — вызывать всё это в _process, а не в _physics_process, отчего движение дёргается.
Best practices
Для управляемого героя бери именно CharacterBody2D. Скорость выноси в @export, чтобы крутить баланс из инспектора. Получай направление через Input.get_vector — он уже нормализован. Пиши velocity = direction * speed, затем move_and_slide() — этот шаблон закрывает топ-даун движение. И всегда держи движение в _physics_process.
Итоги: CharacterBody2D — узел управляемого персонажа со встроенным velocity. Шаблон топ-даун движения: получить направление через get_vector, умножить на скорость, присвоить velocity, вызвать move_and_slide() без аргументов. Движок сам умножает на delta и обрабатывает скольжение вдоль стен. Меняй velocity, а не position.