Товарищ МАСК - Константин Владимирович Ежов
// Перевод экрана в псевдо-3D координаты
int mapX = x 3;
int mapY = y 3;
int tileType = map[mapY][mapX];
// Проверка для текстурирования
if (tileType == WALL) {
DrawWall(x, y);
} else {
DrawFloor(x, y);
}
}
}
}
```
Суть этого подхода заключалась в том, чтобы проецировать 2D-карту на экран, таким образом создавая иллюзию 3D-пространства. Для эмуляции высоты использовались заранее подготовленные тайлы – кирпичики, полы, стены – которые накладывались друг на друга, как слои в фотографии. Эффект получался не совсем реальным, но в условиях отсутствия 3D-ускорителя он был весьма неплох. Тем боле пока потребители даже такого не вкусили толком. Ух они у меня устроят мочилово!
Так и продолжал возиться с рендерингом – в каждом шаге приходилось тщательно учитывать ограничения как потенциальных возможностей машин, на которых будут игру запускать, так и того, что мог мой ноут. "Так, без прямой поддержки 3D могу рассчитывать только на простые матричные преобразования и рисовать на основе значений тайлов. Ну, а свет? Ладно, с фальшивым светом подумаю позже...". На этом открыл блокнот, где предварительно набросал идеи для освещения:
1. **Имитировать световые зоны** – в зависимости от расстояния до источника света текстуры становились бы темнее или светлее.
2. **Простая градация** – чем дальше от игрока, тем темнее зона, создавая эффект глубины.
Ещё пара строк кода, и на экране в отдельном окне возникла сцена с характерным серым полом и тёмными стенами. Ощущение присутствия было сырым, но для первого эксперимента неплохо. Да что там, супер, сказал бы даже. Я молодец! Если себя сам не похвалишь, кто это ещё сделает, тем более никто и не знает, какую революцию тут замутил.
```c
void DrawWall(int x, int y) {
// Закрашиваем стену с оттенком серого
int shade = 255 - (distanceToPlayer(x, y) * 10);
shade = max(0, min(shade, 255)); // Ограничиваем значения
SetPixelColor(x, y, RGB(shade, shade, shade));
}
```
"Отлично, - пришла мысль, — это первый шаг. Простейшие стены готовы".
После чего сделал несколько быстрых глотков чая, поглядывая на экран. Несмотря на довольно простую задачу – отрисовку стен и пола, – процесс оказался гораздо более сложным, чем думал внвчале. Даже каждая мелочь, вроде изменения оттенков или расчета расстояний, съедала память и ресурсы процессора. Гадство…. Прибил бы этого Билла Гейтса с его шестнадцатью килобайтами оперативы, которой хватит на всё! Но одна идея крутится в голове, давая новый импульс: вместо полной симуляции каждого шага, можно ведь бы использовать метод **Raycasting**, в прямом переводе звучащий довольно бессмысленно, – метод, которым создавались игры вроде *Wolfenstein 3D* в ранние девяностые годы. Он не требовал 3D-ускорения, но позволял строить иллюзию трехмерного мира на основе двумерной карты.
Правда появился он куда как ранее, ещё в восьмидесятые, но это сейчас не важно, поскольку до нынешнего времени использовать его в практических приложения возможным не было, поскольку банально не хватало мощности компьютеров. Если сделаю игру и выпущу на рынок раньше, то будут упоминать меня, а не Кармака. Да и игра в которой его применили первой станет *DOOM*. В общем, тут можно сказать саму историю переписываю! А потому, сконцентрировавшись, начал накидывать основные принципы метода:
1. **Лучевая трассировка** - при каждом обновлении экрана будет высылаться множество «лучей», начиная с точки игрока и направленных в разные стороны. Эти лучи «натыкались» на стены, вычисляя расстояние до каждой из них.
2. **Ширина и высота стен** зависели от расстояния до них, создавая эффект перспективы.
3. **Свет и тени** – расстояние к стенам также влияло бы на их яркость, что добавляло бы глубину сцене.
Набросав общий план, открыл новый файл и начал писать код для этого подхода:
```c
void RenderRaycasting() {
for (int x = 0; x screenWidth; x++) {
// Рассчитываем угол луча от игрока
float rayAngle = (player.angle - FOV / 2) + ((float)x / screenWidth) * FOV;
float distanceToWall = 0;
bool hitWall = false;
// Переменные для отслеживания попаданий луча
float eyeX = cos(rayAngle);
float eyeY = sin(rayAngle);
while (!hitWall distanceToWall renderDistance) {
distanceToWall += 0.1;
int testX = (int)(player.x + eyeX * distanceToWall);
int testY = (int)(player.y + eyeY * distanceToWall);
// Проверка столкновения луча с границей карты
if (testX 0 || testX = mapWidth || testY 0 || testY = mapHeight) {
hitWall = true;
distanceToWall = renderDistance; // Установка максимального расстояния
} else if (map[testY][testX] == WALL) {
hitWall = true;
}
}
// Расчёт высоты стены на экране
int ceiling = (screenHeight / 2) - (screenHeight / distanceToWall);
int floor = screenHeight - ceiling;
// Закрашивание столбца стены
for (int y = 0; y screenHeight; y++) {
if (y ceiling) {
DrawCeiling(x, y);
} else if (y floor) {
DrawFloor(x, y);
} else {
int shade = 255 - (distanceToWall * 20); // Чем дальше, тем темнее
SetPixelColor(x, y, RGB(shade, shade, shade));