Итак, обещал -- надо что-то показать.
Начнем с относительно хорошей новости. Собственно,
Код: Выделить всё
int CalcFOVC( FOV_MAP *map, int px, int py, int radius );
Немного не то, что я себе представлял, но работает и работает как положено. CalcLOS нет, об этом ниже.
Карта упакована в FOV_MAP
Код: Выделить всё
typedef struct
{
int Width;
int Height;
FOV_CELL* Cells;
}
FOV_MAP;
где FOV_CELL
Код: Выделить всё
typedef struct
{
unsigned char Flags;
void* Reserved;
}
FOV_CELL;
Конкретные имена типов, полей вообще и поле Reserved в частности -- subject to change. Вполне очевидно, что только начали, сейчас соберемся, подискутируем, подеремся и сделаем хорошо. Идеально не сделаем, я ж пессимист.
Флаги для FOV_CEL::Flags наличествуют следующие: FOV_CELL_VISIBLE (клетка видна игроку), FOV_CELL_OPAQUE (клетка непрозрачна), FOV_CELL_VISITED (клетка была уже видна однажды). Полупрозрачных стен и неоднородного света нет.
К сообщению должен быть прикреплен архив, в нем проекты для Dev-Cpp и бинарные файлы библиотеки и демонстрационной программы. В демо, уж позвольте мне так громко ее называть, можно (и нужно) нажимать стрелки и нумпад, а также клавиши B, F, +, -- и Escape, результат действия каждой нагляден и в пояснениях не нуждается. При желании можно отредактировать файлик map.txt рядом с исполняемым файлом.
Теперь к разным мелочам.
Во-первых, я окончательно и бесповоротно демотивировался в дискретном FOV. Под дискретным я подразумеваю такой, в котором пространство разбито на клеточки и клеточки эти неделимы вообще никак. Ни в жисть не буду делать такой у себя. Я даже проиллюстрирую свою точку зрения:

На этом рисунке изображен кусочек из моих попыток сделать хороший и красивый FOV по клеточкам, но самостоятельно (то, что изображена 1/8 поля непринципиально, все легко масшатбируется зеркалированием и поворотом). Снизу-слева расположена стена с отверстием в одну клетку; серым полупрозрачным -- тени, отбрасываемые стенами. Коричневым отмечены клетки, у которых видна по меньшей мере половина (центр не попал ни в одну тень).
Теперь взгляните на клетки сразу за стеной -- они практически все в тени и не должны бы быть видны. Однако клетки далеко за стеной в немалом количестве попадают в "освещенное" пространство и видны быть уже должны. Клетки, которые видно через клетки, которые не видно o_O? Тени в данном примере специально построены максимально строго (из углов стен). Может показаться, что их можно было бы построить, скажем, от середин сторон проема, что дало бы более широкую освещенную площадь. Такое "решение" -- заблуждение, всегда можно найти такое положение среди комнат и колонн, которое даст узкий луч света. Кроме того, делая тени менее и менее строгими в итоге приходим к ситуации, когда можно заглядывать далеко за углы. Считать клетку освещенной если она на не более, чем на X% в тени? Дохлый номер. В принципе, при некоторой настройке можно пользоваться, но результат, который дают обкатанные опытными рогалико-разработчиками алгоритмы наподобие MRPAS, мне нравится больше.
Что за фигня, во всяких адомах же все спокойненько считается, да? Просто есть момент, когда заканчивается математика и начинается колдунство. Тени, отбрасываемые не по законам оптики; клетки, видимость которых рассчитывается по отвернутой от игрока стороне квадрата, и даже по трем точкам этой стороны; построчный подсчет клеток; проверки, не видны ли еще пара определенных клеток рядом с проверяемой -- и прочее хакерское веселье. От красивого геометрического алгоритма остается вот такенная шелуха:
Код: Выделить всё
if (visible && (m->cells[c-(m->width*dy)].fov == 0 || !m->cells[c-(m->width*dy)].transparent) && (x-dx >= 0 && x-dx < m->width && (m->cells[c-(m->width*dy)-dx].fov == 0 || !m->cells[c-(m->width*dy)-dx].transparent))) visible = false;
Это кусочек из libtcod (забавно, что и в stable, и в beta libtcod в этой процедуре расчета FOV течь; ненамного, примерно 2*mapWidth*mapHeight байт на все время выполнения программы, но все же >_<). Даже приведенный к божескому виду такой код совершенно неочевиден и намертво привязан к квадратным клеткам и выбранному компромиссу между точностью и красотой результата. Какие там шестиугольники =_=
Btw, именно
TCOD_map_compute_fov_restrictive_shadowcasting взят мною за основу
CalcFOVC. Данный метод итеративного FOV на основе теней дает хороший предсказуемый результат при отличной вычислительной сложности. Исходные тексты на сайте автора алгоритма повторяют код из libtcod вплоть до именования структур и переменных, что тоже лол. Текст функции я как мог причесал, сократив его в два раза (понятности не добавило, но хоть править проще будет, в оригинале он состоит из 2-4-8 практически полностью идентичных кусков) и немного пожертвовал производительностью, заменив конструкции типа
m->cells[c-(m->width*dy)-dx].fov на внятное
CellVisible( map, x-dx, y-dy ).
Чем же так фундаментально мне не нравится клеточный FOV? Да тем простым фактом, что как ни крути, а клетка считается освещенной или неосвещенной (или освещенной на столько-то процентов) -- но ЦЕЛИКОМ. Тяжкое наследие консольных приложений в век, когда даже в BIOS уже можно найти Hi-Color и лигатуры. Я ни в коем случае не утверждаю, что old school плохо, но желать всего и сразу не всегда разумно. Я вот пожелал и планомерно набил шишек, потратив время не то чтобы зря, но непродуктивно это уж точно =) По моему скромному мнению, проще сделать честный графический режим с тенями правильной формы (а в программе просто считать освещение в нечеткой логике), нежели методом проб и ошибок подбирать набор условий и постобработок ]:-> Также я теперь понимаю, откуда у идеи libtcod сделать клеточки-из-четырех-клеточек ноги растут, только уж по мне не стоит останавливаться на полпути и следует сразу до пикселей их раздробить =)
Неочевидным следствием из вышесказанного становится следующая проблема: когда видимость ячейки определена по нетривиальному и неочевидному алгоритму, быстро повторить его для простой проверки видно или нет "вот отсюда вот туда" иногда невозможно. Например, тот же брезенхем упорно задевает за стенку, а прямая геометрическая линия либо проходит мимо, либо также пересекает и "невидимую" ячейку. Иными словами, LOS становится какой-никакой, а задачей. Но с этим разберемcя, просто поработать придется. В самом крайнем случае "LOS для монстров, ляляля, ничего не знаю" ^_^.
Оппоненты, ваш ход.
kipar писал(а):В идеале все три библиотеки должны использовать один и тот же массив. Чтобы сгенерил карту и сразу можешь использовать на ней и ФОВ и поиск пути, остается только монстров добавить. Так что подожду FOV от Cfyz и сделаю вызываемую функцию такой же.
В принципе, как-никак, но для расчета пути можно использовать и текущую FOV_MAP (хотя назвать, наверное, придется по-другому тогда), однако для учета веса проходимости клеток придется добавить еще поле в ячейку.
Reserved я оставил впрок с прицелом хранения там указателя (потому и void*) на внутриигровые объекты, которые потом можно будет учитывать/искать/возвращать в разных LOS или подобных.