«ОН ХОДИТ, БЕГАЕТ, ПРЫГАЕТ И ДАЖЕ НЕ ПРОВАЛИВАЕТСЯ ПОД ТЕКСТУРЫ»
Колян ожил. Нет, серьёзно — он теперь не просто капсула с камерой, которая скользит над землей. Он ходит. Бегает. Прыгает. Приседает. Ускоряется. У него есть анимации. И это заняло примерно неделю вечеров, два литра чая и один момент, когда я всерьёз хотел всё переписать на кубах без анимаций вообще.
Но давайте по порядку.
СТРУКТУРА ПЕРСОНАЖА (ИЛИ КАК Я РАЗНЁС ВСЁ ПО КОМПОНЕНТАМ)
Я фронтендер. Я привык, что всё — компоненты. Кнопка, форма, модалка — каждая живёт своей жизнью и не мешает соседу. Поэтому персонажа я собрал так же.
Сцена Player — это CharacterBody3D. Внутри не куча кода в одном файле, а россыпь дочерних нод-компонентов. Каждая отвечает за что-то одно:
Player — сам персонаж, CharacterBody3D. Главный узел, в нём всё.
CollisionShape3D — капсула. Радиус 0.4, высота 1.8. То, чем Колян бьётся об стены.
ModelRoot > CharacterModel — 3D-модель из ассета CasualMale. Временная, потом заменю на свою.
Head — точка на высоте 1.62. К ней привязана камера и всё, что должно быть «на глазах»:
Camera3D — глаза игрока.
InteractRay — луч для взаимодействия с предметами и дверями. Бьёт на 3.2 метра вперёд.
Flashlight — SpotLight3D. Тёплый свет, тени. Фонарик. Работает.
HoldSocket — Marker3D. Место перед камерой, где будет болтаться поднятый предмет. Когда руки доделаю.
GroundSurfaceRay — луч вниз. Понимает, на чём Колян стоит: асфальт, гравий, трава, бетон. Нужен для звуков шагов.
FootstepAudio — AudioStreamPlayer3D. Проигрывает шаги. Звук зависит от поверхности.
Components — папка с мозгами. Каждый компонент — отдельный скрипт:
Movement — движение. Вперёд, назад, вбок, бег, прыжок, приседание. Считает скорость, разгон, усталость. Следит за вводом с клавиатуры.
Camera — камера. Мышь, поворот, зум, наклон головы при ходьбе, тряска при приземлении.
Interaction — логика взаимодействия. Луч попал в предмет — показывает подсказку. Нажал E — подобрал, открыл дверь, дёрнул рычаг.
Vitals — выживание. Здоровье, голод, жажда, усталость. Каждый тикает по таймеру. Если выносливость на нуле — Movement не даёт бежать.
Flashlight — включение и выключение фонарика. Следит за батарейкой (потом добавится).
Hands — руки. Поднять предмет, бросить, осмотреть. Сейчас в разработке.
Footsteps — звуки шагов. Принимает сигнал от Movement, проверяет поверхность через GroundSurfaceRay, включает нужный звук.
Animation — анимации. BlendTree, переходы idle/walk/run/jump/crouch. Отдельная боль, описанная выше.
Зачем так дробить? Потому что когда у тебя один скрипт на 600 строк, который управляет всем — ты умрёшь его дебажить. А когда Movement отдельно, Vitals отдельно, Animation отдельно — ты открываешь нужный файл, правишь пять строк и оно работает. Ну или ломается, но ты хотя бы знаешь где копать.
Player.gd при этом — дирижёр. Он только связывает компоненты сигналами и пробрасывает вызовы:
movement.stamina_requested.connect(vitals.consume_stamina) movement.footstep_requested.connect(footsteps.play_step) movement.movement_state_changed.connect(_on_movement_state_changed) movement.landed.connect(_on_landed) vitals.exhausted_changed.connect(movement.set_exhausted)
Движение ловит ввод, камера следит за мышью, анимация дёргает скелет, виталы следят за голодом. И никто никому не мешает. Мечта.
АНИМАЦИИ. БОЛЬ. BLEND TREE
Вот тут я остановлюсь подробнее, потому что это был ад.
3D-модель персонажа — из ассета (CasualMale). Скелет есть, анимации есть: idle, walk, run, jump_start, jump_loop, crouch_idle, crouch_walk. Всё это нужно было завести так, чтобы персонаж не дёргался при переходе от ходьбы к бегу, не скользил по земле и не улетал в T-pose при резкой смене состояний.
Панель Animation в Godot. Каждая кость скелета — три дорожки вращения (X, Y, Z). Ключи на дорожках задают позу в конкретный момент времени.
В Godot для этого есть AnimationTree и BlendSpace2D. Работает так: у тебя есть двумерное поле, где по одной оси — скорость (от стояния до бега), по второй — направление (вперёд, вбок). Ты раскидываешь анимации по этому полю, а Godot сам их смешивает. На бумаге — красиво. На деле — я убил три вечера только на то, чтобы персонаж не дёргал ногой при переходе с idle на walk.
Главная проблема: ассетные анимации не знают про твой масштаб, твою скорость движения и твои кривые. Если персонаж скользит (ноги двигаются быстрее, чем тело) — это не баг движка, это ты неправильно настроил скорость корневой ноды. Если он резко встаёт из приседа — ты забыл transition в AnimationNodeStateMachine.
Сейчас всё работает: ходьба, бег, прыжок с фазой взлёта и приземления, приседание с отдельной idle-анимацией. Но осадочек остался. Анимации — это не код. Код ты можешь отдебажить, поставить breakpoint, понять. Анимации — это метод тыка, визуальный подбор и молитва.
ЧТО ДЕЛАЮ ПРЯМО СЕЙЧАС
Руки. Буквально. Персонаж должен уметь:
Открывать дверь машины (анимация руки тянется к ручке).
Поднимать предмет с земли (наклон, рука вниз).
Держать предмет перед собой (предмет крепится к HoldSocket на камере).
Это отдельная боль. Анимации рук должны смешиваться с анимациями тела через AnimationLayer — то есть ноги идут, а руки в этот момент делают что-то своё. В теории Godot умеет так делать. На практике я пока закопался в документацию и форумы.
Как добью — покажу гифку. Если кто работал с AnimationLayer в Godot 4 и знает, как не сломать позвоночник персонажу при смешивании — маякните в комментах. Реально надо.
МИР — ПОКА ПРИМИТИВЫ
Отдельно не показываю (следующий пост будет про мир), но упомянуть надо. Весь мир сейчас — серые боксы, кубы и цилиндры. Заправка, дорога, здание, колонки. Всё в масштабе игры, можно ходить, можно залезать в машину, можно ездить. Расстояния чувствуются. Свет и туман — уже работают. Но про это — в следующем посте, с картинками.
ЧТО ДАЛЬШЕ
Добить анимации рук (поднятие, дверь, держание).
Привязать предметы к HoldSocket.
Сделать пост про мир с нормальными скринами.
Демо всё ещё впереди, но Колян уже живой. Он двигается, дышит, устаёт. Осталось научить его не быть беспомощным перед открытой дверью машины.
Подписывайтесь, если тема заходит. Вопросы, советы, проклятия — в комменты.
P.S. Да, модель персонажа пока ассетная. Свою сделаю позже, когда руки дойдут. Сначала — геймплей.










