Flow Fields
The video demonstrates my latest AI programming assignment. When the game starts and the player uses WASD to move, the game will generate a grid and proceed to calculate a cost field, integration field, and flow field to find the shortest distance to the tile the player is on. The flow field is recalculated every time the player moves. When the 1 key is pressed, a number of units will spawn and follow the flow field to reach the player, who must try and avoid them.
The three main components that make up the pathfinding are:
-
The Cost Field
-
The data that represents the cost it takes to for a unit to move across a specific tile
-
-
The Integration Field
-
The data that calculates the cost it would take to get to a destination from every tile in the game
-
-
The Flow Field
-
Where each tile checks its neighbors and finds the direction to the one with the lowest cost, giving us the shortest path
-
The code creates each field one after another and saves the data into the correct node in the list of nodes, where the data can be called upon in the next field.
THE GRID AND COST FIELD
The first step is to split the scene into a grid and create a list of nodes, where each node corresponds to and holds data for a single tile in the grid. Each tile is given a cost value representing the cost to cross that tile specifically, and a best cost value that holds the lowest cost from that tile to the destination (Starts at 255 on creation).
Next, we create the cost field. I placed rough terrain and walls strewn across the scene and placed each on a specific layer in Unity so that I could use layer masking to get the cost. Using layer masking, I was able to check to see if any hitboxes from the correct layers overlapped with the bounds of a tile and could calculate the cost value of that tile depending on what layer the overlapping terrain was on. If the terrain was on the rough terrain layer (the white terrain) the cost would go up from a default 1 to a 4, and if the terrain was on the impassible layer (the black terrain), the cost would max out at 255.
THE INTEGRATION FIELD
The second step is to use the cost field created so that each tile can calculate the total cost it would take to get from that specific tile to the targeted destination. It does this by starting at the destination node with the cost of 0, adding it to a list of nodes to check. Then, by using a function to get each of the tiles surrounding the current one, it checks each of the neighboring nodes, calculating to see if the best cost from the current tile + the neighbor's cost is better than the neighbors current best cost. Because all the nodes start with a best cost of 255, each neighbor will have the new best cost calculated and then be added to the list to check. It then repeats the process with the next node in the list, now skipping any neighbor node with a lower best cost, as those have already been calculated. At the end, when every tile has been checked and the list is empty, the loop stops and every node has its total best cost.
THE FLOW FIELD
The flow field itself is then fairly simple to implement. It works in a loop similar to the integration field, but instead of having each node check and calculate the cost from it's neighbors, and then adding them to a list to check, the loop just goes through each node one by one, adding each neighbor of the current cell to a short list, and checking each one. It finds the neighbor with the lowest best cost and sets the current node's best direction variable to the direction facing the correct neighbor, represented as a simple direction vector. It does this for each node in the grid until every single one has calculated the best direction.
PUTTING IT TOGETHER WITH THE UNITS
After all that, I instantiate an arrow sprite or an X sprite on each node depending on the cost, and then rotate the arrow sprite to face the correct direction. This step is optional and only serves to help visualize the flow field for the viewer. To make the units follow the field is also quite simple. I spawn the units randomly and each one moves individually. They check the tile underneath them by using their world position and a function from the grid to find the correct node, and use the best direction vector saved in that node to set the rigidbody's current velocity. I move the player using a simple movement code with WASD and update the destination node to be the one under the player every time I move. This makes it so no matter where I go, the units can follow the flow field and chase the player down.
I am personally quite pleased with this functionality, and I cannot wait to apply this pathfinding method to other games in the future.