Движение персонажа: 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.

Проверьте себя
1. Как правильно вызвать move_and_slide() в Godot 4?
Amove_and_slide(velocity)
Bmove_and_slide() без аргументов, velocity берётся из свойства
Cmove_and_slide(position, speed)
Dmove_and_slide(delta)
2. Почему лучше менять velocity, а не position персонажа напрямую?
Aposition нельзя менять вообще
BПри смене velocity и move_and_slide движок обрабатывает столкновения, а прямая правка position проходит сквозь стены
Cvelocity работает быстрее
DТак короче код