Game::RaycastFOV - raycast field-of-view and related routines
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);
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.
.#.## .##.##### # #.##..##... #. .##.#.##.#... #. #####..#.#### # #. .#.#.#.###.##.. #.#.## ####....#.##... #....# ##...#.@#....## #.@# #..#.###....#.# ###.. .##.#####..#... #.. .##...####.## ##.# ....#.###.#.. . ###.###.#.. .######.# ....#
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.
pairwise
-1
Like bypair but does not include code to abort processing the list. Since v1.01.
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.
%circle_points
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.
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.
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.
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?
Performs a shadow cast FOV calculation of the given radius around the point x, y. The blockcb is called with nx, ny and should determine whether that coordinate is blocked on the level map. The litcb is also called with coordinates and should indicate that that cell is visible.
The radius callback is given the deltax, deltay, and radius and should return true if the deltas are within the radious. This allows for different FOV shapes.
The callbacks may be called with points outside of the level map. The radius callback delta values may be negative so may need to be run through abs or ** 2 to determine the distance.
abs
** 2
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.
See also the eg/ directory of this module's distribution.
eg/
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.
or patches might best be applied towards
https://github.com/thrig/Game-RaycastFOV
Game::Xomb uses a modified version of this module's raycast code to provide FOV.
NetHack::FOV
https://github.com/thrig/ministry-of-silly-vaults/
There are other FOV algorithms and implementations to be found on the Internet.
thrig - Jeremy Mates (cpan:JMATES) <jeremy.mates at gmail.com>
<jeremy.mates at gmail.com>
This software is Copyright (c) 2020 by Jeremy Mates.
This is free software, licensed under:
The (three-clause) BSD License
To install Game::RaycastFOV, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Game::RaycastFOV
CPAN shell
perl -MCPAN -e shell install Game::RaycastFOV
For more information on module installation, please visit the detailed CPAN module installation guide.