Flow Fields Navigation
Path calculations are split in two main steps:
- Calculate the path using portals data and A*;
- Calculate the smoothed path
A* Path
Every portal on the map is a node on the A* graph. Possible paths between two portals are represented as edges in the A* graph. When a path is requested, two extra nodes (start position and destination) are added to the graph with edges connected to portals which are reachable from the new nodes within the flow field controller. The A* result is a sequence of portals which the pathfinder have to cross to reach its destination.
Smooth Path
The smooth path is calculated per agent. The goal is to remove unnecessary corners on the path created by the A* navigation and directions in the flow fields (only 8 possible directions).
Only the first corner of the smooth path is calculated in order improve the performance of the pathfinders which might change their destination before reaching it, meaning that the general path will be gradually smoothed, and it progresses as soon as a smooth corner is reached.
Path Caching
Similar paths (paths with the same start/end positions, controllers and same closest portal within the controller) are cached and reused by the pathfinders.
Portal Fields
Flows towards each portal are precalculated and reused when needed. Recalculation happens every time a tile's cost changes in the controller or a portal's size/position changes (when cost changes in the neighboring controller on the border).
Movement
Units movement is not part of this addon. The FlowFieldPathfinder
provides the direction in which the unit should move to follow calculated path.
There are two movement implementations included in this sample:
- Simple movement setting the position directly in the Transform2D component - see
MovementBasic
; - More advanced movement using the PhysicsBody2D component - see
MovementAdvanced
.
MovementBasic Example
C#
var pathfinder = frame.GetPointer<FlowFieldPathfinder>(entity);
if (pathfinder->HasDestination == false || pathfinder->AtDestination == true)
return;
var direction = pathfinder->GetDirection(frame, entity);
if (direction.Valid == false)
return;
var transform = frame.GetPointer<Transform2D>(entity);
transform->Position += direction.Direction * Speed * frame.DeltaTime;
MovementAdvanced Example
C#
var pathfinder = frame.GetPointer<FlowFieldPathfinder>(entity);
var physicsBody = frame.GetPointer<PhysicsBody2D>(entity);
if (pathfinder->HasDestination == false || pathfinder->AtDestination == true)
{
physicsBody->Velocity = default;
return;
}
var direction = pathfinder->GetRotationDirection(frame, entity);
if (direction.Valid == false)
{
physicsBody->Velocity = default;
return;
}
physicsBody->Velocity = FPVector2.Rotate(FPVector2.Up, direction.Rotation) * Speed;
physicsBody->WakeUp();