12 KiB
CLAUDE.md
Заметки для агентов, работающих с этим репозиторием.
Что это за проект
Двумерная игра с видом сбоку на Godot 4.7. Референс — Fallout Shelter: разрез убежища,
комнаты в сетке, жители. Игра полностью 2D. Новые игровые сцены — на Node2D/Control;
3D-узлам в проекте места нет. Рендерер при этом Forward+ — так решил владелец проекта,
не переключайте его без явной просьбы.
Работа через MCP, а не через файлы
В проекте включён плагин addons/godot_ai, редактор Godot обычно запущен и подключён.
Сцены и ресурсы правьте инструментами MCP (scene_manage, ui_manage, node_create,
theme_manage, resource_manage), а не редактированием .tscn руками — иначе открытый
редактор перезапишет изменения при сохранении.
Скрипты — через script_create / script_patch: они возвращают диагностику парсера сразу.
Проверять результат — project_run + editor_screenshot(source="game"). Ввод в запущенную
игру шлётся через game_manage(op="input_key"); input_action не двигает фокус Control-ов,
для навигации по UI нужны настоящие события клавиш.
Известные особенности
node_set_propertyне принимает целочисленный0— используйтеbatch_executeс командойset_property.- Правка скрипта, который зарегистрирован автозагрузкой, даёт в ответе
GDScript reload failed with error code 43. Это неудачная горячая перезагрузка в редакторе, а не ошибка разбора: проверяйтеlogs_read(source="editor"), игра при этом запускается чисто. project_manage(op="settings_set")отказывается менятьapplication/run/main_scene.- Большие
ui_manage(op="build_layout")иногда не доезжают целиком — стройте дерево несколькими вызовами поменьше. - Запуск из редактора отдаёт встроенное окно:
Embedded window can't be resizedв логе — норма.
Конвенции кода
- Отступы — табы (см.
.editorconfig), статическая типизация везде, где возможно. - Комментарии и весь UI-текст — по-русски; имена узлов, файлов, методов, сигналов — по-английски.
- Комментарий объясняет почему, а не пересказывает код.
- Приватные члены — с подчёркиванием:
_card,_focus_first_button(). - Документирующие комментарии
##— на классах, сигналах и неочевидных методах.
Архитектура UI
- Единая тема
themes/main_menu.theme.tresна корне каждого экрана. Цвета в узлах не хардкодим. - Экран = сцена со своим скриптом, наружу торчат
open()/close()и сигналclosed. - Оверлеи центрируются
CenterContainer, а не пресетомcenter: пресет ставит левый верхний угол карточки в центр экрана. Та же ловушка уbottom_wide— он прижимает к нижнему краю верх панели, и она уезжает за экран; для нижней панели используйтеMarginContainerво весь экран +HBoxContainerсsize_flags_vertical = SIZE_SHRINK_ENDу содержимого. - Контейнеры не раскладывают скрытые узлы — после
visible = trueнуженawait get_tree().process_frame, прежде чем читатьsize(например, дляpivot_offset). - Наведение мышью делается фокусом (
mouse_entered→grab_focus), чтобы у мыши и клавиатуры была одна подсветка.
Игровой слой
Файлы в scripts/game/ разделены по принципу «данные и правила отдельно, отрисовка отдельно».
Модель и правила — без узлов и без _draw():
vault_grid.gd— сетка и комнаты. Сюда идут типы комнат, размещение, ресурсы. Вертикальное смещение бункера под землю — константаDEPTH, её учитывают иcell_rect(), иcell_at(), поэтому картинка и попадание мышью не разъезжаются.game_clock.gd— игровые дата и время, скорость, своя пауза. Такт наружу — сигналminute_passed; темп задаётсяMINUTES_PER_SECOND.weather_system.gd— тип погоды, матрица переходов и плавная смена параметров. Такт берёт у часов, поэтому стоит вместе с паузой. Форма осадков решается температурой, а не месяцем: у нуля дождь становится снегом.climate.gd— сезонная норма температуры и ветра (средняя полоса Европы). Перечисление погоды сюда не тянем — вышла бы циклическая ссылка.sky_cycle.gd— палитра неба по часу суток, положение солнца и луны. Чистые вычисления.
Отрисовка — только _draw(), читают модель и ничего не решают:
sky_view.gd— градиент неба, звёзды, светила.surface_view.gd— поверхность: горы, холмы, деревья, облака, осадки.vault_view.gd— разрез: порода, корпус, шахта, комнаты, будка входа.
vault_scene.gd связывает всё это с камерой и HUD и держит контракт сохранения.
Новый тип комнаты добавляется одной записью в VaultGrid.KINDS: кнопка в панели строительства
и подсчёт ресурсов подхватятся сами. Новый тип погоды — записью в WeatherSystem.KINDS
плюс строкой в TRANSITIONS.
Текстуры фона
Ловушка с плиткой. draw_texture_rect(tex, rect, tile = true) повторяет
текстуру в её натуральную величину и по обеим осям. Если растянуть
прямоугольник ради масштаба, текстура просто уложится в него несколько раз, и
на стыке копий пойдёт горизонтальная полоса через весь экран. Плюс при
texture_repeat = ENABLED фильтрация на краю подмешивает противоположный край
текстуры — ещё одна тонкая линия.
Поэтому полосы пейзажа кладутся плитками вручную. При этом важен ещё один
момент: крайние столбцы пикселей у этих текстур полупрозрачные от сглаживания,
и встык они складываются дважды, давая вертикальную линию. Отсюда
EDGE_INSET — плитки рисуются через draw_texture_rect_region() с отступом
в пиксель с каждой стороны, плюс пиксель нахлёста. Повтор на узле выключен.
background/ — набор Kenney Background Elements. Два стиля, и они не взаимозаменяемы:
PNG/Flat/* — бледные, почти белые силуэты, их место на дальних планах, где цвет всё равно
задаётся тонировкой; PNG/* — цветные спрайты для ближнего плана. Перепутать легко: деревья
из Flat на переднем плане выглядят выцветшими.
Консоль разработчика
Открывается тильдой или F1 в любой сцене, работает и на паузе. Сама об игре
ничего не знает: команды регистрируют сцены через DevConsole.register(...),
и они умирают вместе со сценой — консоль выбрасывает команду, когда её
Callable протухает.
Команды игровой сцены живут в vault_scene.gd, в разделе «Команды консоли»:
stats, time, date, speed, weather, temp, wind, build, wipe.
У погоды есть латинские ключи (snow, rain, …) — набирать русские названия
с латинской раскладки посреди отладки неудобно.
Файловый мост — как выполнять команды из MCP
Набрать команду через game_manage(op="input_key") нельзя: внедрённые события
доносят до LineEdit код клавиши, но не символ. Enter при этом проходит — то
есть дело не в фокусе. Поэтому в консоли есть мост через файлы в user://
(только OS.is_debug_build(), в релиз не попадает):
DIR="$APPDATA/Godot/app_userdata/GWorld"
printf 'weather storm\ntemp -3\nstats\n' > "$DIR/console_in.txt"
cat "$DIR/console_log.txt"
Консоль опрашивает console_in.txt четыре раза в секунду, выполняет строки и
удаляет файл. Весь вывод дублируется в console_log.txt, который
перезаписывается на каждый запуск игры. Работает и с закрытой консолью, и на
паузе.
Автозагрузки
GameSettings (GameSettingsService), SaveManager (SaveManagerService)
и DevConsole (сцена scenes/ui/dev_console.tscn).
У обеих есть class_name: константы, перечисления и типы берём через него, экземпляр —
через get_node("/root/..."). Имя автозагрузки редактор не резолвит без перезапуска проекта.
Контракт сохранения игровой сцены описан в README.md.
Проверка
Изменения в UI считаются сделанными только после запуска и скриншота. Логи смотреть в обоих
источниках: logs_read(source="game") и logs_read(source="editor") — ошибки загрузки скриптов
попадают только во второй.