EBLAN_ENGINE/TODO_ARTEM.md

16 KiB
Raw Blame History

ТЕХНИЧЕСКОЕ ЗАДАНИЕ ДЛЯ АРТЕМА: EBLAN ENGINE 3D & EBLANSTUDIO

Назначение документа: Детальный план разработки следующих ключевых подсистем игрового 2.5D движка на ассемблере (EBLAN ENGINE 3D) и сопутствующего C++ визуального редактора (EblanStudio).
Целевой стек: x86 MASM32 (движок), C++17 + Dear ImGui + DirectX 11 (редактор).


📋 Оглавление

  1. Задача 1: Устранение Fish-Eye эффекта и фикс рендера
  2. Задача 2: Полноценный импорт ассетов в EblanStudio
  3. Задача 3: Многоуровневые локации и стриминг подуровней
  4. Задача 4: Система кат-сцен и скриптовых последовательностей
  5. Чек-лист готовности и порядок реализации

Задача 1: Устранение Fish-Eye эффекта и фикс рендера

1.1. Суть проблемы в текущем коде

В файле include/engine_raycaster.inc лучи выпускаются с равномерным шагом угла:

; Линейное распределение углов по ширине экрана:
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 при вызове InitMathTables сгенерировать через FPU таблицу из 320 значений DWORD.
    • Для каждого столбца x:
      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):
    • Заменить линейный расчет угла на быстрый табличный:
      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

2.1. Требования

У Артема и дизайнеров уровней должна быть возможность в пару кликов мыши в редакторе загружать свои изображения (PNG) и звуки (WAV) без ручного запуска сторонних питоновских скриптов.

2.2. Архитектура Asset Manager в C++ (editor/)

  1. Библиотека декодирования картинок:
    • Подключить stb_image.h (однофайловая C++ библиотека без внешних зависимостей) в папку editor/src/stb_image.h.
    • Загрузка любого PNG, JPG, BMP в 32-битный массив пикселей:
      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: Многоуровневые локации и стриминг подуровней

3.1. Секторно-комнатная модель уровней

Вместо одной изолированной сетки 32 \times 32 мир организуется как граф связанных секторов (комнат).

  • Каждый сектор — это отдельный бинарный файл подуровня (например, sublevel_01.dat, sublevel_02.dat) со своей сеткой 32 \times 32, цветами пола/потолка и локальными спрайтами.
  • Сектора соединяются Порталами / Дверями перехода (Sector Portals).

3.2. Структура данных перехода (Portal Transition)

В заголовок или массив сущностей карты добавляется тип CellType::CELL_PORTAL:

// Структура перехода между подуровнями
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)
};

3.3. Логика динамической подгрузки и выгрузки в Ассемблере

В include/engine_map.inc:

  1. Процедура перехода SwitchSublevel(szNextMap, spawnX, spawnY, spawnAngle):
    • Шаг 1 (Fade Out): Плавное затемнение буфера экрана screenBuffer за 10-15 кадров (уменьшение яркости RGB).
    • Шаг 2 (Unload): Очистка текущих спрайтов (mov spriteCount, 0), сброс флагов дверей.
    • Шаг 3 (Load): Чтение нового файла карты в gameMap, обновление цветов неба/пола (mapCeilingColor, mapFloorColor), загрузка массива спрайтов нового сектора.
    • Шаг 4 (Spawn): Установка новых координат игрока playerX = spawnX * 64 + 32, playerY = spawnY * 64 + 32, playerAngle = spawnAngle.
    • Шаг 5 (Fade In): Плавное осветление экрана.
  2. Память:
    • Оперативная память остается постоянной (~1-2 МБ), старые комнаты полностью освобождают буферы, что позволяет создавать гигантские замки и комплексы из сотен связанных комнат.

Задача 4: Система кат-сцен и скриптовых последовательностей

4.1. Архитектура: Бинарный трек событий (Keyframe Track)

Кат-сцена представляет собой упорядоченный во времени список событий (Action Track). Во время кат-сцены управление игрока блокируется, а виртуальный режиссер интерполирует камеру, спавнит звуки и выводит диалоговые субтитры.

4.2. Формат команд кат-сцены

Каждая команда — это фиксированная 16-байтовая структура:

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 к цели:
      ; Движение playerX -> targetCamX
      mov eax, targetCamX
      sub eax, playerX
      ; нормализация шага и добавление скорости
      
    • Аналогично для угла поворота playerAngle по кратчайшей дуге окружности 512.
  3. Отрисовка 2D-субтитров:
    • В 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_data.inc, include/engine_render2d.inc, main.asm Пролет камеры по ключевым точкам, показ диалоговых субтитров, блокировка инпута.
Этап 5 Визуальный таймлайн в EblanStudio editor/src/editor_app.cpp, editor/src/editor_app.h Визуальное редактирование траектории камеры и сценария кат-сцены в UI.

Файл подготовлен и структурирован для передачи в работу Артему.