-
Notifications
You must be signed in to change notification settings - Fork 30
Field Of View
GoRogue offers a high-performance class for FOV that can support a variety of different shapes.
- Table of Contents
- Code Examples
- FOV vs SenseMap
- Constructing an FOV Instance
- Calculating FOV
- Reading FOV Output
- Printing an FOV Instance
- Exposing FOV as Read-Only
Code examples in this section show only code in the Main function. The code provided assumes that the following "using" statements are at the top of the code file:
using GoRogue;
using GoRogue.MapGeneration;
using GoRogue.MapViews;
The FOV class is designed to implement a single-source, more or less roguelike-standard FOV algorithm in a simplistic way. GoRogue does offer support for light/multiple sensory-based sources, however -- see the Sense Mapping system for these use cases.
To create an FOV
instance, you must simply give it an IMapView<bool>
instance (see Map View documentation for details). The IMapView
should produce a value of true for locations that are transparent (do not block FOV), and false for locations that do block FOV. Alternatively, for convenience you may pass it an IMapView<double>
like the views that Sense Mapping takes as input.
ArrayMap<bool> map = new ArrayMap<bool>(10, 10);
// Sets edges to false, and all other locations to true. See Map Generation documentation for details
QuickGenerators.GenerateRectangleMap(map);
FOV fov = new FOV(map);
// IMapView that converts values of true in map to 0.0, and values of false in map to 1.0. This is a
// mock-up for values that SenseMap would take as input, see SenseMap documentation for details.
IMapView<double> senseMapView = new LambdaTranslationMap<bool, double>(map, val => val ? 0.0 : 1.0);
FOV doubleFov = new FOV(senseMapView);
Once an FOV
instance has been created, you can call one of the various overloads of Calculate
to complete the FOV calculation.
In its most basic form, you simply pass to Calculate
a source location. The radius of the calculated FOV is assumed to be infinite.
ArrayMap<bool> map = new ArrayMap<bool>(10, 10);
// Sets edges to false, and all other locations to true. See Map Generation documentation for details
QuickGenerators.GenerateRectangleMap(map);
// Create an obstacle for FOV demonstration right next to the source
map[4, 5] = false;
FOV fov = new FOV(map);
Coord start = Coord.Get(5, 5);
fov.Calculate(start);
// We can also specify the source as x/y value, rather than a Coord:
// fov.Calculate(start.X, start.Y);
// See documentation of Stringifying FOV for details. In this output, '+' means a value is in FOV,
// and '-' means it is outside. Notice how the walls on the visible edges are considered in FOV -- as
// per standard, walls in FOV are considered "lit"
System.Console.WriteLine(fov.ToString());
You may also pass to Calculate
a radius for the resulting FOV. Any location outside this radius is guaranteed to be outside the resulting FOV. Since we do not specify a Distance/Radius shape, it defaults to using a circle (Euclidean distance):
ArrayMap<bool> map = new ArrayMap<bool>(10, 10);
// Sets edges to false, and all other locations to true. See Map Generation documentation for details
QuickGenerators.GenerateRectangleMap(map);
// Create an obstacle for FOV demonstration right next to the source
map[4, 5] = false;
FOV fov = new FOV(map);
Coord start = Coord.Get(5, 5);
fov.Calculate(start, 3);
// We can also specify the source as x/y value, rather than a Coord:
// fov.Calculate(start.X, start.Y, 3);
// See documentation of Stringifying FOV for details. In this output, '+' means a value is in FOV,
// and '-' means it is outside. Notice how the walls on the visible edges are considered in FOV -- as
// per standard, walls in FOV are considered "lit"
System.Console.WriteLine(fov.ToString());
Finally, we may also specify a Radius instance to Calculate
to specify the shape of the resulting FOV (provided it is unobstructed). Since Radius and Distance are implicitly convertable to each other, we can also specify a Distance instance that determines the radius shape:
ArrayMap<bool> map = new ArrayMap<bool>(10, 10);
// Sets edges to false, and all other locations to true. See Map Generation documentation for details
QuickGenerators.GenerateRectangleMap(map);
// Create an obstacle for FOV demonstration right next to the source
map[4, 5] = false;
FOV fov = new FOV(map);
Coord start = Coord.Get(5, 5);
fov.Calculate(start, 3, Radius.SQUARE);
// Note that we can also specify an x and y value for the source.
// As per the Distance/Radius documentation linked above, CHEBYSHEV distance
// defines distance in such a way that the radius is a square
// fov.Calculate(start.X, start.Y, Distance.CHEBYSHEV);
// See documentation of Stringifying FOV for details. In this output, '+' means a valueis in FOV,
// and '-' means it is outside. Notice how the walls on the visible edges are considered in FOV -- as
// per standard, walls in FOV are considered "lit"
System.Console.WriteLine(fov.ToString());
The above examples calculate FOV as 360 degree shapes. You may, in addition to the parameters above, pass an angle
and span
parameter to Calculate
. The angle
parameter is an angle in degrees that specifies the "direction" the field of view faces. 0 degrees indicates that the FOV faces right (that is, its center is directly to the right of the source), so 90 faces down, and so forth. The span
parameter specifies the number of degrees (total) the arc of the field of view covers. Exactly span/2
degrees will be included in FOV on either side of the center point (defined by the angle
given). For example, a 60 degree span
will cover 30 degrees on either side of the center:
ArrayMap<bool> map = new ArrayMap<bool>(10, 10);
// Sets edges to false, and all other locations to true. See Map Generation documentation for details
QuickGenerators.GenerateRectangleMap(map);
// Create an obstacle for FOV demonstration right next to the source
map[4, 5] = false;
FOV fov = new FOV(map);
Coord start = Coord.Get(5, 5);
// FOV that points downward, and covers 30 degrees on either side of the center
fov.Calculate(start, 3, Radius.SQUARE, 90, 60);
// Note that we can also specify an x and y value for the source.
// As per the Distance/Radius documentation linked above, CHEBYSHEV distance
// defines distance in such a way that the radius is a square
// fov.Calculate(start.X, start.Y, Distance.CHEBYSHEV, 90, 60);
// See documentation of Stringifying FOV for details. In this output, '+' means a valueis in FOV,
// and '-' means it is outside. Notice how the walls on the visible edges are considered in FOV -- as
// per standard, walls in FOV are considered "lit"
System.Console.WriteLine(fov.ToString());
The FOV class exposes the results of the last calculation in a number of ways.
The simplest way to access the current FOV is via the FOV.BooleanFOV
property. This property is an IMapView<bool>
instance that, for a given location, returns true
if that location is in the current fov (visible), and false
if that location is outside fov (not visible):
ArrayMap<bool> map = new ArrayMap<bool>(10, 10);
// Sets edges to false, and all other locations to true. See Map Generation documentation for details
QuickGenerators.GenerateRectangleMap(map);
// Create an obstacle for FOV demonstration right next to the source
map[4, 5] = false;
FOV fov = new FOV(map);
Coord start = Coord.Get(5, 5);
fov.Calculate(start, 3);
System.Console.WriteLine("(2, 5) in FOV: " + fov.BooleanFOV[2, 5]);
System.Console.WriteLine("Start position in FOV: " + fov.BooleanFOV[start]); // Source is always visible
The FOV
class itself also implements IMapView<double>
, in order to expose the FOV values as doubles. A value of 1.0 is the start location, and 0.0 is not visible. The values in between fall off linearly with respect to the radius given:
ArrayMap<bool> map = new ArrayMap<bool>(10, 10);
// Sets edges to false, and all other locations to true. See Map Generation documentation for details
QuickGenerators.GenerateRectangleMap(map);
// Create an obstacle for FOV demonstration right next to the source
map[4, 5] = false;
FOV fov = new FOV(map);
Coord start = Coord.Get(5, 5);
fov.Calculate(start, 3);
System.Console.WriteLine("(2, 5) FOV value: " + fov[2, 5]); // (2, 5) is not visible
System.Console.WriteLine("Start position FOV value: " + fov[start]); // Source is always visible at 1.0
// Falls off at a rate of 1/(radius+1) (to ensure we include the starting point), which is 1/4 = 0.25
// per tile from source. This location is 1 tile away from the source so it has a value of 0.75
System.Console.WriteLine("Start position FOV value: " + fov[start.X + 1, start.Y]);
The FOV
class also has a property CurrentFOV
, which is a HashSet<Coord>
containing all locations that are in the FOV (as it was calculated the last time Calculate
was called):
ArrayMap<bool> map = new ArrayMap<bool>(10, 10);
// Sets edges to false, and all other locations to true. See Map Generation documentation for details
QuickGenerators.GenerateRectangleMap(map);
// Create an obstacle for FOV demonstration right next to the source
map[4, 5] = false;
FOV fov = new FOV(map);
Coord start = Coord.Get(5, 5);
fov.Calculate(start, 3);
// GoRogue-defined extension method to print IEnumerable<T> like a Python list
System.Console.WriteLine(fov.CurrentFOV.ExtendToString());
GoRogue FOV also provides an easy methods to iterate over values that are visible in the currently calculated FOV that were not visible in the previously calculated FOV. The FOV
class exposes the NewlySeen
property for this:
ArrayMap<bool> map = new ArrayMap<bool>(10, 10);
// Sets edges to false, and all other locations to true. See Map Generation documentation for details
QuickGenerators.GenerateRectangleMap(map);
FOV fov = new FOV(map);
Coord start = Coord.Get(5, 5);
// Calculate FOV, then calculate it again from a spot 1 x-value to the right
fov.Calculate(start, 3, Radius.SQUARE);
fov.Calculate(start.X + 1, start.Y, 3, Radius.SQUARE);
// GoRogue-defined extension method to print IEnumerable<T> like a Python list
// Note how only the 7 (2 * radius + 1) newly visible coordinates print
System.Console.WriteLine(fov.NewlySeen.ExtendToString());
Similar to NewlySeen
, GoRogue also provides a convenient method to get any coordinates that are hidden in the currently calculated FOV that were visible in the previously calculated FOV. The FOV
class exposes the NewlyUnseen
property for this:
ArrayMap<bool> map = new ArrayMap<bool>(10, 10);
// Sets edges to false, and all other locations to true. See Map Generation documentation for details
QuickGenerators.GenerateRectangleMap(map);
FOV fov = new FOV(map);
Coord start = Coord.Get(5, 5);
// Calculate FOV, then calculate it again from a spot 1 x-value to the right
fov.Calculate(start, 3, Radius.SQUARE);
fov.Calculate(start.X + 1, start.Y, 3, Radius.SQUARE);
// GoRogue-defined extension method to print IEnumerable<T> like a Python list
// Note how only the 7 (2 * radius + 1) newly hidden coordinates print
System.Console.WriteLine(fov.NewlyUnseen.ExtendToString());
GoRogue's FOV
class provides a number of ToString
overloads and other stringifying-functions to help print FOV to the screen (which may be useful for debugging).
The no-parameter version of FOV.ToString
simply prints the FOV in a grid to the screen, where '+' represents any location inside FOV
, and '-' represents any location outside of FOV. It can also optionally take characters to substitute for the '+' and '-' characters. Finally, to print the double values, we may specify a single integer to ToString
indicating the number of decimal places to print for each value:
ArrayMap<bool> map = new ArrayMap<bool>(10, 10);
// Sets edges to false, and all other locations to true. See Map Generation documentation for details
QuickGenerators.GenerateRectangleMap(map);
FOV fov = new FOV(map);
Coord start = Coord.Get(5, 5);
fov.Calculate(start, 3);
System.Console.WriteLine("FOV default ToString:");
System.Console.WriteLine(fov);
System.Console.WriteLine("FOV with custom chars:");
System.Console.WriteLine(fov.ToString(' ', '.'));
System.Console.WriteLine("FOV values to 2 decimal places:");
System.Console.WriteLine(fov.ToString(2));
The ToString
functions of FOV
can cover many common cases, however because FOV
implements IMapView
, the IMapView.ExtendToString
function overloads can be used to provide additional customization. The following code example shows that the ExtendToString
function can be used on either FOV
or the FOV.BooleanFOV
property. ExtendToString
offers a number of parameters that can provide extremely advanced customization, which are covered in the IMapView ExtendToString documentation.
ArrayMap<bool> map = new ArrayMap<bool>(10, 10);
// Sets edges to false, and all other locations to true. See Map Generation documentation for details
QuickGenerators.GenerateRectangleMap(map);
FOV fov = new FOV(map);
Coord start = Coord.Get(5, 5);
fov.Calculate(start, 3);
System.Console.WriteLine("FOV ExtendToString default params:");
System.Console.WriteLine(fov.ExtendToString(fieldSize: 4, elementStringifier: d => d.ToString("0.00")));
System.Console.WriteLine("FOV.BooleanFOV ExtendToString default params:");
System.Console.WriteLine(fov.BooleanFOV.ExtendToString(fieldSize: 5));
In a complex map structure, it may be useful to expose a "read-only" view of an FOV
instance -- that is, an instance that exposes only the FOV
properties and methods that do not modify the FOV
. GoRogue's FOV
implements IReadOnlyFOV
, which exposes the CurrentFOV
, NewlySeen
, NewlyUnseen
, and BooleanFOV
properties, as well as all functionality from IMapView<double>
:
class Map
{
private FOV _fov;
public IReadOnlyFOV FOV { get => _fov; }
public Map(ArrayMap<bool> generatedTerrain)
{
_fov = new FOV(generatedTerrain);
}
public void CalculateFOV(Coord start, int radius)
{
_fov.Calculate(start, radius);
// Here you might perform other functions that should happen each time FOV is calculated,
// such as updating explored locations, etc.
}
}
class Program
{
static void Main(string[] args)
{
ArrayMap<bool> terrainMap = new ArrayMap<bool>(10, 10);
// Sets edges to false, and all other locations to true. See Map Generation documentation for details
QuickGenerators.GenerateRectangleMap(terrainMap);
Map map = new Map(terrainMap);
map.CalculateFOV(Coord.Get(5, 5), 3);
System.Console.WriteLine("Current FOV:");
System.Console.WriteLine(map.FOV);
// Uncommenting this would produce a compile-time error, since Calculate modifies the FOV state
// map.FOV.Calculate(Coord.Get(5, 5), 3);
}
}
- Home
- Getting Started
- GoRogue 1.0 to 2.0 Upgrade Guide
- Grid System
- Dice Rolling
- Effects System
- Field of View
- Map Generation (docs coming soon)
- Map View System
- Pathfinding (docs coming soon)
- Random Number Generation (docs coming soon)
- Sense Mapping (docs coming soon)
- Spatial Maps (docs coming soon)
- Utility/Miscellaneous (docs coming soon)