public void GenerateFlowField(FlowFieldCell destinationCell) { ResetGrid(); GenerateIntegrationField(destinationCell); foreach (FlowFieldCell currentCell in _grid) { //skip impassable and endgoal //if (currentCell.Cost == byte.MaxValue || currentCell.BestCost == 0) //bestcost is a better option since a cost of 0 might be possible? // continue; //make units go 'back' from impassable terrain FlowFieldCell closestNeighbour = null; int bestCost = currentCell.BestCost; foreach (FlowFieldCell neighbour in GetNeighboursFromGridIndex(currentCell.GridIndex)) { if (neighbour.BestCost < bestCost) { closestNeighbour = neighbour; bestCost = neighbour.BestCost; } } if (closestNeighbour != null) { if (currentCell.Cost == byte.MaxValue) { currentCell.Direction = GetDirectionFromGridIndex(currentCell.GridIndex - destinationCell.GridIndex); //destination to current (pointing away) } else { currentCell.Direction = GetDirectionFromGridIndex(closestNeighbour.GridIndex - currentCell.GridIndex); //current to closest } } } //deprecated //foreach (FlowFieldCell currentCell in _grid) //{ // FlowFieldCell closestNeighbour = null; // float closestToGoal = float.MaxValue; // foreach (FlowFieldCell neighbour in GetNeighboursFromWorldPos(currentCell.WorldPos)) // { // float neighbourTotalCost = Vector3.Distance(destinationCell.WorldPos, neighbour.WorldPos) + neighbour.Cost; // if (closestToGoal > neighbourTotalCost) // { // closestNeighbour = neighbour; // closestToGoal = neighbourTotalCost; // } // } // // Vector3 vel = (closestNeighbour.WorldPos - currentCell.WorldPos).normalized; // currentCell.Direction = new Vector2(vel.x, vel.z); //} }
public void SetEndGoal(FlowFieldCell endGoalCell) { _endGoalIdx = endGoalCell.GridIndex; _grid.GenerateFlowField(endGoalCell); if (_endGoalIndicator) { _endGoalIndicator.transform.position = new Vector3(_endGoalIdx.x * _grid.CellSize, 0, _endGoalIdx.y * _grid.CellSize); } }
private void CreateGrid() { _grid = new FlowFieldCell[_rows * _cols]; for (int y = 0; y < _cols; ++y) { for (int x = 0; x < _rows; ++x) { _grid[x + y * _cols] = new FlowFieldCell(new Vector3(x * _cellSize, 0, y * _cellSize), new Vector2Int(x, y)); } } }
private void GenerateIntegrationField(FlowFieldCell destinationCell) { //overwrite/assign new destination cell _destinationCell = destinationCell; //save cost from destinationcell so that it cannot get lost _formerCost = _destinationCell.Cost; //since we set it to 0 here _destinationCell.Cost = 0; //bestcost can be ignored anyway _destinationCell.BestCost = 0; Queue <FlowFieldCell> openList = new Queue <FlowFieldCell>(); openList.Enqueue(_destinationCell); while (openList.Count > 0) { FlowFieldCell currentCell = openList.Dequeue(); //get neighbours of currentCell foreach (FlowFieldCell neighbour in GetNeighboursFromGridIndex(currentCell.GridIndex)) { //skip impassable cells if (neighbour.Cost == byte.MaxValue) { continue; } //if normal cost + totalcost is smaller than neighbour's totalcost (either ushort.maxvalue else it probably has already been updated), update it if (neighbour.Cost + currentCell.BestCost < neighbour.BestCost) { //assign total cost to current neighbour neighbour.BestCost = (ushort)(neighbour.Cost + currentCell.BestCost); openList.Enqueue(neighbour); //add neighbour to check all of its neighbours } } } //Source: https://leifnode.com/2013/12/flow-field-pathfinding/ //The algorithm starts by resetting the value of all cells to a large value(I use 65535). //The goal node then gets its total path cost set to zero and gets added to the open list. //From this point the goal node is treated like a normal node. //The current node is made equal to the node at the beginning of the open list and gets removed from the list. //All of the current node’s neighbors get their total cost set to the current node’s cost plus their cost read from the cost field then they get added to the back of the open list. //This happens if and only if the new calculated cost is lower than the old cost.If the neighbor has a cost of 255 then it gets ignored completely. //This algorithm continues until the open list is empty. }
private void FixedUpdate() { foreach (GameObject unit in _units) { FlowFieldCell cell = _flowfieldController.Grid.GetCellFromWorldPos(unit.transform.position); if (cell == null) { continue; } Vector3 newVel = new Vector3(cell.Direction.x, 0, cell.Direction.y); //unit.GetComponent<Rigidbody>().MovePosition(unit.transform.position + newVel * unit.GetComponent<FlowFieldUnit>().Speed * Time.deltaTime); unit.GetComponent <Rigidbody>().AddForce(newVel * unit.GetComponent <FlowFieldUnit>().Speed); } }