https://github.com/antix-development/flow-field-generator
Single destination Flow-Field Generator for grid based games
https://github.com/antix-development/flow-field-generator
breadth-first-search flow-field flow-field-navigation javascript
Last synced: 3 months ago
JSON representation
Single destination Flow-Field Generator for grid based games
- Host: GitHub
- URL: https://github.com/antix-development/flow-field-generator
- Owner: Antix-Development
- Created: 2022-11-15T08:48:26.000Z (over 2 years ago)
- Default Branch: master
- Last Pushed: 2022-11-15T09:11:56.000Z (over 2 years ago)
- Last Synced: 2025-01-07T21:52:55.210Z (4 months ago)
- Topics: breadth-first-search, flow-field, flow-field-navigation, javascript
- Language: JavaScript
- Homepage:
- Size: 11.7 KB
- Stars: 2
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: readme.md
Awesome Lists containing this project
README
# Flow-Field Generator
## About
The `FlowFieldGenerator` class can be used to create single source 2 dimensional flow-fields for grid based games from one target node in a graph, to all other nodes in the same graph that are deemed "passable".
This type of flow-field is useful in games where many actors are trying to find the shortest path to a single actor (many sources, one destination).
The `FlowFieldGenerator` class is based on the Python code found at [Flow Field Pathfinding for Tower Defense](https://www.redblobgames.com/pathfinding/tower-defense/#diagram-parents), and I would highly recommend reading that article as it contains a very good explanation about Breadth First Searching and how to use the technique to create flow-fields.
There are a number of files in this repository...
- **grid2d.js** - A simple class for creating and manipulating 2 dimensional arrays of numbers. It is used to create a grid which is then passed to the FlowFieldGenerator class to be converted into a graph of nodes suitable for building flow-fields from. This file can be ignored.- **util.js** - Some dirty code to display the graph and allow some basic interaction with it. This file can also be ignored.
- **index.html** - Contains an interactive demo of the FlowFieldGenerator class.
- **test.html** - A small performance test for a graph 1000 x 1000 in size. Dumps results to the console, so open your dev tools.
- **node.js** - A sparse graph node class.
- **flowfieldgenerator.js** - The `FlowFieldGenerator` class. It should be fairly easy to follow and modify for your own applications.
Please open an issue or contact me ([email protected]) if you have questions or suggestions on how to improve the performance of the `FlowFieldGenerator` class.
## Properties
The `FlowFieldGenerator` class encapsulates the following properties:### `graph`
*{[node] A 2 dimensional array of nodes*### `passableFilter`
*{function} Called by `isPassable` to determine if a graph node is passable.*### `reached`
*{number} Incremental counter used to identify graph nodes that have been reached during queries.*### `w`
*Width of the graph.*### `h`
*{number} Height of the graph.*### `deltas`
*{[array]} an array offsets used when determining a graph nodes adjacent neighbors.*
## Methods
The `FlowFieldGenerator` class encapsulates the following methods:
### `graphFromArray(arr, convert = false)`
*Create graph from the given 2 dimensional array.*@param {[number]} arr
@convert {boolean} convert
returns {[node]} graph
If a an array of values is passed to `graphFromArray()`, you can also specify whether or not you want to convert the passed array into an array of nodes (mutated). By default, `graphFromArray()` will remain unchanged (not mutated).
### `buildFlowField(x, y)`
*Build (or rebuild) the flow-field wherein all passable nodes point towards the target node at the given coordinates.*@param {number} x
@param {number} y
The target node located at the given x,y coordinates `distance` property will be set to 0. All other nodes in the graph that are "reachable" will have their `distance` property set to their distance from the target node.
### `buildPathToTarget(x, y, trim = false)`
*Build a path of nodes from the node at the given coordinates to the target node.*@param {number} x
@param {number} y
@param {boolean} trim
@returns {[node]} path
If the node at the given x, y coordinates was able to be reached during the latest query, an array of nodes will be created (and returned) by back-tracking through its parent node property until it arrives at the target node.
If you choose to trim the path, it will not contain the nodes containing the source or destination coordinates.
### `enableDiagonalMovement(state = true)`
*Enable or disable the use of diagonal movement according to the given state.*@param {boolean} state
New instances of the `FlowFieldGenerator` class will have diagonal movement disabled.
NOTE: If the given state is different than the current state, then the graph will be rebuilt.
### `setPassableFilter(f)`
*Set passible filter, called by `isPassable()`*@param {function} f
You can also set the passable filter by directly poking the `FlowFieldGenerator` instances `passableFilter` property.
### `isPassable(x, y)`
*Determine if the node at the given coordinates is passable.*@param {number} x
@param {number} y
@returns {boolean} passable
calls the current `passableFilter` function, passing it the graph node at the given x, y coordinates.
### `defaultPassableFilter(node)`
*Default filter called by `isPassable()`, determines whether the given node is passable*@param {node} node
@returns {boolean}The default function to determine if a node is deemed to be passable is
```
defaultPassableFilter(node) {
return node.value === 0;
}
```
### `getGraph()`
*Get graph (a 2 dimensional array of node).*@returns {[node]} graph
You can also access graph by peeking the `FlowFieldGenerator` instances `graph` property.
### `buildGraph()`
*Connect nodes in the graph by creating neighboring node lists for every node contained within it.*
### `buildNeighborsFor(node)`
*Build neighbors list for the given node.*@param {node} node
### `disconnect(node)`
*Disconnect the given node from the graph.*@param {node} node
### `reconnect(node)`
*Reconnect a previously disconnected node to the graph.*@param {node} node
## Usage
Using the `FlowFieldGenerator` class in your own projects should be fairly straightforward.
```
// You will need to include the following
// two scripts in your code..
// Create a 2d grid 20 wide and 10 high
let grid = [];
for (let y = 0; y < 10; y++) {
let row = [];
for (let x = 0; x < 20; x++) {
row.push(0);
}
grid.push(row);
}// Create a new instance of the class
const ffg = new FlowFieldGenerator();// Set the filter called when `isPassable()` is
// called by the class instance
ffg.setPassableFilter((v) => (v === 0));// Enable diagonal movement
ffg.enableDiagonalMovement(true);// Create the graph from our 2d array of values
graph = ffg.graphFromArray(grid);// Create a flow-field with all empty nodes
// pathing to 10, 5
ffg.buildFlowField(10, 5);// Build a path from 16, 8 to 10, 5
let path = ffg.buildPathToTarget(16, 8);// Output the result
console.log(path);
```