# ТЕХНИЧЕСКОЕ ЗАДАНИЕ ДЛЯ АРТЕМА: EBLAN ENGINE 3D & EBLANSTUDIO > **Назначение документа**: Детальный план разработки следующих ключевых подсистем игрового 2.5D движка на ассемблере (**EBLAN ENGINE 3D**) и сопутствующего C++ визуального редактора (**EblanStudio**). > **Целевой стек**: x86 MASM32 (движок), C++17 + Dear ImGui + DirectX 11 (редактор). --- ## Проверка реализации этапов 1–3 — 21.10.2026 - Этап 1: тангенциальные углы лучей, единственная косинусная коррекция, DDA с 8 дробными битами до проекции. В регрессионной сцене прямая стена имеет одинаковую глубину во всех 320 столбцах. - Этап 2: PNG/JPG/BMP, BGRA и хромакей, безопасное создание и обновление пакета, стандартные слоты при первом импорте, WAV и манифесты. Портал не использует слот фонаря. - Этап 3: EBLN v3 с чтением v1/v2, полная проверка перед заменой комнаты, обработка ошибочного перехода, спавны, затемнение и защита от повторного срабатывания при появлении в портале. F5 работает с временной копией карт и ресурсов. - Регрессионные проверки: `python tools/test_regressions.py`. Ручная проверка интерфейса и длительный игровой прогон остаются отдельной проверкой качества. - Этапы 4 и 5 реализованы следующим изменением, см. статус ниже. ## Реализация этапов 4–5 — 22.10.2026 - Этап 4: бинарный трек EBLN v4, чистый ASM, линейные пролёты по точкам, повороты по кратчайшей дуге, паузы, русские субтитры CP1251, letterbox 2.35:1, WAV, затемнения и переходы. Автозапуск при входе, C для повтора и Tab для пропуска; управление игроком блокируется на время сцены. Старые карты v1/v2/v3 читаются. - Этап 5: F6 открывает таймлайн с дорожками, перетаскиванием ключей, списком команд, инспектором, установкой точек на карте, автозапуском, Undo/Redo и сохранением. Предпросмотр и перемотка показывают сцену глазами камеры во встроенном игровом виде, а на карте — траекторию. F5 запускает полную игру и связанные комнаты с проверкой WAV. - Пример `examples/cutscene_demo/run_demo.bat` показывает пролёт, текст, звук и переход во вторую комнату; основные карты проекта он не меняет. - Регрессионный набор расширен проверками формата v4, проигрывателей ASM/C++, оверлея, переходов, редактора и покадрового совпадения состояния. - Уточнения исходного ТЗ: сериализуемый `dataPtr` заменён на `dataId`, строки хранятся в отдельном пуле; движение задаётся длительностью. Использованы линейные waypoints, камера во время пролёта не сталкивается со стенами. WAV воспроизводится одним каналом PlaySoundA без внешних библиотек. ## Завершение редактора и исправление F5 — 22.10.2026 - Встроен игровой вид, который получает кадры от настоящего ASM-рендерера. Доступны режимы «Рядом», «Игровой вид», «Карта», свободный осмотр и камера таймлайна. Изменения стен, цветов, спрайтов и текстур видны без сохранения. - Исправлена блокировка F5/F6 при захвате клавиатуры интерфейсом ImGui. Ошибки запуска показываются отдельным диалогом. Начальная карта открывается относительно расположения редактора, независимо от рабочей папки. - Автопроверки проверяют горячие клавиши с фокусом в интерфейсе, реальную передачу кадров из x86 ASM в x64 редактор и DX11, обновление геометрии, перемотку кат-сцены, точное совпадение пикселей с отдельным движком и освобождение процесса рендерера. - Все пять этапов реализованы. Предпросмотр редактора останавливается на команде перехода между комнатами; полный сценарий переходов и игровой ввод доступны через F5. Это ограничение предпросмотра, а не отсутствие переходов в движке. - Итоговая проверка: 94/94 сценария редактора и 107/107 сценариев ASM, совпадение всех 320 углов лучей, покадровое совпадение проигрывателей и точное совпадение пикселей встроенного вида с движком. Обе программы пересобраны. Исходные ресурсы проекта при проверках остались без изменений. Ручное управление окнами было остановлено пользователем через Esc; полный визуальный прогон интерфейса не завершён. Разделы ниже сохраняют исходное ТЗ и описание прежних проблем как историю требований. Актуальные формат и поведение описаны в `AGENTS.md` и `docs/ENGINE_ARCHITECTURE.md`. --- ## 📋 Оглавление 1. [Задача 1: Устранение Fish-Eye эффекта и фикс рендера](#задача-1-устранение-fish-eye-эффекта-и-фикс-рендера) 2. [Задача 2: Полноценный импорт ассетов в EblanStudio](#задача-2-полноценный-импорт-ассетов-в-eblanstudio) 3. [Задача 3: Многоуровневые локации и стриминг подуровней](#задача-3-многоуровневые-локации-и-стриминг-подуровней) 4. [Задача 4: Система кат-сцен и скриптовых последовательностей](#задача-4-система-кат-сцен-и-скриптовых-последовательностей) 5. [Чек-лист готовности и порядок реализации](#чек-лист-готовности-и-порядок-реализации) --- ## Задача 1: Устранение Fish-Eye эффекта и фикс рендера [ВЫПОЛНЕНО / DONE] ### 1.1. Суть проблемы в текущем коде В файле [`include/engine_raycaster.inc`](include/engine_raycaster.inc) лучи выпускаются с равномерным шагом угла: ```masm ; Линейное распределение углов по ширине экрана: rayAngle = playerAngle - FOV_HALF + (screenX * FOV_ANGLES / SCREEN_W) ``` - **Геометрическое искажение**: когда экран плоский, равные интервалы пикселей по оси $X$ соответствуют разным угловым приращениям ($\tan \theta \ne \theta$). Лучи, выпущенные с постоянным шагом угла, проецируются на изогнутую цилиндрическую поверхность. - **Артефакт "рыбьего глаза"**: стены по краям экрана кажутся вздутыми, а при повороте камеры прямые стены начинают "плавать" и изгибаться волной. - **Ошибки DDA**: в секции `apply_fisheye_fix` расстояние повторно умножается на косинус, что при некоторых углах дает артефакт двойного искажения. ### 1.2. Решение: Тангенциальная коррекция углов лучей Сохраняя радиальную архитектуру лучей (512 дискретных углов), пересчитать угол каждого столбца $x \in [0..319]$ через тангенс: $$\theta_x = \text{playerAngle} + \arctan\left(\left(\frac{2x}{\text{SCREEN\_W}} - 1\right) \cdot \tan\left(\frac{\text{FOV}}{2}\right)\right)$$ #### Что нужно сделать: 1. **Таблица предвычисленных смещений лучей (`tableRayAngleOffset`)**: - В [`include/engine_math.inc`](include/engine_math.inc) при вызове `InitMathTables` сгенерировать через FPU таблицу из 320 значений `DWORD`. - Для каждого столбца $x$: ```c float cameraX = (2.0f * x / 320.0f) - 1.0f; float radOffset = atanf(cameraX * tanf(FOV_RAD / 2.0f)); int angleOffset = (int)roundf(radOffset * 512.0f / (2.0f * M_PI)); tableRayAngleOffset[x] = angleOffset; ``` 2. **Использование в цикле лучей (`include/engine_raycaster.inc`)**: - Заменить линейный расчет угла на быстрый табличный: ```masm mov eax, screenX mov edx, [tableRayAngleOffset + eax*4] add edx, playerAngle and edx, ANGLE_MASK mov rayAngle, edx ``` 3. **Коррекция перпендикулярного расстояния**: - Перпендикулярная дистанция: $$\text{perpWallDist} = \text{rawDist} \cdot \cos(\text{rayAngle} - \text{playerAngle})$$ - Убедиться, что `sideDistX` и `deltaDistX` не накапливают масштабирующий множитель 64 дважды при вычислении текстурных координат (`texX`). --- ## Задача 2: Полноценный импорт ассетов в EblanStudio [ВЫПОЛНЕНО / DONE] ### 2.1. Требования У Артема и дизайнеров уровней должна быть возможность в пару кликов мыши в редакторе загружать свои изображения (PNG) и звуки (WAV) без ручного запуска сторонних питоновских скриптов. ### 2.2. Архитектура Asset Manager в C++ (`editor/`) 1. **Библиотека декодирования картинок**: - Подключить `stb_image.h` (однофайловая C++ библиотека без внешних зависимостей) в папку `editor/src/stb_image.h`. - Загрузка любого PNG, JPG, BMP в 32-битный массив пикселей: ```cpp int w, h, channels; unsigned char* data = stbi_load(filename, &w, &h, &channels, 4); // RGBA32 ``` 2. **Диалоговые окна выбора файлов Win32**: - Использовать стандартный `GetOpenFileNameA` для выбора PNG/WAV файлов. 3. **Конвертация и упаковка текстур (Wall Textures & Sprites)**: - Входное изображение ресемплится/обрезается до стандарта движка: **128x128 пикселей**. - Цветовые каналы конвертируются из RGBA в BGRA (`00RRGGBBh`), где для прозрачных спрайтов альфа = 0 заменяется на ключевой цвет мадженты `0x00FF00FF` (`COLOR_CHROMA_KEY`). - Автоматическая перезапись и сохранение в `assets/textures/texturepack.bin` (заголовок `EBTX` + 16 слотов по 65536 байт). 4. **Интерфейс в EblanStudio**: - Вкладка меню: `Ассеты -> Импорт текстуры стены...` (выбор слота 1..15). - Вкладка меню: `Ассеты -> Импорт спрайта...` (добавление в список доступных спрайтов). - Вкладка меню: `Ассеты -> Импорт аудио (WAV)...` (копирование звука в `assets/sounds/` с автоматической генерацией алиаса MCI). - Превью текстуры прямо в палитре тайлов в левой панели редактора. --- --- ## Задача 3: Многоуровневые локации и стриминг подуровней [ВЫПОЛНЕНО / DONE] ### 3.1. Секторно-комнатная модель уровней Вместо одной изолированной сетки $32 \times 32$ мир организуется как **граф связанных секторов (комнат)**. - Каждый сектор — это отдельный бинарный файл подуровня (например, `map.dat`, `sublevel_02.dat`) со своей сеткой $32 \times 32$, цветами пола/потолка и локальными спрайтами. - Сектора соединяются **Порталами / Дверями перехода (Sector Portals)**. ### 3.2. Структура данных перехода (Portal Transition) В заголовок и сущности карты добавлен тип `CELL_PORTAL = 10` и структура: ```cpp // Структура перехода между подуровнями (42 байта packed) struct LevelTransition { uint8_t triggerCellX; // Координата X двери/триггера в текущем секторе uint8_t triggerCellY; // Координата Y двери/триггера в текущем секторе char targetMap[32]; // Имя целевого файла: "sublevel_02.dat" uint16_t targetSpawnX; // Точка появления в новом секторе (X) uint16_t targetSpawnY; // Точка появления в новом секторе (Y) uint16_t targetAngle; // Направление взгляда после перехода uint8_t fadeEffect; // 0 = мгновенно, 1 = затемнение экрана (Fade to Black) uint8_t reserved; // Выравнивание }; ``` ### 3.3. Логика динамической подгрузки и выгрузки в Ассемблере В [`include/engine_map.inc`](include/engine_map.inc): 1. **Процедура перехода `SwitchSublevel(szNextMap, spawnX, spawnY, spawnAngle, fadeEffect)`**: - **Шаг 1 (Fade Out)**: Плавное затемнение буфера экрана `screenBuffer` за 16 кадров с помощью целочисленного масштабирования цветовых каналов RGB. - **Шаг 2 (Unload)**: Очистка текущих спрайтов (`mov spriteCount, 0`), сброс флагов и переходных триггеров. - **Шаг 3 (Load)**: Чтение нового файла карты через `LoadSublevelBinary` в `gameMap`, обновление цветов неба/пола (`mapCeilingColor`, `mapFloorColor`), загрузка массива спрайтов нового сектора. - **Шаг 4 (Spawn)**: Установка новых координат игрока `playerX = spawnX * 64 + 32`, `playerY = spawnY * 64 + 32`, `playerAngle = spawnAngle`. - **Шаг 5 (Fade In)**: Плавное 16-кадровое осветление экрана. 2. **Память и производительность**: - Постоянный объем оперативной памяти (~1-2 МБ), буферы очищаются и заполняются новым сектором на лету, позволяя создавать бесконечные цепочки и комплексы комнат. - Во время обычного геймплея оверхед пост-процесса затемнения равен нулю (`cmp fadeState, 0; je fade_done`). 3. **Интеграция в EblanStudio**: - Добавлен тайл `10. Портал перехода` с неоново-фиолетовой палитрой и меткой портала на 2D-сетке. - Инспектор свойств перехода: выбор файла целевого подуровня (`.dat`), кнопка обзора файловой системы, слайдеры целевого спавна и угла, переключатель Fade to Black, быстрое удаление и кнопка мгновенного перехода к подуровню в редакторе. - Поддержка сериализации и десериализации переходов в бинарный формат с сохранением 100% обратной совместимости. --- ## Задача 4: Система кат-сцен и скриптовых последовательностей [ВЫПОЛНЕНО / DONE] ### 4.1. Архитектура: Бинарный трек событий (Keyframe Track) Кат-сцена представляет собой упорядоченный во времени список событий (Action Track). Во время кат-сцены управление игрока блокируется, а виртуальный режиссер интерполирует камеру, спавнит звуки и выводит диалоговые субтитры. ### 4.2. Формат команд кат-сцены Каждая команда — это фиксированная 16-байтовая структура: ```cpp enum CutsceneOpCode : uint8_t { OP_END = 0, // Конец кат-сцены, возврат управления OP_WAIT, // Ждать N кадров/тиков OP_MOVE_CAM_TO, // Плавное перемещение камеры в (X, Y) со скоростью V OP_PAN_CAM_TO, // Плавный поворот камеры на угол Angle OP_SHOW_SUBTITLE, // Вывести текст субтитра на экране (ID строки) OP_HIDE_SUBTITLE, // Скрыть субтитры OP_PLAY_SOUND, // Проиграть звук (WAV алиас) OP_FADE_SCREEN, // Затемнить / осветлить экран OP_CHANGE_SUBLEVEL // Инициировать переход на другой подуровень }; struct CutsceneEvent { uint16_t tickTimestamp; // Кадр запуска от старта сцены uint8_t opCode; // Тип команды (CutsceneOpCode) uint8_t param8; // Доп. байт параметров (напр. флаг fade) int32_t paramA; // Целевой X / угол / длительность int32_t paramB; // Целевой Y / скорость int32_t dataPtr; // Указатель на текст / имя звука }; ``` ### 4.3. Реализация в движке (MASM32) 1. **Флаг состояния `isCutsceneActive:DWORD`**: - Когда `isCutsceneActive == 1`, в `PollInput` игнорируются клавиши `WASD` и стрелочки. 2. **Интерполятор камеры `UpdateCutscene`**: - Вызывается в `UpdatePlayer`. - Если задано `targetCamX`, плавно сдвигает `playerX` к цели: ```masm ; Движение playerX -> targetCamX mov eax, targetCamX sub eax, playerX ; нормализация шага и добавление скорости ``` - Аналогично для угла поворота `playerAngle` по кратчайшей дуге окружности 512. 3. **Отрисовка 2D-субтитров**: - В [`include/engine_render2d.inc`](include/engine_render2d.inc) использовать существующий `DrawString8x8` для вывода полупрозрачной плашки внизу экрана (Letterbox 2.35:1) и текста диалогов с центрированием. ### 4.4. Инструментарий в EblanStudio - Добавить в редактор панель **"Cutscene Timeline"**: - Горизонтальная шкала времени (в секундах / кадрах). - Кнопки добавления ключевых точек: `[+ Камера]`, `[+ Текст]`, `[+ Звук]`, `[+ Переход]`. - Возможность кликом по карте расставить контрольные точки пролета камеры (Spline/Waypoints). - Кнопка предпросмотра пролета прямо в окне редактора. --- ## Чек-лист готовности и порядок реализации | Этап | Задача | Затронутые файлы | Ожидаемый результат | |---|---|---|---| | **Этап 1 — готово** | Исправление Fish-Eye и угла лучей | `include/engine_math.inc`, `include/engine_raycaster.inc` | Стены остаются ровными по краям экрана, нет искажения при вращении камеры. | | **Этап 2 — готово** | Импорт текстур и спрайтов в редакторе | `editor/src/stb_image.h`, `editor/src/editor_app.cpp` | Кнопка импорта в GUI загружает PNG и обновляет `assets/textures/texturepack.bin`. | | **Этап 3 — готово** | Система связанных секторов (комнат) | `editor/src/level_format.h`, `include/engine_map.inc` | Переход между комнатами по триггеру с выгрузкой старой карты и загрузкой новой. | | **Этап 4 — готово** | Движок кат-сцен и диалогов | `include/engine_cutscene.inc`, `include/engine_data.inc`, `main.asm` | Пролёт камеры по ключевым точкам, субтитры, звук, затемнения, переходы, блокировка инпута. | | **Этап 5 — готово** | Визуальный таймлайн в EblanStudio | `editor/src/editor_cutscene.inl`, `editor/src/editor_viewport.inl`, `editor/src/cutscene_preview.h` | Редактирование траектории и сценария, предпросмотр игрового вида внутри редактора, Undo/Redo, F5 для полной игры. | --- > *Файл подготовлен и структурирован для передачи в работу Артему.*