Difference between revisions of "Mutual Visibility Field Of View"

From RogueBasin
Jump to navigation Jump to search
m (Reverted edits by 46.33.241.195 (talk) to last revision by PaulBlay)
 
Line 1: Line 1:
<pre>
by Jakub Debski
by Jakub Debski


---------------
== INTRODUCTION ==
1. INTRODUCTION
---------------
 
In this article I will not write what the Field Of View (FOV) is and will not write about all the possible ways to implement it. Instead I will focus an algorithm differing from the others in visibility. Mutual visibility guaranties, that if cell_1 is visible from cell_2, then cell_2 is visible from cell_1.
In this article I will not write what the Field Of View (FOV) is and will not write about all the possible ways to implement it. Instead I will focus an algorithm differing from the others in visibility. Mutual visibility guaranties, that if cell_1 is visible from cell_2, then cell_2 is visible from cell_1.
The algorithm was designed mostly for turn based games with ranged weapons.
The algorithm was designed mostly for turn based games with ranged weapons.
Line 12: Line 8:
Let’s look at the typical situation. We have a crossroad and two monsters with ranged weapons (1 and 2).
Let’s look at the typical situation. We have a crossroad and two monsters with ranged weapons (1 and 2).


<pre>
######################
######################
...................2..
...................2..
#####1################
#####1################
#####.################
#####.################
</pre>


With standard FOV Monster 2 will see the monsters 1...
With standard FOV Monster 2 will see the monsters 1...
 
<pre>
######################
######################
...................2..
...................2..
#####1################
#####1################
%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%
</pre>


... but monster 1 will not see the monster 2!
... but monster 1 will not see the monster 2!
 
<pre>
%%%#####%%%%%%%%%%%%%%
%%%#####%%%%%%%%%%%%%%
%%%%...%%%%%%%%%%%%%%%
%%%%...%%%%%%%%%%%%%%%
%%%%#1#%%%%%%%%%%%%%%%
%%%%#1#%%%%%%%%%%%%%%%
%%%%#.#%%%%%%%%%%%%%%%
%%%%#.#%%%%%%%%%%%%%%%
 
</pre>
�#’ are cells that block LOS, dots are cells that do not block and �%’ are not visible cells.
'#' are cells that block LOS, dots are cells that do not block and '%’ are not visible cells.


Monster 2 has unfair advantage over 1 although monster 1 is hidden behind the corner. Monster 1 should be able to lean out the corner and score a headshot or throw grenade, but it even does not see the threat! This situation can lead to instant death of 1.
Monster 2 has unfair advantage over 1 although monster 1 is hidden behind the corner. Monster 1 should be able to lean out the corner and score a headshot or throw grenade, but it even does not see the threat! This situation can lead to instant death of 1.
Line 38: Line 37:


   
   
-------------
== MUTUAL FOV ==
2. MUTUAL FOV
-------------
 
To explain how mutual visibility can be achieved let’s zoom the proper FOV. Simple observation gives us the answer. Visible are these cells, which have any corner visible from any corner of the starting cell.
To explain how mutual visibility can be achieved let’s zoom the proper FOV. Simple observation gives us the answer. Visible are these cells, which have any corner visible from any corner of the starting cell.
 
<pre>
########........########........########.
########........########........########.
########.      ########.      ########.
########.      ########.      ########.
Line 77: Line 73:
.      .*      ###############*%%%%%%%%.
.      .*      ###############*%%%%%%%%.
........*................................
........*................................
 
</pre>
Corners are mutually visible when connecting line (ray) does not intersect any blocking wall.  
Corners are mutually visible when connecting line (ray) does not intersect any blocking wall.  
The simplest implementation of this algorithm is to cast rays from 4 corners of the starting cell to 4 corners of every cell and check intersections. It would be extremely slow and we would have to find a proper way to find intersecting cells. We cannot use the Bresenham’s line algorithm for that, because the line would not go exactly through the required cell. Additional tests would slow the algorithm even more.
The simplest implementation of this algorithm is to cast rays from 4 corners of the starting cell to 4 corners of every cell and check intersections. It would be extremely slow and we would have to find a proper way to find intersecting cells. We cannot use the Bresenham’s line algorithm for that, because the line would not go exactly through the required cell. Additional tests would slow the algorithm even more.
Line 84: Line 80:




----------
=== RULES ===
2.1. RULES
----------
 
These observations allow us to define rules of the algorithm.
These observations allow us to define rules of the algorithm.


Line 94: Line 87:


Rule 2.
Rule 2.
If the corner is visible, then the 4 cells �around� it (the corner belongs to) are visible.
If the corner is visible, then the 4 cells 'around' it (the corner belongs to) are visible.


Rule 3.
Rule 3.
If the line from start corner to end corner intersects any blocking cell, then the end corner is not visible.
If the line from start corner to end corner intersects any blocking cell, then the end corner is not visible.


Suppose that we cast the ray from top-left corner of cell �S’ to bottom-right corner of cell �E’.
Suppose that we cast the ray from top-left corner of cell 'S' to bottom-right corner of cell 'E'.
 
<pre>
S..    S#.
S..    S#.
###    #.#
###    #.#
..E    .#E
..E    .#E
 
</pre>
In both cases line goes through 2 corners of central cell, but only in first case it is blocked.
In both cases line goes through 2 corners of central cell, but only in first case it is blocked.


Line 113: Line 106:




------------------
=== INTERSECTIONS ===
2.2. INTERSECTIONS
------------------
 
As we live in the world of right angles and the distance between cells is always equal 1, calculating intersections is easy task.
As we live in the world of right angles and the distance between cells is always equal 1, calculating intersections is easy task.


 
<pre>
--------------------------delta_x------------------------
--------------------------delta_x------------------------
start              x=3
start              x=3
Line 152: Line 142:
2...................................................... E
2...................................................... E
                                                         end
                                                         end
</pre>


 
<pre>
 
delta_x=abs(end.x-start.x);
delta_x=abs(end.x-start.x);
delta_y=abs(end.y-start.y);
delta_y=abs(end.y-start.y);
</pre>


We are looking for the point (x,n_y) that the line intersects (here at x=3).
We are looking for the point (x,n_y) that the line intersects (here at x=3).
From the Thales' theorms we know that:
From the Thales' theorms we know that:


<pre>
   x        n_y
   x        n_y
-------  = -------
-------  = -------
delta_x    delta_y
delta_x    delta_y
</pre>


therefore
therefore


<pre>
n_y=x*delta_y/delta_x;
n_y=x*delta_y/delta_x;
</pre>


If delta_x is equal zero, we can skip checking vertical intersections and just focus on horizontal.
If delta_x is equal zero, we can skip checking vertical intersections and just focus on horizontal.
To avoid float operations, "modulo" can be used:
To avoid float operations, "modulo" can be used:


<pre>
r_y=x*delta_y%delta_x;
r_y=x*delta_y%delta_x;
</pre>


If r_y is equal zero, then intersection is at the corner and special rules must be used.
If r_y is equal zero, then intersection is at the corner and special rules must be used.
In other case the line intersects the wall at position (start.x + x,start.y + n_y).
In other case the line intersects the wall at position (start.x + x,start.y + n_y).
As a result if cell[start.x + x][start.y + n_y] or cell[start.x + x - 1][start.y + n_y] blocks the LOS, then the end corner is not visible.
As a result if cell[start.x + x][start.y + n_y] or cell[start.x + x - 1][start.y + n_y] blocks the LOS, then the end corner is not visible.


The same calculation we perform looking for horizontal intersections, but for known y:
The same calculation we perform looking for horizontal intersections, but for known y:
<pre>
n_x=y*delta_x/delta_y;
n_x=y*delta_x/delta_y;
 
</pre>
   
   
------------------
=== NEAREST CELLS ===
2.3. NEAREST CELLS
------------------
 
The ray is blocked if it intersects anything on the way, but we must not check the starting and ending corners. Stopping if the end corner blocks LOS would make the destination wall invisible.
The ray is blocked if it intersects anything on the way, but we must not check the starting and ending corners. Stopping if the end corner blocks LOS would make the destination wall invisible.
Stopping on start corners would block all the rays in such situation:
Stopping on start corners would block all the rays in such situation:
 
<pre>
#.#
#.#
.S.
.S.
#.#
#.#
 
</pre>
Hence we have to add additional tests for nearest cells.
Hence we have to add additional tests for nearest cells.
 
<pre>
S..
S..
.#.
.#.
..E
..E
 
</pre>
In the case above between start’s bottom-right corner and end’s top-left there is nothing that blocks! Both corners of internal cell are the start and end corners and the E cell would be visible.
In the case above between start’s bottom-right corner and end’s top-left there is nothing that blocks! Both corners of internal cell are the start and end corners and the E cell would be visible.


--------------
=== ALGORITHM ===
2.4. ALGORITHM
--------------
 
Finally I can present the algorithm:
Finally I can present the algorithm:


<pre>
1. Set all the corners as not visible
1. Set all the corners as not visible
2. To each corner in the FOV (end corners)  
2. To each corner in the FOV (end corners)  
Line 215: Line 209:
3. For each visible corner in FOV
3. For each visible corner in FOV
         Mark 4 cells around it as visible.
         Mark 4 cells around it as visible.
</pre>


Checking if ray from start to end is blocked:
Checking if ray from start to end is blocked:


<pre>
1. Check the nearest cells (situation described in 2.3)
1. Check the nearest cells (situation described in 2.3)
2. For each x from start.x to end.x calculate y
2. For each x from start.x to end.x calculate y
Line 226: Line 221:
         If at (x,y) is a horizontal wall, then ray is blocked
         If at (x,y) is a horizontal wall, then ray is blocked
               If (x,y) is a corner, use additional rule
               If (x,y) is a corner, use additional rule
</pre>


 
=== ADDITIONAL RULES FOR LINES INTERSECTING CORNERS ===
----------------------------------------------------
2.5. ADDITIONAL RULES FOR LINES INTERSECTING CORNERS
----------------------------------------------------
 
The ray is blocked at the corner only sometimes.  
The ray is blocked at the corner only sometimes.  
It is always blocked when the cells around the corner are placed this way:
It is always blocked when the cells around the corner are placed this way:
 
<pre>
##  ..  #.  .#
##  ..  #.  .#
..  ##  #.  .#
..  ##  #.  .#
 
</pre>
It’s also blocked when ray goes diagonally to blocking cell.
It’s also blocked when ray goes diagonally to blocking cell.
 
<pre>
#.  .#
#.  .#
.#  #.
.#  #.
 
</pre>
Ray is not blocked in other cases.
Ray is not blocked in other cases.




---------------
== OPTIMIZATION ==
3. OPTIMIZATION
---------------
 
Simple optimization can be performed, but it does not improve the speed very much.
Simple optimization can be performed, but it does not improve the speed very much.


If we check destination (end) corners in a spiral:
If we check destination (end) corners in a spiral:
 
<pre>
13 14 15 16 17 18
13 14 15 16 17 18
32 1  2  3  4  19
32 1  2  3  4  19
Line 260: Line 249:
29 10 9  8  7  22
29 10 9  8  7  22
28 27 26 25 24 23
28 27 26 25 24 23
 
</pre>
then we can use �shadow propagation�. It means that if cell is blocked, all the further cells in the same direction are blocked too:
then we can use 'shadow propagation'. It means that if corner is blocked, all the further corners in the same direction are blocked too:
 
<pre>
...............%..
...............%..
.............%....
.............%....
Line 270: Line 259:
.....#............
.....#............
...S..............
...S..............
</pre>


 
== PERFORMANCE ==
--------------
4. PERFORMANCE
--------------
 
Performance was measured on Intel Pentium 1,8GHz and is presented in FOV calculations/sec (Fc/s). Calculations were done on demo level (80x25 cells).
Performance was measured on Intel Pentium 1,8GHz and is presented in FOV calculations/sec (Fc/s). Calculations were done on demo level (80x25 cells).


Line 292: Line 278:
Open area with a few walls, FOV radius 80: 880  Fc/s
Open area with a few walls, FOV radius 80: 880  Fc/s


The complexity of this algorithm is O(n^2), when n is the number of corners.


--------------
== CONCLUSIONS ==
5. CONCLUSIONS
--------------
 
The Mutual FOV is not the fastest FOV algorithm possible, but is fast enough (look at the previous section). It gives mutual visibility of cells, no problems with long corridors, no problems with corners... and just works very well :)
The Mutual FOV is not the fastest FOV algorithm possible, but is fast enough (look at the previous section). It gives mutual visibility of cells, no problems with long corridors, no problems with corners... and just works very well :)
You are free to use it.
You are free to use it.


-------
== DEMO ==
6. DEMO
-------
 
C++ Source code + Windows executable is available here
C++ Source code + Windows executable is available here
http://pillsm.com/?a=7539
http://www.alamak0ta.republika.pl/mutual_fov.zip
 
</pre>


[[Category:LOS]][[Category:Articles]]
[[Category:LOS]][[category:FOV]]

Latest revision as of 21:55, 29 August 2011

by Jakub Debski

INTRODUCTION

In this article I will not write what the Field Of View (FOV) is and will not write about all the possible ways to implement it. Instead I will focus an algorithm differing from the others in visibility. Mutual visibility guaranties, that if cell_1 is visible from cell_2, then cell_2 is visible from cell_1. The algorithm was designed mostly for turn based games with ranged weapons. In such games lack of mutual visibility cause unfair advantage of attacker, when in reality target should be on better position.

Let’s look at the typical situation. We have a crossroad and two monsters with ranged weapons (1 and 2).

######################
...................2..
#####1################
#####.################

With standard FOV Monster 2 will see the monsters 1...

######################
...................2..
#####1################
%%%%%%%%%%%%%%%%%%%%%%

... but monster 1 will not see the monster 2!

%%%#####%%%%%%%%%%%%%%
%%%%...%%%%%%%%%%%%%%%
%%%%#1#%%%%%%%%%%%%%%%
%%%%#.#%%%%%%%%%%%%%%%

'#' are cells that block LOS, dots are cells that do not block and '%’ are not visible cells.

Monster 2 has unfair advantage over 1 although monster 1 is hidden behind the corner. Monster 1 should be able to lean out the corner and score a headshot or throw grenade, but it even does not see the threat! This situation can lead to instant death of 1.

What we need is mutual visibility in field of view.


MUTUAL FOV

To explain how mutual visibility can be achieved let’s zoom the proper FOV. Simple observation gives us the answer. Visible are these cells, which have any corner visible from any corner of the starting cell.

########........########........########.
########.       ########.       ########.
########.       ########.       ########.
########.       ########.       ########.
########.       ########.       ########.
########.       ########.       ########.
########.       ########.       ########.
*#######.       ########.       ########.
.*.......................................
. **    .       .       .       .       .
.   **  .       .       .       .       .
.     **.       .       .       .       .
.       **      .       .       .       .
.       . **    .       .       .       .
.       .   **  .       .       .       .
.       .     **.       .       .       .
%%%%%%%%########*.......########%%%%%%%%.
%%%%%%%%########.       ########%%%%%%%%.
%%%%%%%%########.       ########%%%%%%%%.
%%%%%%%%########.       ########%%%%%%%%.
%%%%%%%%########.   @   ########%%%%%%%%.
%%%%%%%%########.       ########%%%%%%%%.
%%%%%%%%########.       ########%%%%%%%%.
%%%%%%%%########.       ########%%%%%%%%.
................*#######********%%%%%%%%.
.       .      *#########*######%%%%%%%%.
.       .     * ##########*#####%%%%%%%%.
.       .    *  ###########*####%%%%%%%%.
.       .   *   ############*###%%%%%%%%.
.       .  *    #############*##%%%%%%%%.
.       . *     ##############*#%%%%%%%%.
.       .*      ###############*%%%%%%%%.
........*................................

Corners are mutually visible when connecting line (ray) does not intersect any blocking wall. The simplest implementation of this algorithm is to cast rays from 4 corners of the starting cell to 4 corners of every cell and check intersections. It would be extremely slow and we would have to find a proper way to find intersecting cells. We cannot use the Bresenham’s line algorithm for that, because the line would not go exactly through the required cell. Additional tests would slow the algorithm even more.

As an alternative we should use the observation, that the map is the domain of the right angles. It simplifies calculating of intersections. We also can see that the line is going from corner to corner, not from cell to cell. It reduces required calculations 4 times.


RULES

These observations allow us to define rules of the algorithm.

Rule 1. We have to cast a ray from 4 corners of starting cell to all the corners in FOV.

Rule 2. If the corner is visible, then the 4 cells 'around' it (the corner belongs to) are visible.

Rule 3. If the line from start corner to end corner intersects any blocking cell, then the end corner is not visible.

Suppose that we cast the ray from top-left corner of cell 'S' to bottom-right corner of cell 'E'.

S..     S#.
###     #.#
..E     .#E

In both cases line goes through 2 corners of central cell, but only in first case it is blocked.

Rule 4. When the line intersects corners, additional rules have to be applied. These rules will be shown later.

We have the rules defined and now we have to find the proper way of intersection calculation.


INTERSECTIONS

As we live in the world of right angles and the distance between cells is always equal 1, calculating intersections is easy task.

--------------------------delta_x------------------------
start               x=3
0*************1*************2*************3.............4     |
  ..          .             .             *             .     |
    ..        .             .             *             .     |
      ..      .             .             *             .     |
        ..    .             .             *             .     |
          ..  .             .             *             .     |
            ...             .             *             .     |
              ..            .             *             .     |
              . ..          .             *             .     |
              .   ..        .            n_y            .     |
              .     ..      .             *             .     |
              .       ..    .             *             .     |
              .         ..  .             *             .     |
              .           ...             *             .     |
1.........................................*..............   delta_y
              .             . ..          *             .     |
              .             .   ..        *             .     |
              .             .     ..      *             .     |
              .             .       ..    *             .     |
              .             .         ..  *             .     |
              .             .           ..*             .     |
              .             .             ..            .     |
              .             .             . ..          .     |
              .             .             .   ..        .     |
              .             .             .     ..      .     |
              .             .             .       ..    .     |
              .             .             .         ..  .     |
              .             .             .           ...     |
2...................................................... E
                                                        end
delta_x=abs(end.x-start.x);
delta_y=abs(end.y-start.y);

We are looking for the point (x,n_y) that the line intersects (here at x=3). From the Thales' theorms we know that:

   x         n_y
-------  = -------
delta_x    delta_y

therefore

n_y=x*delta_y/delta_x;

If delta_x is equal zero, we can skip checking vertical intersections and just focus on horizontal. To avoid float operations, "modulo" can be used:

r_y=x*delta_y%delta_x;

If r_y is equal zero, then intersection is at the corner and special rules must be used. In other case the line intersects the wall at position (start.x + x,start.y + n_y).

As a result if cell[start.x + x][start.y + n_y] or cell[start.x + x - 1][start.y + n_y] blocks the LOS, then the end corner is not visible.

The same calculation we perform looking for horizontal intersections, but for known y:

n_x=y*delta_x/delta_y;

NEAREST CELLS

The ray is blocked if it intersects anything on the way, but we must not check the starting and ending corners. Stopping if the end corner blocks LOS would make the destination wall invisible. Stopping on start corners would block all the rays in such situation:

#.#
.S.
#.#

Hence we have to add additional tests for nearest cells.

S..
.#.
..E

In the case above between start’s bottom-right corner and end’s top-left there is nothing that blocks! Both corners of internal cell are the start and end corners and the E cell would be visible.

ALGORITHM

Finally I can present the algorithm:

1. Set all the corners as not visible
2. To each corner in the FOV (end corners) 
        from each corner from start (4 corners)
		    Check if ray from start to end is blocked
        If any ray these 4 was not blocked
		    Then the end corner is visible from start.
3. For each visible corner in FOV
        Mark 4 cells around it as visible.

Checking if ray from start to end is blocked:

1.	Check the nearest cells (situation described in 2.3)
2.	For each x from start.x to end.x calculate y
         If at (x,y) is a vertical wall, then ray is blocked
              If (x,y) is a corner, use additional rule
3.	For each y from start.y to end.y calculate x
         If at (x,y) is a horizontal wall, then ray is blocked
              If (x,y) is a corner, use additional rule

ADDITIONAL RULES FOR LINES INTERSECTING CORNERS

The ray is blocked at the corner only sometimes. It is always blocked when the cells around the corner are placed this way:

##  ..  #.  .#
..  ##  #.  .#

It’s also blocked when ray goes diagonally to blocking cell.

#.  .#
.#  #.

Ray is not blocked in other cases.


OPTIMIZATION

Simple optimization can be performed, but it does not improve the speed very much.

If we check destination (end) corners in a spiral:

13 14 15 16 17 18
32 1  2  3  4  19
31 12 S  S  5  20
30 11 S  S  6  21
29 10 9  8  7  22
28 27 26 25 24 23

then we can use 'shadow propagation'. It means that if corner is blocked, all the further corners in the same direction are blocked too:

...............%..
.............%....
...........%......
.........%........
.......%..........
.....#............
...S..............

PERFORMANCE

Performance was measured on Intel Pentium 1,8GHz and is presented in FOV calculations/sec (Fc/s). Calculations were done on demo level (80x25 cells).

Typical roguelike level, FOV radius 10: 13200 Fc/s Typical roguelike level, FOV radius 80: 3400 Fc/s

Open area with a few walls, FOV radius 10: 8300 Fc/s Open area with a few walls, FOV radius 80: 980 Fc/s

Without the "shadow propagation":

Typical roguelike level, FOV radius 10: 12500 Fc/s Typical roguelike level, FOV radius 80: 2700 Fc/s

Open area with a few walls, FOV radius 10: 7700 Fc/s Open area with a few walls, FOV radius 80: 880 Fc/s

The complexity of this algorithm is O(n^2), when n is the number of corners.

CONCLUSIONS

The Mutual FOV is not the fastest FOV algorithm possible, but is fast enough (look at the previous section). It gives mutual visibility of cells, no problems with long corridors, no problems with corners... and just works very well :) You are free to use it.

DEMO

C++ Source code + Windows executable is available here http://www.alamak0ta.republika.pl/mutual_fov.zip