thrig
and 1 contributors

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?

shadowcast x y radius blockcb litcb radiuscb

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.

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

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.

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