Я бы предпочел отбрасывать теневые лучи вместо лучей прямой видимости.
Допустим, это ваша область обзора (потенциально видимая область)
######################
#####.............####
###................###
##..................##
#....................#
#....................#
#..........@.........#
#....................#
#....................#
##..................##
###................###
#####.............####
######################
Блоки # не видны, пока. видны
Давайте поставим некоторое препятствие X:
######################
#####.............####
###................###
##.....X.....XXX....##
#......X.......X.....#
#...X.XX.............#
#...X......@.........#
#...X..........X.....#
#...XXXXXX...........#
##..................##
###....X...........###
#####.............####
######################
У вас есть список X, которые находятся внутри области просмотра, и вы помечаете как скрытые все плитки, которые находятся за каждым из этих препятствий: когда препятствие помечается как скрытое, вы удаляете его из списка.
######################
#####.............####
###................###
##.....X.....XXX....##
#......X.......X.....#
#...X.XX.............#
#...X......@.........#
#...X..........X.....#
#...XXXXX*...........#
##......##..........##
###....*#..........###
#####.###.........####
######################
В приведенном выше примере вы можете видеть тень, отбрасываемую самой правой нижней стенкой, и то, как эта тень удаляет скрытое препятствие из списка препятствий, которые вы должны проверить (X должен проверить; * проверен).
Если вы сортируете список по некоторому двоичному разделу, чтобы сначала проверять самый близкий X, вы можете немного ускорить проверку.
Вы можете использовать своего рода алгоритм «Naval Battles» для одновременной проверки блока X (в основном, ищите вспомогательный X, который находится в направлении, которое может сделать конус тени шире)
[РЕДАКТИРОВАТЬ]
Для правильного отбрасывания тени необходимы два луча, и, поскольку плитка является прямоугольной, можно сделать много предположений, используя доступные симметрии.
Координаты луча могут быть вычислены с помощью простого разделения пространства вокруг плитки препятствий:
Каждая прямоугольная область представляет собой выбор того, какой угол плитки следует принять за край теневого конуса.
Эта логика может быть продолжена, чтобы соединить несколько смежных плиток и позволить им создать один более широкий конус следующим образом.
Первый шаг состоит в том, чтобы убедиться, что никакие препятствия не направлены в направлении наблюдателя, в этом случае вместо этого рассматривается ближайшее препятствие:
Если желтая плитка является препятствием, эта плитка становится новой красной плиткой.
Теперь давайте рассмотрим верхний край конуса:
Синие плитки - все возможные кандидаты, чтобы позволить теневому конусу шире: если хотя бы одно из них является препятствием, луч можно перемещать, используя пространство, разделяющее эту плитку, как показано ранее.
Зеленая плитка является кандидатом, только если наблюдатель находится над оранжевой линией, которая следует за:
То же самое относится к другому лучу и другим позициям наблюдателя относительно красного препятствия.
Основная идея состоит в том, чтобы покрыть как можно большую площадь для каждого заклинания конуса и как можно быстрее сократить список препятствий, которые необходимо проверить.