Найти в Дзене
ZDG

Блог разработки игры Crypt Quest Remake: Тени

Предыдущая часть: Карта в игре отображается вот так: Это просто набор плоских плиток. Но для придания "объёма" к некоторым стенкам и объектам пририсовываются тени. Обозначу их пока что схематически: Уже стало лучше. Такие тени можно назвать процедурными, потому что они вычисляются динамически для каждой плитки в зависимости от того, какие соcеди её окружают. А точнее, не окружают, а находятся слева, сверху и слева-сверху. Вот все возможные конфигурации: И в целом задача ясна: при рисовании плитки нужно посмотреть, в каких сочетаниях находятся три соседние плитки, и нарисовать соответствующую тень. У автора в оригинале для этой цели используются полупрозрачные битмапы в уже готовом виде, например: Я же буду рисовать тени процедурно, тем более что это ничем не будет отличаться от блиттинга битмапа, только без битмапа. Можно заметить, что угловая внутренняя тень состоит из вертикального и горизонтального сегмента, которые сами по себе тоже варианты тени. Так что возникает соблазн собират

Предыдущая часть:

Карта в игре отображается вот так:

Это просто набор плоских плиток. Но для придания "объёма" к некоторым стенкам и объектам пририсовываются тени. Обозначу их пока что схематически:

-2

Уже стало лучше. Такие тени можно назвать процедурными, потому что они вычисляются динамически для каждой плитки в зависимости от того, какие соcеди её окружают. А точнее, не окружают, а находятся слева, сверху и слева-сверху.

Вот все возможные конфигурации:

-3

И в целом задача ясна: при рисовании плитки нужно посмотреть, в каких сочетаниях находятся три соседние плитки, и нарисовать соответствующую тень.

У автора в оригинале для этой цели используются полупрозрачные битмапы в уже готовом виде, например:

-4

Я же буду рисовать тени процедурно, тем более что это ничем не будет отличаться от блиттинга битмапа, только без битмапа.

Можно заметить, что угловая внутренняя тень состоит из вертикального и горизонтального сегмента, которые сами по себе тоже варианты тени. Так что возникает соблазн собирать тень как конструктор.

Да, именно так я и буду делать,

Но есть нюанс

Можно также заметить, что тень не простая, а градиентная. Это позволяет сделать её границу мягкой. И направление градиента в вертикальном и горизонтальном сегменте разное.

Это вызовет дополнительные сложности с рисованием, например, такого угла:

-5

Он выглядит как простой квадратик, но должен быть составлен из двух разных кусочков:

-6

А тень номер 5 должна рисоваться как 3+4.

Итого, нужно уметь рисовать: горизонтальный прямоугольник с вертикальным градиентом, вертикальный прямоугольник с горизонтальным градиентом, те же прямоугольники, только со срезанным углом, и те же прямоугольники, только со срезанным другим углом.

Горизонтальный прямоугольник рисуется построчно. Если к началу каждой строки добавлять смещение 1, то мы получим диагональный срез левой части прямоугольника, а если каждую следующую строку укорачивать или удлинять на 1, то получим диагональный срез правой части.

Сначала функция для горизонтальной тени с вертикальным градиентом:

-7

tile_x и tile_y это экранные координаты плитки, w и h это ширина и высота прямоугольника тени, begin_step и end_step это смещения, прибавляемые к началу и концу линии, и begin_dim и end_dim это начальная и конечная степень затенения. Таким образом, тени можно настраивать.

Совершенно аналогично будет выглядеть функция для рисования вертикального сегмента тени draw_v_shadow_gradient(), просто в ней будут рисоваться не строки сверху вниз, а столбцы слева направо, так как градиент идёт по горизонтали.

Теперь займёмся определением того, какие части тени и когда рисовать. Для этого надо проверить только три варианта:

-8

Заведём битовую маску. Если есть бросающий тень объект слева, установим в маске первый бит (значение 1). Если есть слева-сверху, установим второй бит (значение 2). Если есть сверху, установим третий бит (значение 4). Итого маска может иметь 8 разных значений от 0 до 7.

А вот и разбор маски:

-9

В зависимости от того, какие биты установлены, рисуем разные части тени. Особо тут ничего не объясняю, так как результат получен эмпирическим путём после проверки вариантов на бумажке.

Ну и вот финальные тени:

-10

(Деревянный ящик не отбрасывает тень, но это не баг, просто в дальнейшем он будет перерисован в что-то другое.)