В моей пошаговой игре я пытаюсь заставить вражеские юниты найти путь к области, в которой они могут атаковать базу игроков. Когда я заканчиваю очередь на вызов DoActions()
, Unity съедает мой процессор и перестает отвечать, поэтому я предполагаю, что написал бесконечное l oop. Однако я не могу найти причину проблемы в сценариях, которые я использовал для этого.
public class Pathfinding : MonoBehaviour
{
public static List<Vector2> FindPath(Vector2 start, Vector2 goal, Unit unit)
{
PriorityQueue<double, Vector2> frontier = new PriorityQueue<double, Vector2>();
Vector2 current;
frontier.Enqueue(0, start);
Dictionary<Vector2, Vector2> from = new Dictionary<Vector2, Vector2>();
Dictionary<Vector2, double> cost_so_far = new Dictionary<Vector2, double>();
cost_so_far[start] = 0;
while (!frontier.IsEmpty)
{
current = frontier.Dequeue().Value;
if (current == goal)
break;
foreach(Vector2 next in GetNeighborsCoor(current))
{
if (next.x < Tile.tilemap.GetLength(0) && next.y < Tile.tilemap.GetLength(1) && Building.FindBuildingAtCoor(next) == null)
{
double cost = cost_so_far[current] + GetMoveCost(next, unit);
if (!from.ContainsKey(next))
{
cost_so_far[next] = cost;
frontier.Enqueue(cost, next);
from[next] = current;
}
}
}
}
current = goal;
List<Vector2> path = new List<Vector2>();
while(current != start)
{
path.Add(current);
current = from[current];
}
path.Add(start);
path.Reverse();
return path;
}
public static List<Vector2> FindPathToArea(Vector2 start, List<Vector2> area, Unit unit)
{
PriorityQueue<double, Vector2> frontier = new PriorityQueue<double, Vector2>();
Vector2 current;
Vector2 goal = Vector2.negativeInfinity;
frontier.Enqueue(0, start);
Dictionary<Vector2, Vector2> from = new Dictionary<Vector2, Vector2>();
Dictionary<Vector2, double> cost_so_far = new Dictionary<Vector2, double>();
cost_so_far[start] = 0;
while (!frontier.IsEmpty)
{
current = frontier.Dequeue().Value;
if (area.Contains(current))
{
goal = current;
break;
}
foreach(Vector2 next in GetNeighborsCoor(current))
{
if (next.x < Tile.tilemap.GetLength(0) && next.y < Tile.tilemap.GetLength(1) && Building.FindBuildingAtCoor(next) == null)
{
double cost = cost_so_far[current] + GetMoveCost(next, unit);
if (!from.ContainsKey(next))
{
cost_so_far[next] = cost;
frontier.Enqueue(cost, next);
from[next] = current;
}
}
}
}
current = goal;
List<Vector2> path = new List<Vector2>();
while(current != start)
{
path.Add(current);
current = from[current];
}
path.Add(start);
path.Reverse();
return path;
}
public static List<Vector2> GetNeighborsCoor(Vector2 coor)
{
List<Vector2> neighborsCoor = new List<Vector2>();
neighborsCoor.Add(new Vector2(coor.x + 1, coor.y));
neighborsCoor.Add(new Vector2(coor.x - 1, coor.y));
neighborsCoor.Add(new Vector2(coor.x, coor.y + 1));
neighborsCoor.Add(new Vector2(coor.x, coor.y - 1));
return neighborsCoor;
}
public static double GetMoveCost(Vector2 coor, Unit unit)
{
double cost = 0;
Building building = Building.FindBuildingAtCoor(coor);
if(building == null)
{
cost = unit.apt / unit.speed;
}
else
{
cost = building.HP / unit.atk;
}
return cost;
}
public static Vector3 TransformCoor(Vector2 coor)
{
Vector3 position = Tile.tilemap[(int)coor.x, (int)coor.y].gameObject.transform.position;
position.x = -1;
return position;
}
public static List<Vector2> FindAreaToAttackFrom(Vector2 target, double range)
{
List<Vector2> area = new List<Vector2>();
for (int x = 0; x < Tile.tilemap.GetLength(0); x++)
{
for (int y = 0; y < Tile.tilemap.GetLength(1); y++)
{
Vector2 current = new Vector2(x, y);
if (Vector2.Distance(target, current) <= range)
{
if(Building.FindBuildingAtCoor(current) == null && Unit.FindUnitAtCoor(current))
{
if(EnemyAI.CheckLineOfSight(current, target, 1, out List<Building> obstacles))
{
area.Add(current);
}
}
}
}
}
return area;
}
}
public class EnemyAI : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
Turn.OnTurnEnded += DoActions;
}
public void DoActions()
{
Building playerBase = Building.GetBaseOfOwner(0);
foreach (Unit unit in Unit.myUnits)
{
if(unit.owner != 0 && unit.action != 0)
{
if(unit.range >= Vector2.Distance(playerBase.coor, unit.coor))
{
bool clearLOS = CheckLineOfSight(unit.coor, playerBase.coor, 1, out List<Building> obstacles);
if (clearLOS)
{
playerBase.HP -= unit.atk * unit.action;
}
else
{
obstacles[0].HP -= unit.atk * unit.action;
}
unit.action = 0;
}
else
{
while(unit.action != 0)
{
WalkPath(Pathfinding.FindPathToArea(unit.coor, Pathfinding.FindAreaToAttackFrom(playerBase.coor, unit.range), unit), unit);
}
}
}
}
}
public void WalkPath(List<Vector2> path, Unit unit)
{
for (int step = 0; step <= unit.speed;)
{
if (Building.FindBuildingAtCoor(path[step]) == null)
{
if (step == unit.speed)
{
Unit.MoveUnit(unit, path[step]);
unit.action -= 1;
}
else
{
step++;
}
}
else
{
Building building = Building.FindBuildingAtCoor(path[step]);
if (CheckLineOfSight(unit.coor, path[step], 1, out List<Building> obstacles))
{
building.HP -= unit.atk * unit.action;
unit.action = 0;
break;
}
else
{
Unit.MoveUnit(unit, path[step - 1]);
unit.action -= 1;
break;
}
}
}
}
public static bool CheckLineOfSight(Vector2 from, Vector2 target, int actor, out List<Building> obstacles)
{
bool clear = true;
obstacles = new List<Building>();
RaycastHit[] hits = Physics.RaycastAll(Pathfinding.TransformCoor(from), Pathfinding.TransformCoor(target) - Pathfinding.TransformCoor(from));
if (hits != null)
{
foreach (RaycastHit hit in hits)
{
if (hit.transform.GetComponent<Building>() != null)
{
if ((hit.transform != Building.FindBuildingAtCoor(from).gameObject) && hit.transform != Building.FindBuildingAtCoor(target).gameObject)
{
Building building = hit.transform.GetComponent<Building>();
if (building.owner != actor)
{
obstacles.Add(building);
clear = false;
}
}
}
}
}
return clear;
}
}