NAME
Game::RaycastFOV - raycast field-of-view and related routines
SYNOPSIS
use Game::RaycastFOV qw(bypair circle line);
bypair( { my ($x,$y) = @_; ... } $x1, $y1, $x2, $y2, ...);
# Bresenham in XS
circle( { my ($cx,$cy) = @_; ... } $x, $y, $radius);
line( { my ($lx,$ly) = @_; ... } $x0, $y0, $x1, $y1);
raycast( \&circle, sub { ... }, $x, $y, ...);
# alternative (faster, slower) circle constructions
cached_circle( { my ($cx,$cy) ... } $x, $y, $radius)
swing_circle( { my ($cx,$cy) ... } $x, $y, $radius, $swing);
DESCRIPTION
This module contains various subroutines for fast integer calculation of lines and circles (and a slow one, too) that help perform Field Of View (FOV) calculations to show what cells are visible from a given cell via raycasting out from that cell. Raycasting visits adjacent squares lots especially as the FOV grows so will benefit from caching and more closed-in than open level layouts.
Raycast Explained in One Thousand Words or Less
.#.##
.##.##### #
#.##..##... #.
.##.#.##.#... #.
#####..#.#### # #.
.#.#.#.###.##.. #.#.##
####....#.##... #....#
##...#.@#....## #.@#
#..#.###....#.# ###..
.##.#####..#... #..
.##...####.## ##.#
....#.###.#.. .
###.###.#..
.######.#
....#
FUNCTIONS
- bypair callback ...
-
Utility function for slicing up an arbitrary list pairwise. Sort of like
pairwise
of List::Util only in a void context, and that returning the value-1
from the callback subroutine will abort the processing of subsequent items in the input list. - bypairall callback ...
-
Like bypair but does not include code to abort processing the list. Since v1.01.
- cached_circle callback x y radius
-
This routine looks up the radius in the
%circle_points
variable (which can be modified by users of this module) to obtain a pre-computed list of circle points that are fed to the callback as is done for the circle call.Will silently do nothing if the radius is not found in the cache. This is by design so that cached_circle is fast.
NOTE these cached points may change without notice; applications should if necessary set their own specific sets of points to use.
- circle callback x y radius
-
Bresenham circle via fast integer math. Note that this may not produce a completely filled-in FOV at various radius. Also note that this call will produce duplicate values for various points, especially for small radius.
- line callback x0 y0 x1 y1
-
Bresenham line via fast integer math. Returning the value
-1
from the callback subroutine will abort the processing of the line at the given point. - raycast circle-fn point-fn x y ...
-
Given a circle-fn such as circle or swing_circle, the raycast calls line between x and y and the points returned by the circle function; line in turn will call the user-supplied point-fn to handle what should happen at each raycasted point. Additional arguments ... will be passed to the circle-fn following x and y (the center of the circle. "EXAMPLES" may be of more help?
- swing_circle callback x0 y0 radius swing
-
Constructs points around the given radius by rotating a ray by swing radians over a complete circle. Smaller swing values will result in a more complete circle at the cost of additional CPU and memory use.
cached_circle uses values pre-computed from this call but only for specific radius.
EXAMPLES
See also the eg/
directory of this module's distribution.
https://github.com/thrig/ministry-of-silly-vaults/ has a FOV subdirectory with example scripts.
use Game::RaycastFOV qw(circle raycast swing_circle);
use Math::Trig 'deg2rad';
# to only draw within the map area
our $MAX_X = 79;
our $MAX_Y = 23;
# assuming a rows/columns array-of-arrays with characters
our @map = ( ... );
sub plot { ... }
my ($x, $y, $radius) = ...;
raycast(
\&circle, sub {
my ($lx, $ly) = @_;
# whoops line has wandered outside of map
return -1 if $lx < 0 or $lx > $MAX_X
or $ly < 0 or $ly > $MAX_Y;
# may instead build up a string to print to terminal
my $ch = $map[$ly][$lx];
plot($lx, $ly, $ch);
# abort the line if FOV is blocked
return -1 if $ch eq '#';
}, $x, $y, $radius
);
# or instead using swing_circle
raycast(
\&swing_circle, sub {
my ($lx, $ly) = @_;
return -1 if $lx < 0 or $lx > $MAX_X
or $ly < 0 or $ly > $MAX_Y;
my $ch = $map[$ly][$lx];
plot($lx, $ly, $ch);
return -1 if $ch eq '#';
}, $x, $y, $radius, deg2rad(5) # different arguments!
);
The plot routine should cache whether something has been printed to the given cell to avoid repeated terminal or display updates.
BUGS
or patches might best be applied towards
https://github.com/thrig/Game-RaycastFOV
SEE ALSO
https://github.com/thrig/ministry-of-silly-vaults/
There are other FOV algorithms and implementations to be found on the Internet.
AUTHOR
thrig - Jeremy Mates (cpan:JMATES) <jeremy.mates at gmail.com>
COPYRIGHT AND LICENSE
This software is Copyright (c) 2020 by Jeremy Mates.
This is free software, licensed under:
The (three-clause) BSD License