16 KiB
ТЕХНИЧЕСКОЕ ЗАДАНИЕ ДЛЯ АРТЕМА: EBLAN ENGINE 3D & EBLANSTUDIO
Назначение документа: Детальный план разработки следующих ключевых подсистем игрового 2.5D движка на ассемблере (EBLAN ENGINE 3D) и сопутствующего C++ визуального редактора (EblanStudio).
Целевой стек: x86 MASM32 (движок), C++17 + Dear ImGui + DirectX 11 (редактор).
📋 Оглавление
- Задача 1: Устранение Fish-Eye эффекта и фикс рендера
- Задача 2: Полноценный импорт ассетов в EblanStudio
- Задача 3: Многоуровневые локации и стриминг подуровней
- Задача 4: Система кат-сцен и скриптовых последовательностей
- Чек-лист готовности и порядок реализации
Задача 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)
Что нужно сделать:
- Таблица предвычисленных смещений лучей (
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;
- В
- Использование в цикле лучей (
include/engine_raycaster.inc):- Заменить линейный расчет угла на быстрый табличный:
mov eax, screenX mov edx, [tableRayAngleOffset + eax*4] add edx, playerAngle and edx, ANGLE_MASK mov rayAngle, edx
- Заменить линейный расчет угла на быстрый табличный:
- Коррекция перпендикулярного расстояния:
- Перпендикулярная дистанция:
\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/)
- Библиотека декодирования картинок:
- Подключить
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
- Подключить
- Диалоговые окна выбора файлов Win32:
- Использовать стандартный
GetOpenFileNameAдля выбора PNG/WAV файлов.
- Использовать стандартный
- Конвертация и упаковка текстур (Wall Textures & Sprites):
- Входное изображение ресемплится/обрезается до стандарта движка: 128x128 пикселей.
- Цветовые каналы конвертируются из RGBA в BGRA (
00RRGGBBh), где для прозрачных спрайтов альфа = 0 заменяется на ключевой цвет мадженты0x00FF00FF(COLOR_CHROMA_KEY). - Автоматическая перезапись и сохранение в
assets/textures/texturepack.bin(заголовокEBTX+ 16 слотов по 65536 байт).
- Интерфейс в 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. Логика динамической подгрузки и выгрузки в Ассемблере
- Процедура перехода
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): Плавное осветление экрана.
- Шаг 1 (Fade Out): Плавное затемнение буфера экрана
- Память:
- Оперативная память остается постоянной (~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)
- Флаг состояния
isCutsceneActive:DWORD:- Когда
isCutsceneActive == 1, вPollInputигнорируются клавишиWASDи стрелочки.
- Когда
- Интерполятор камеры
UpdateCutscene:- Вызывается в
UpdatePlayer. - Если задано
targetCamX, плавно сдвигаетplayerXк цели:; Движение playerX -> targetCamX mov eax, targetCamX sub eax, playerX ; нормализация шага и добавление скорости - Аналогично для угла поворота
playerAngleпо кратчайшей дуге окружности 512.
- Вызывается в
- Отрисовка 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. |
Файл подготовлен и структурирован для передачи в работу Артему.