189 lines
16 KiB
Markdown
189 lines
16 KiB
Markdown
# ТЕХНИЧЕСКОЕ ЗАДАНИЕ ДЛЯ АРТЕМА: EBLAN ENGINE 3D & EBLANSTUDIO
|
||
|
||
> **Назначение документа**: Детальный план разработки следующих ключевых подсистем игрового 2.5D движка на ассемблере (**EBLAN ENGINE 3D**) и сопутствующего C++ визуального редактора (**EblanStudio**).
|
||
> **Целевой стек**: x86 MASM32 (движок), C++17 + Dear ImGui + DirectX 11 (редактор).
|
||
|
||
---
|
||
|
||
## 📋 Оглавление
|
||
1. [Задача 1: Устранение Fish-Eye эффекта и фикс рендера](#задача-1-устранение-fish-eye-эффекта-и-фикс-рендера)
|
||
2. [Задача 2: Полноценный импорт ассетов в EblanStudio](#задача-2-полноценный-импорт-ассетов-в-eblanstudio)
|
||
3. [Задача 3: Многоуровневые локации и стриминг подуровней](#задача-3-многоуровневые-локации-и-стриминг-подуровней)
|
||
4. [Задача 4: Система кат-сцен и скриптовых последовательностей](#задача-4-система-кат-сцен-и-скриптовых-последовательностей)
|
||
5. [Чек-лист готовности и порядок реализации](#чек-лист-готовности-и-порядок-реализации)
|
||
|
||
---
|
||
|
||
## Задача 1: Устранение Fish-Eye эффекта и фикс рендера
|
||
|
||
### 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
|
||
|
||
### 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: Многоуровневые локации и стриминг подуровней
|
||
|
||
### 3.1. Секторно-комнатная модель уровней
|
||
Вместо одной изолированной сетки $32 \times 32$ мир организуется как **граф связанных секторов (комнат)**.
|
||
- Каждый сектор — это отдельный бинарный файл подуровня (например, `sublevel_01.dat`, `sublevel_02.dat`) со своей сеткой $32 \times 32$, цветами пола/потолка и локальными спрайтами.
|
||
- Сектора соединяются **Порталами / Дверями перехода (Sector Portals)**.
|
||
|
||
### 3.2. Структура данных перехода (Portal Transition)
|
||
В заголовок или массив сущностей карты добавляется тип `CellType::CELL_PORTAL`:
|
||
```cpp
|
||
// Структура перехода между подуровнями
|
||
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`](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-байтовая структура:
|
||
```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_data.inc`, `include/engine_render2d.inc`, `main.asm` | Пролет камеры по ключевым точкам, показ диалоговых субтитров, блокировка инпута. |
|
||
| **Этап 5** | Визуальный таймлайн в EblanStudio | `editor/src/editor_app.cpp`, `editor/src/editor_app.h` | Визуальное редактирование траектории камеры и сценария кат-сцены в UI. |
|
||
|
||
---
|
||
|
||
> *Файл подготовлен и структурирован для передачи в работу Артему.*
|