项目作者: MauroVanHoutte

项目描述 :
Ahhh yes vectors
高级语言: C
项目地址: git://github.com/MauroVanHoutte/FlowField.git
创建时间: 2021-01-12T11:07:24Z
项目社区:https://github.com/MauroVanHoutte/FlowField

开源协议:

下载


FlowField pathfinding

A flow field is a way of pathfinding that works on a grid, for every cell in the grid a vector is calculated, any agent traversing the grid can then use the vector assigned to the cell the agent is currently in to take the shortest route to the destination.

Flowfield

Design

The flow field is designed to work on the grid and graph implementations of the Elite framework, the box2d library is used as physics engine and the interface is made using ImGui.
The algorithm itself runs in 2 steps, the first step is calculating the cost from the destination cell to each other cell in the grid, the second step is creating a unit vector for each cell pointing towards the cells neighbour with the cheapest cost.
To let the agents utilize the flow field, the cell the agent is in is calculated from the agents position, then the agent steers towards the direction of the cell he is in

Implementation

Calculation of each cells cost in pseudo code:

  1. vector<Node> openlist // vector that keeps track of nodes that have to be calculated
  2. list<Node> closedlist // list that keeps track of nodes that have been calculated
  3. openlist.push_back(destinationNode) // start from the destination and expands outwards
  4. closedlist.push_back(destinationNode)
  5. while (!openlist.empty)
  6. node = FindLowestCostNode(openlist) // lowest cost always has to be calculated first, especially important when connection costs are variable
  7. openlist.remove(node)
  8. cellcost[node.Index] = node.cost // cellcost is a vector that is a member of the flowfield class
  9. for (connection : node.connections)
  10. newNode = connection.GetTo // get the connected node
  11. newNode.cost = node.cost + connection.cost
  12. if (!find(closedlist, newNode)) //nodes that have been calculated already wont get added again
  13. closedlist.push_back(newNode)
  14. openlist.push_back(newNode)

Calculation of each cells vector:

  1. for (node : graph.GetAllNodes)
  2. cheapestNode = FindCheapestNeighbour(node)
  3. flowfield[node.Index] = Normalized(cheapestNode.worldPos - node.worldPos) // flowfield contains the unit vectors for each cell
  4. flowfield[endNode.Index] = ZeroVector

The brown cells here have a higher cost

Brown tiles have a higher cost

While implementing the flowfield i had 2 other systems in mind that use the flowfield as a foundation.

1 : Teleporter

Teleporters

The implementation exists of a struct holding the node indices where the 2 teleporters are located and a variable that keeps track of which teleporter is closer to the destination.
To adjust the flowfield to this, during the calculation of the cell costs and before adding a node’s connections to the openlist, if the node is one of the teleporters add the
other teleporter to the openlist with the same cost.
In Pseudo code that looks like this:

  1. ...
  2. node = FindLowestCostNode(openlist)
  3. openlist.remove(node)
  4. cellcost[node.Index] = node.cost
  5. if (node.Index == teleporter)
  6. teleporterNode = teleporter.other // other teleporter connecting to this one
  7. openlist.pushback(teleporterNode)
  8. closedlist.pushback(teleporterNode)
  9. for (connection : node.connections)
  10. newNode = connection.GetTo
  11. newNode.cost = node.cost + connection.cost
  12. ...

2 : Traffic

The problem

No Measure Against Traffic

The solution

Anti Traffic

To prevent all agents from taking the same narrow path when the vectors of each cell are calculated the cell cost is increased for every agent currently in the cell,
this implementation does have an impact on performance as the vectors of the cell now need to be calculated every frame

Future work

Adapting to 3d could have some interesting use cases