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.
    }
Exemple #5
0
 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);
     }
 }