public virtual List <WayNode> GetSurround(Map map, WayNode center) { var result = new List <WayNode>(); var centerPos = center.Current; var lPos = centerPos + new int2(-1, 0); var rPos = centerPos + new int2(1, 0); var uPos = centerPos + new int2(0, 1); var dPos = centerPos + new int2(0, -1); if (map.Avaliable(lPos)) { result.Add(this[lPos]); } if (map.Avaliable(rPos)) { result.Add(this[lPos]); } if (map.Avaliable(uPos)) { result.Add(this[uPos]); } if (map.Avaliable(dPos)) { result.Add(this[dPos]); } return(result); }
public override bool StartFindWay(Map map, out WayNode path) { _openList.Clear(); _closeList.Clear(); _map = map; var startNode = map.FindWayData.Entrance; var endNode = map.FindWayData.Destination; _startPos = startNode.Pos; _endPos = endNode.Pos; //起点终点不可达 if (!startNode.Walkable || !endNode.Walkable) { return(base.StartFindWay(map, out path)); } var node = new AStarWayNode(_startPos, new int2(-1, -1)); node.CalcF(map); _openList.Add(node); while (_openList.Count > 0) { //获得F最小的点 var minFNode = GetMinF(); //从列表中移除 _openList.Remove(minFNode); //加入关闭列表 _closeList.Add(minFNode); var surroundPoints = GetSurround(map, minFNode); foreach (var surroundPoint in surroundPoints) { var surround = (AStarWayNode)surroundPoint; //在关闭列表中 丢弃 if (!_closeList.Contains(surround) && !_openList.Contains(surround)) { surround.CalcF(map); surround.Parent = minFNode.Current; if (surround.Current.Equals(map.FindWayData.Destination.Pos)) { path = surround; return(true); } else { _openList.Add(surround); } } } } return(base.StartFindWay(map, out path)); }
private void ShowPath(WayNode path) { var end = new int2(-1, -1); while (!path.Parent.Equals(Map.FindWayData.Entrance.Pos) && !path.Current.Equals(end)) { path = FindWayOp[path.Parent]; var floor = Map.FindWayData[path.Current]; floor.SetFloor(FloorType.Way); } }
public override List <WayNode> GetSurround(Map map, WayNode center) { var result = new List <WayNode>(); var centerPos = center.Current; var lPos = centerPos + new int2(-1, 0); var rPos = centerPos + new int2(1, 0); var uPos = centerPos + new int2(0, 1); var dPos = centerPos + new int2(0, -1); if (map.Avaliable(lPos) && map.FindWayData[lPos].Walkable) { if (!WayNodes.ContainsKey(lPos)) { WayNodes[lPos] = new AStarWayNode(lPos, new int2(-1, -1)); } result.Add(this[lPos]); } if (map.Avaliable(rPos) && map.FindWayData[rPos].Walkable) { if (!WayNodes.ContainsKey(rPos)) { WayNodes[rPos] = new AStarWayNode(rPos, new int2(-1, -1)); } result.Add(this[rPos]); } if (map.Avaliable(uPos) && map.FindWayData[uPos].Walkable) { if (!WayNodes.ContainsKey(uPos)) { WayNodes[uPos] = new AStarWayNode(uPos, new int2(-1, -1)); } result.Add(this[uPos]); } if (map.Avaliable(dPos) && map.FindWayData[dPos].Walkable) { if (!WayNodes.ContainsKey(dPos)) { WayNodes[dPos] = new AStarWayNode(dPos, new int2(-1, -1)); } result.Add(this[dPos]); } return(result); }
private (WayNode, bool) CheckPosNode(Floor parentFloor, Floor checkedFloor) { if (_map.Avaliable(checkedFloor.Pos)) { if (checkedFloor.Walkable) { var wayNode = new WayNode(checkedFloor.Pos, parentFloor.Pos); var isReach = checkedFloor.Pos.Equals(_endPos); return(wayNode, isReach); } } return(null, false); }
/// <summary> /// Loads pathData & create path under editor mode. /// </summary> /// <param name="data">Data.</param> public void LoadPathInEditor(PathData data) { connection = new Connection(data.connection.x, data.connection.y); gameObject.name = string.Format("Path {0}", data.connection.ToString()); rt.anchoredPosition = Average(data.points); color = data.c; for (int i = 0; i < data.points.Length; i++) { GameObject go = Instantiate(prefab) as GameObject; go.name = gameObject.name + " Point." + i; go.transform.SetParent(transform.parent, false); WayNode node = go.AddComponent <WayNode>(); node.c = (color != null)?color:Color.yellow; node.p = data.points[i]; node.Match(); nodes.Add(node); } }
/**********************************************************************************/ // строим путь из from в to // !!!!!!! используется ТОЛЬКО при построении системы указателей !!!!!!! // /**********************************************************************************/ private void BuildPath(Point from, Point to) { if (from.IsSamePoint(to)) { Debug.LogError("We cann't build path from 1 same point"); return; } WayNode[,] bluprintOfRoad = new WayNode[m_xSizeOfMap, m_ySizeOfMap]; for (int x = 0; x < m_xSizeOfMap; x++) { for (int y = 0; y < m_ySizeOfMap; y++) { bluprintOfRoad[x, y] = new WayNode(); } } PrepareBluprintOfPath(bluprintOfRoad, from, to, m_blockConnectionData, m_sizeOfBlock * 4); // проходим по результирующему пути задом наперёд и запоминаем направление движения // для системы указателей важно только направление первого шага в пути Base.DIREC nextDirection = Base.DIREC.NO_DIRECTION; Point toProcess = new Point(to); do { WayNode nodeToProcess = bluprintOfRoad[toProcess.x, toProcess.y]; toProcess.ShiftPoint(nodeToProcess.previusRoadDirection); nextDirection = Base.InvertDirection(nodeToProcess.previusRoadDirection); } while (!toProcess.IsSamePoint(from)); // по окончанию обработки nextDirection содержит направление первого шага по достижению точки "to" из "from" // его и сохраняем в таблице int keyToMark = GetSingKey(from.x, from.y); // ключик блока "from" int keyToConnect = GetSingKey(to.x, to.y); // ключик блока "to" m_singMap[keyToMark, keyToConnect] = nextDirection; }
/**********************************************************************************/ // просчитываем конкретную точку построения пути // проверяем её стоимость и на оснеовании проверки вносим коррективы в карту маршрутов // /**********************************************************************************/ void CalculatePoint(WayNode[,] bluprintOfRoadMap, Point currentPoint, Point to, Base.DIREC prevPointDir, int currentWayCost, ref int nodeClosestDist, LinkedList <Point> currentPontsToProcess, LinkedList <Point> nextIterationPontsToProcess) { int newX = currentPoint.x; int newY = currentPoint.y; // выбираем координаты новой точки в зависимости от положения предыдущей switch (prevPointDir) { case Base.DIREC.DOWN: newY++; break; case Base.DIREC.UP: newY--; break; case Base.DIREC.LEFT: newX++; break; case Base.DIREC.RIGHT: newX--; break; } // если координата удовлетворяет ... if (newX < 0 || newX >= bluprintOfRoadMap.GetLength(0) || newY < 0 || newY >= bluprintOfRoadMap.GetLength(1)) { return; } WayNode wayNodeToCheck = bluprintOfRoadMap[newX, newY]; // формирование цены прохода происходит из стоимости предыдущих клеток + стоимость прохода по клетке + загруженность пути int possibleWayCost = currentWayCost + wayNodeToCheck.cellCost + m_trafficData[newX, newY]; Point pointToCheck = null; if (wayNodeToCheck.wayCost > possibleWayCost) { wayNodeToCheck.wayCost = possibleWayCost; wayNodeToCheck.previusRoadDirection = prevPointDir; pointToCheck = new Point(newX, newY); } else if (wayNodeToCheck.wayCost == possibleWayCost) { // в случае, если стоимости равны - в половине случаев переключаемся на новый путь для разнообразия if (Random.Range(0, 100) >= 50) { wayNodeToCheck.wayCost = possibleWayCost; wayNodeToCheck.previusRoadDirection = prevPointDir; } } // если мы нашли точку для просчёта - определяемся когда будем её просчитывать // если "квадратное" растояние меньше или равно самого близкого к целевой точке, то будем просчитывать её в первую очередь if (pointToCheck != null) { int dist = Mathf.Abs(pointToCheck.x - to.x) + Mathf.Abs(pointToCheck.y - to.y); if (dist == 0) { nodeClosestDist = 0; return; } else if (dist <= nodeClosestDist) { currentPontsToProcess.AddLast(pointToCheck); nodeClosestDist = dist; } else { nextIterationPontsToProcess.AddLast(pointToCheck); } } }
/**********************************************************************************/ // строим путь из точки from в точку to // результат записываем в bluprintOfPath // // @ bluprintOfPath - чертёж дороги (пустой, если не хотим накладывать какие-то ограничения и модификации поведения при поиске пути) // @ from - точка откуда строят путь // @ to - точка куда строят путь // @ map - карта с ограничением движений // @ maxCost - максимальная кол-во итераций при расчёте пути // /**********************************************************************************/ void PrepareBluprintOfPath(WayNode[,] bluprintOfPath, Point from, Point to, BlockDescriptorImitation[,] map, int maxCost) { LinkedList <Point> currentPontsToProcess = new LinkedList <Point>(); LinkedList <Point> nextIterationPontsToProcess = new LinkedList <Point>(); // устанавливаем стартовые параметры алгоритма - начальную точку и её стоимость пути bluprintOfPath[from.x, from.y].wayCost = 0; currentPontsToProcess.AddFirst(from); int possibleClosestDist = (Mathf.Abs(from.x - to.x) + Mathf.Abs(from.y - to.y)) * WayNode.GROUND_COST; int nodeClosestDist = possibleClosestDist; bool buildingComplite = false; int maxSteps = maxCost; while (!buildingComplite && maxSteps > 0) { maxSteps--; while (currentPontsToProcess.Count > 0) { // забираем точку для просчёта и работаем с ней Point currentPoint = currentPontsToProcess.First.Value; currentPontsToProcess.RemoveFirst(); int currentWayCost = bluprintOfPath[currentPoint.x, currentPoint.y].wayCost; // просчитываем верхную точку, если соединение возможно if (map[currentPoint.x, currentPoint.y].RoadConnections[(int)Base.DIREC.UP] != ROAD_CONNECTION_STATUS.BLOCKED) { CalculatePoint(bluprintOfPath, currentPoint, to, Base.DIREC.DOWN, currentWayCost, ref nodeClosestDist, currentPontsToProcess, nextIterationPontsToProcess); } // просчитываем нижнюю точку, если соединение возможно if (map[currentPoint.x, currentPoint.y].RoadConnections[(int)Base.DIREC.DOWN] != ROAD_CONNECTION_STATUS.BLOCKED) { CalculatePoint(bluprintOfPath, currentPoint, to, Base.DIREC.UP, currentWayCost, ref nodeClosestDist, currentPontsToProcess, nextIterationPontsToProcess); } // просчитываем левую точку, если соединение возможно if (map[currentPoint.x, currentPoint.y].RoadConnections[(int)Base.DIREC.LEFT] != ROAD_CONNECTION_STATUS.BLOCKED) { CalculatePoint(bluprintOfPath, currentPoint, to, Base.DIREC.RIGHT, currentWayCost, ref nodeClosestDist, currentPontsToProcess, nextIterationPontsToProcess); } // просчитываем правую точку, если соединение возможно if (map[currentPoint.x, currentPoint.y].RoadConnections[(int)Base.DIREC.RIGHT] != ROAD_CONNECTION_STATUS.BLOCKED) { CalculatePoint(bluprintOfPath, currentPoint, to, Base.DIREC.LEFT, currentWayCost, ref nodeClosestDist, currentPontsToProcess, nextIterationPontsToProcess); } } // принимаем решение о дальнейших итерациях if (to.x < 0 || to.x > bluprintOfPath.GetLength(0) || to.y < 0 || to.y > bluprintOfPath.GetLength(1)) { Debug.LogError("Wrong position"); } WayNode WayNodeFinal = bluprintOfPath[to.x, to.y]; if (WayNodeFinal.wayCost == possibleClosestDist) { buildingComplite = true; } else { // если мы нашли путь (любой оптимальности) и при этом исчерпали возможности для построения маршрута - завершаем проектирование маршрута // так же завершаем поиск пути в случае исчерпания ходов построения пути (только при условии, что у нас есть хоть какой-то путь) if (WayNodeFinal.wayCost != WayNode.UNREACHABLE) { if (maxSteps == 0 || nextIterationPontsToProcess.Count == 0) { buildingComplite = true; } } // в противном случае продолжаем else { // меняем местами коллекции LinkedList <Point> supportPointer = currentPontsToProcess; currentPontsToProcess = nextIterationPontsToProcess; nextIterationPontsToProcess = supportPointer; } } } if (!buildingComplite) { Debug.LogError("We cann't buld the path!!! From: " + from + ". To: " + to); Logger.CreatePathFinderErrorReport(bluprintOfPath, from, to, m_freeSpaceGlobalMap); } }
/**********************************************************************************/ // функция строящая путь между точками и возвращающая коллекцию точек для прохождения // /**********************************************************************************/ LinkedList <Point> BuildSpecificWay(Point from, Point to) { LinkedList <Point> wayPoints = new LinkedList <Point>(); // если точка старта совпадает с точкой конца пути - возвращаем пустую коллекцию if (from.IsSamePoint(to)) { return(wayPoints); } // подготавливаем блюпринт пути // TODO: здоровый, подумать, можно ли оптимизировать расход памяти и времени WayNode[,] bluprintOfRoad = new WayNode[m_xSizeOfMap * m_sizeOfBlock, m_ySizeOfMap *m_sizeOfBlock]; for (int x = 0; x < m_xSizeOfMap * m_sizeOfBlock; x++) { for (int y = 0; y < m_ySizeOfMap * m_sizeOfBlock; y++) { bluprintOfRoad[x, y] = new WayNode(); } } PrepareBluprintOfPath(bluprintOfRoad, from, to, m_connectionGlobalMap, m_sizeOfBlock * 3); // проходим по результирующему пути задом наперёд и формируем список точек пути Point toProcess = new Point(to); int maxSteps = (from - to).GetSimpleLength() * 3; // устанавливаем ограничитель, но не меньше 20 шаговы if (maxSteps < 20) { maxSteps = 20; } do { WayNode nodeToProcess = bluprintOfRoad[toProcess.x, toProcess.y]; wayPoints.AddLast(new Point(toProcess)); // так же увеличиваем загруженность трафика на данном пути m_trafficData[toProcess.x, toProcess.y] += m_trafficIncrCost; toProcess.ShiftPoint(nodeToProcess.previusRoadDirection); maxSteps--; } while (!toProcess.IsSamePoint(from) && maxSteps > 0); if (maxSteps == 0) { if (!toProcess.IsSamePoint(from)) { Logger.CreatePathFinderErrorReport(bluprintOfRoad, from, to, m_freeSpaceGlobalMap); Debug.LogError("ERROR!!! Path constraction was failed!!! from: " + from.ToString() + " to: " + to.ToString()); } } // TODO: дополнительное выравнивание по точке начала пути способствует значительному возростанию точности и аккуратности прохождения маршрута // юнит, не цепляется за посторонние статичные объекты // с другой стороны, это иногда вызывает странное поведение, когда юнит делает короткое движение "туда-обратно" в точку начала маршрута (выравнивается) // а потом разворачивается на 180 градусов и продолжает движение к следующей точке маршрута (возможно решается по средствам регулировки точности при выполнении манёвров) // удаляем последнюю точку, так как она совподает с координатой from // wayPoints.RemoveLast(); return(wayPoints); }
public virtual bool StartFindWay(Map map, out WayNode path) { path = null; return(false); }
public static void CreatePathFinderErrorReport(WayNode[,] bluprintOfPath, Point from, Point to, bool[,] freeSpaceMap) { string fileName = "PathFinderReport_"; string logPath = @"E:\Game Projects\Logs"; string filePath = logPath + @"\" + fileName + s_errorReportIndex.ToString() + DateTime.Now.ToString("MM_dd_yyyy_HH-mm") + ".txt"; if (!File.Exists(filePath)) { // Create a file to write to. using (StreamWriter sw = File.CreateText(filePath)) { sw.WriteLine("Path Finder Error Report"); sw.WriteLine("We tryed to build path from: " + from.ToString() + " to: " + to.ToString()); sw.WriteLine("************************"); } } string headLine = "\t*"; for (int x = 0; x < bluprintOfPath.GetLength(0); x++) { headLine += "\t" + x.ToString() + "#"; } using (StreamWriter sw = File.AppendText(filePath)) { sw.WriteLine(headLine); } for (int y = bluprintOfPath.GetLength(1) - 1; y >= 0; y--) { using (StreamWriter sw = File.AppendText(filePath)) { sw.Write("\t" + y.ToString() + "#"); } string str = ""; for (int x = 0; x < bluprintOfPath.GetLength(0); x++) { if (freeSpaceMap[x, y]) { str += "\t" + "_"; } else { str += "\t" + "X"; } } using (StreamWriter sw = File.AppendText(filePath)) { sw.WriteLine(str); } } using (StreamWriter sw = File.AppendText(filePath)) { sw.WriteLine(headLine); } using (StreamWriter sw = File.AppendText(filePath)) { sw.WriteLine("************************"); } using (StreamWriter sw = File.AppendText(filePath)) { sw.WriteLine(headLine); } for (int y = bluprintOfPath.GetLength(1) - 1; y >= 0; y--) { using (StreamWriter sw = File.AppendText(filePath)) { sw.Write("\t" + y.ToString() + "#"); } string str = ""; for (int x = 0; x < bluprintOfPath.GetLength(0); x++) { WayNode currentNode = bluprintOfPath[x, y]; switch (currentNode.previusRoadDirection) { case Base.DIREC.DOWN: str += "\t↓"; break; case Base.DIREC.UP: str += "\t↑"; break; case Base.DIREC.LEFT: str += "\t←"; break; case Base.DIREC.RIGHT: str += "\t→"; break; case Base.DIREC.NO_DIRECTION: str += "\t•"; break; } } using (StreamWriter sw = File.AppendText(filePath)) { sw.WriteLine(str); } } using (StreamWriter sw = File.AppendText(filePath)) { sw.WriteLine(headLine); } using (StreamWriter sw = File.AppendText(filePath)) { sw.WriteLine("************************"); } using (StreamWriter sw = File.AppendText(filePath)) { sw.WriteLine(headLine); } for (int y = bluprintOfPath.GetLength(1) - 1; y >= 0; y--) { using (StreamWriter sw = File.AppendText(filePath)) { sw.Write("\t" + y.ToString() + "#"); } string str = ""; for (int x = 0; x < bluprintOfPath.GetLength(0); x++) { WayNode currentNode = bluprintOfPath[x, y]; if (currentNode.wayCost == WayNode.UNREACHABLE) { str += "\t*"; } else { str += "\t" + currentNode.wayCost / WayNode.GROUND_COST; } } using (StreamWriter sw = File.AppendText(filePath)) { sw.WriteLine(str); } } using (StreamWriter sw = File.AppendText(filePath)) { sw.WriteLine(headLine); } using (StreamWriter sw = File.AppendText(filePath)) { sw.WriteLine("************************"); } using (StreamWriter sw = File.AppendText(filePath)) { sw.WriteLine(headLine); } for (int y = bluprintOfPath.GetLength(1) - 1; y >= 0; y--) { using (StreamWriter sw = File.AppendText(filePath)) { sw.Write("\t" + y.ToString() + "#"); } string str = ""; for (int x = 0; x < bluprintOfPath.GetLength(0); x++) { Point pointToPrint = new Point(x, y); if (pointToPrint.IsSamePoint(from)) { str += "\tS"; } else if (pointToPrint.IsSamePoint(to)) { str += "\tE"; } else { str += "\t•"; } } using (StreamWriter sw = File.AppendText(filePath)) { sw.WriteLine(str); } } using (StreamWriter sw = File.AppendText(filePath)) { sw.WriteLine(headLine); } using (StreamWriter sw = File.AppendText(filePath)) { sw.WriteLine("************************"); sw.WriteLine("Target point cost: " + bluprintOfPath[to.x, to.y].wayCost.ToString()); } s_errorReportIndex++; }
public override bool StartFindWay(Map map, out WayNode path) { _map = map; var startNode = map.FindWayData.Entrance; var endNode = map.FindWayData.Destination; _endPos = endNode.Pos; //起点终点不可达 if (!startNode.Walkable || !endNode.Walkable) { return(base.StartFindWay(map, out path)); } //检查队列 var queue = new Queue <Floor>(); //已经检查过的点 var checkedDic = new Dictionary <Floor, bool>(); queue.Enqueue(startNode); while (queue.Count > 0) { var tmp = queue.Dequeue(); var tmpPos = tmp.Pos; var lPos = tmpPos + new int2(-1, 0); var rPos = tmpPos + new int2(1, 0); var uPos = tmpPos + new int2(0, 1); var dPos = tmpPos + new int2(0, -1); var lFloor = _map.FindWayData[lPos]; var rFloor = _map.FindWayData[rPos]; var uFloor = _map.FindWayData[uPos]; var dFloor = _map.FindWayData[dPos]; if (lFloor && !checkedDic.ContainsKey(lFloor)) { var w = CheckPosNode(tmp, lFloor); if (w.Item1 != null) { WayNodes[lPos] = w.Item1; queue.Enqueue(lFloor); if (w.Item2) { path = w.Item1; return(true); } } checkedDic.Add(lFloor, true); } if (rFloor && !checkedDic.ContainsKey(rFloor)) { var w = CheckPosNode(tmp, rFloor); if (w.Item1 != null) { WayNodes[rPos] = w.Item1; queue.Enqueue(rFloor); if (w.Item2) { path = w.Item1; return(true); } } checkedDic.Add(rFloor, true); } if (uFloor && !checkedDic.ContainsKey(uFloor)) { var w = CheckPosNode(tmp, uFloor); if (w.Item1 != null) { WayNodes[uPos] = w.Item1; queue.Enqueue(uFloor); if (w.Item2) { path = w.Item1; return(true); } } checkedDic.Add(uFloor, true); } if (dFloor && !checkedDic.ContainsKey(dFloor)) { var w = CheckPosNode(tmp, dFloor); if (w.Item1 != null) { WayNodes[dPos] = w.Item1; queue.Enqueue(dFloor); if (w.Item2) { path = w.Item1; return(true); } } checkedDic.Add(dFloor, true); } } return(base.StartFindWay(map, out path)); }
/**********************************************************************************/ // просчитываем конкретную точку построения пути // проверяем её стоимость и на оснеовании проверки вносим коррективы в карту маршрутов // /**********************************************************************************/ void CalculatePoint(BlockDescriptor descriptor, WayNode[,] bluprintOfRoadMap, Point currentPoint, Point to, Base.DIREC prevPointDir, int sizeOfBlock, int currentWayCost, ref int nodeClosestDist, LinkedList <Point> currentPontsToProcess, LinkedList <Point> nextIterationPontsToProcess) { // проверки параметров if (currentPoint == null || to == null || descriptor == null || bluprintOfRoadMap == null || bluprintOfRoadMap.Length == 0) { Debug.LogError("CalculatePoint: Wrong arguments! " + "descriptor is null? " + (descriptor == null).ToString() + " currentPoint is null? " + (currentPoint == null).ToString() + " prevPointDir " + prevPointDir.ToString() + " to is null? " + (to == null).ToString() + " bluprintOfRoadMap is null? " + (bluprintOfRoadMap == null).ToString() + " bluprintOfRoadMap.Length " + bluprintOfRoadMap.Length); return; } int newX = currentPoint.x; int newY = currentPoint.y; // выбираем координаты новой точки в зависимости от положения предыдущей switch (prevPointDir) { case Base.DIREC.DOWN: newY++; break; case Base.DIREC.UP: newY--; break; case Base.DIREC.LEFT: newX++; break; case Base.DIREC.RIGHT: newX--; break; } // если координата удовлетворяет ... if (newX < 0 || newX >= sizeOfBlock || newY < 0 || newY >= sizeOfBlock) { return; } // ... и место свободно для дорог - процессим if (descriptor.FreeWaysMap[newX, newY] == false) { return; } WayNode wayNodeToCheck = bluprintOfRoadMap[newX, newY]; int possibleWayCost = currentWayCost + wayNodeToCheck.cellCost; Point pointToCheck = null; if (wayNodeToCheck.wayCost > possibleWayCost) { wayNodeToCheck.wayCost = possibleWayCost; wayNodeToCheck.previusRoadDirection = prevPointDir; pointToCheck = new Point(newX, newY); } else if (wayNodeToCheck.wayCost == possibleWayCost) { // в случае, если стоимости равны - в половине случаев переключаемся на новый путь для разнообразия if (Random.Range(0, 100) >= 50) { wayNodeToCheck.wayCost = possibleWayCost; wayNodeToCheck.previusRoadDirection = prevPointDir; } } // если мы нашли точку для просчёта - определяемся когда будем её просчитывать // если "квадратное" растояние меньше или равно самого близкого к целевой точке, то будем просчитывать её в первую очередь if (pointToCheck != null) { int dist = Mathf.Abs(pointToCheck.x - to.x) + Mathf.Abs(pointToCheck.y - to.y); if (dist == 0) { nodeClosestDist = 0; return; } else if (dist <= nodeClosestDist) { currentPontsToProcess.AddLast(pointToCheck); nodeClosestDist = dist; } else { nextIterationPontsToProcess.AddLast(pointToCheck); } } }
/**********************************************************************************/ // строим дорогу из точки from в точку to // результат записываем в bluprintOfRoadMap // /**********************************************************************************/ void PrepareBluprintOfRoads(BlockDescriptor descriptor, WayNode[,] bluprintOfRoadMap, Point from, Point to, int sizeOfBlock) { // проверки параметров if (from == null || to == null || descriptor == null || bluprintOfRoadMap == null || bluprintOfRoadMap.Length == 0) { Debug.LogError("PrepareBluprintOfRoads: Wrong arguments! " + "descriptor is null? " + (descriptor == null).ToString() + " from is null? " + (from == null).ToString() + " to is null? " + (to == null).ToString() + " bluprintOfRoadMap is null? " + (bluprintOfRoadMap == null).ToString() + " bluprintOfRoadMap.Length " + bluprintOfRoadMap.Length); return; } LinkedList <Point> currentPontsToProcess = new LinkedList <Point>(); LinkedList <Point> nextIterationPontsToProcess = new LinkedList <Point>(); // устанавливаем стартовые параметры алгоритма - начальную точку и её стоимость пути bluprintOfRoadMap[from.x, from.y].wayCost = 0; currentPontsToProcess.AddFirst(from); int possibleClosestDist = Mathf.Abs(from.x - to.x) + Mathf.Abs(from.y - to.y); int nodeClosestDist = possibleClosestDist; bool buildingComplite = false; int maxSteps = sizeOfBlock * 4; while (!buildingComplite && maxSteps > 0) { while (currentPontsToProcess.Count > 0) { // забираем точку для просчёта и работаем с ней Point currentPoint = currentPontsToProcess.First.Value; currentPontsToProcess.RemoveFirst(); int currentWayCost = bluprintOfRoadMap[currentPoint.x, currentPoint.y].wayCost; // просчитываем верхную точку CalculatePoint(descriptor, bluprintOfRoadMap, currentPoint, to, Base.DIREC.DOWN, sizeOfBlock, currentWayCost, ref nodeClosestDist, currentPontsToProcess, nextIterationPontsToProcess); // просчитываем нижнюю точку CalculatePoint(descriptor, bluprintOfRoadMap, currentPoint, to, Base.DIREC.UP, sizeOfBlock, currentWayCost, ref nodeClosestDist, currentPontsToProcess, nextIterationPontsToProcess); // просчитываем левую точку CalculatePoint(descriptor, bluprintOfRoadMap, currentPoint, to, Base.DIREC.RIGHT, sizeOfBlock, currentWayCost, ref nodeClosestDist, currentPontsToProcess, nextIterationPontsToProcess); // просчитываем правую точку CalculatePoint(descriptor, bluprintOfRoadMap, currentPoint, to, Base.DIREC.LEFT, sizeOfBlock, currentWayCost, ref nodeClosestDist, currentPontsToProcess, nextIterationPontsToProcess); } // принимаем решение о дальнейших итерациях WayNode WayNodeFinal = bluprintOfRoadMap[to.x, to.y]; if (WayNodeFinal.wayCost == possibleClosestDist) { buildingComplite = true; } else { // если мы исчерпали возможности для построения маршрута - завершаем проектирование маршрута if (WayNodeFinal.wayCost != WayNode.UNREACHABLE && nextIterationPontsToProcess.Count == 0) { buildingComplite = true; } // в противном случае продолжаем else { // меняем местами коллекции LinkedList <Point> supportPointer = currentPontsToProcess; currentPontsToProcess = nextIterationPontsToProcess; nextIterationPontsToProcess = supportPointer; } } maxSteps--; } if (!buildingComplite) { Debug.LogError("PrepareBluprintOfRoads: We cann't build way"); Logger.CreatePathFinderErrorReport(bluprintOfRoadMap, from, to, descriptor.FreeSpaceMap); } else { // формируем направления дороги по окончанию работы алгоритма "А star!" Point pointToBuild = new Point(to); WayNode nodeForBuilding = null; WayNode finalBuildingNode = bluprintOfRoadMap[from.x, from.y]; Base.DIREC nextDirection = Base.DIREC.NO_DIRECTION; maxSteps = sizeOfBlock * 4; do { // проверяем координыты if (pointToBuild.x < 0 || pointToBuild.x >= sizeOfBlock || pointToBuild.y < 0 || pointToBuild.y >= sizeOfBlock) { continue; } nodeForBuilding = bluprintOfRoadMap[pointToBuild.x, pointToBuild.y]; // добавляем направления, если надо Base.DIREC newDirection = nodeForBuilding.previusRoadDirection; if (!nodeForBuilding.approvedDirections.Contains(newDirection)) { nodeForBuilding.approvedDirections.Add(newDirection); } if (!nodeForBuilding.approvedDirections.Contains(nextDirection) && nextDirection != Base.DIREC.NO_DIRECTION) { nodeForBuilding.approvedDirections.Add(nextDirection); } // устанавливаем цену дороги nodeForBuilding.cellCost = WayNode.ROAD_COST; switch (newDirection) { case Base.DIREC.DOWN: pointToBuild.y--; break; case Base.DIREC.UP: pointToBuild.y++; break; case Base.DIREC.LEFT: pointToBuild.x--; break; case Base.DIREC.RIGHT: pointToBuild.x++; break; default: Debug.LogError("PrepareBluprintOfRoads: wrong direction!"); break; } nextDirection = Base.InvertDirection(newDirection); maxSteps--; } while (nodeForBuilding != finalBuildingNode && maxSteps > 0); } }
/**********************************************************************************/ // строим дороги внутри блока // /**********************************************************************************/ public void BuildRoadInBlock(BlockDescriptor descriptor, int x, int y, int blockSize) { // проверки параметров if (x < 0 || y < 0 || descriptor == null || blockSize < 0) { Debug.LogError("BuildRoadInBlock: Wrong arguments! " + "x " + x + " y " + y + " blockSize " + blockSize + "descriptor is null? " + (descriptor == null).ToString()); return; } // подготавливаем коллекции для работы с дорогами и путями List <Point> pointsToConnect = new List <Point>(); WayNode[,] roadMap = new WayNode[blockSize, blockSize]; for (int i = 0; i < blockSize; i++) { for (int z = 0; z < blockSize; z++) { roadMap[i, z] = new WayNode(); } } // добавляем точки дорог к зданиям for (int buildingID = 0; buildingID < descriptor.Buildings.Count; buildingID++) { GameObject building = descriptor.Buildings[buildingID]; BuildingController ctr = building.GetComponent <BuildingController>(); int xCorInRoadMap = ctr.RoadPoint.x + ctr.GetLocalPosition().x - ctr.XBuildingSize / 2; int yCorInRoadMap = ctr.RoadPoint.y + ctr.GetLocalPosition().y - ctr.YBuildingSize / 2; pointsToConnect.Add(new Point(xCorInRoadMap, yCorInRoadMap)); roadMap[xCorInRoadMap, yCorInRoadMap].approvedDirections.Add(Base.DIREC.UP); roadMap[xCorInRoadMap, yCorInRoadMap].previusRoadDirection = Base.DIREC.UP; roadMap[xCorInRoadMap, yCorInRoadMap].cellCost = WayNode.ROAD_COST; } // добавляем все входы в блок, которые надо соеденить if (descriptor.RoadConnections[(int)Base.DIREC.DOWN] != ROAD_CONNECTION_STATUS.BLOCKED) { int xCorInRoadMap = blockSize / 2; int yCorInRoadMap = 0; pointsToConnect.Add(new Point(xCorInRoadMap, yCorInRoadMap)); roadMap[xCorInRoadMap, yCorInRoadMap].approvedDirections.Add(Base.DIREC.DOWN); roadMap[xCorInRoadMap, yCorInRoadMap].previusRoadDirection = Base.DIREC.DOWN; roadMap[xCorInRoadMap, yCorInRoadMap].cellCost = WayNode.ROAD_COST; } if (descriptor.RoadConnections[(int)Base.DIREC.UP] != ROAD_CONNECTION_STATUS.BLOCKED) { int xCorInRoadMap = blockSize / 2; int yCorInRoadMap = blockSize - 1; pointsToConnect.Add(new Point(xCorInRoadMap, yCorInRoadMap)); roadMap[xCorInRoadMap, yCorInRoadMap].approvedDirections.Add(Base.DIREC.UP); roadMap[xCorInRoadMap, yCorInRoadMap].previusRoadDirection = Base.DIREC.UP; roadMap[xCorInRoadMap, yCorInRoadMap].cellCost = WayNode.ROAD_COST; } if (descriptor.RoadConnections[(int)Base.DIREC.LEFT] != ROAD_CONNECTION_STATUS.BLOCKED) { int xCorInRoadMap = 0; int yCorInRoadMap = blockSize / 2; pointsToConnect.Add(new Point(xCorInRoadMap, yCorInRoadMap)); roadMap[xCorInRoadMap, yCorInRoadMap].approvedDirections.Add(Base.DIREC.LEFT); roadMap[xCorInRoadMap, yCorInRoadMap].previusRoadDirection = Base.DIREC.LEFT; roadMap[xCorInRoadMap, yCorInRoadMap].cellCost = WayNode.ROAD_COST; } if (descriptor.RoadConnections[(int)Base.DIREC.RIGHT] != ROAD_CONNECTION_STATUS.BLOCKED) { int xCorInRoadMap = blockSize - 1; int yCorInRoadMap = blockSize / 2; pointsToConnect.Add(new Point(xCorInRoadMap, yCorInRoadMap)); roadMap[xCorInRoadMap, yCorInRoadMap].approvedDirections.Add(Base.DIREC.RIGHT); roadMap[xCorInRoadMap, yCorInRoadMap].previusRoadDirection = Base.DIREC.RIGHT; roadMap[xCorInRoadMap, yCorInRoadMap].cellCost = WayNode.ROAD_COST; } // проходим по всем точкам соединения и строим чертёж дороги for (int i = 0; i < pointsToConnect.Count; i++) { Point from = pointsToConnect[i]; for (int z = i + 1; z < pointsToConnect.Count; z++) { Point to = pointsToConnect[z]; // после выбора точки строим дорогу "from - to" // подготавливаем чертёж PrepareBluprintOfRoads(descriptor, roadMap, from, to, blockSize); // обнуляем стоимости путей после каждой итерации построения for (int xBuprint = 0; xBuprint < blockSize; xBuprint++) { for (int yBuprint = 0; yBuprint < blockSize; yBuprint++) { roadMap[xBuprint, yBuprint].wayCost = WayNode.UNREACHABLE; } } } } // строим дорогу for (int xBuprint = 0; xBuprint < blockSize; xBuprint++) { for (int yBuprint = 0; yBuprint < blockSize; yBuprint++) { List <Base.DIREC> approvedDirection = roadMap[xBuprint, yBuprint].approvedDirections; if (approvedDirection.Count != 0) { int roadKey = GetRoadKeyFromDirection(approvedDirection); if (!m_roadDictionary.ContainsKey(roadKey)) { Debug.LogError("Wrong road generation!"); } GameObject roadToInstance = m_roadDictionary[roadKey]; // правильные координаты будут выставлены несколькими шагами дальше через localPosition GameObject roadInstance = GameObject.Instantiate(roadToInstance, new Vector3(0.0f, 0.0f, 0.0f), Quaternion.identity) as GameObject; // устанавливаем в родителя roadInstance.transform.SetParent(descriptor.gameObject.transform); roadInstance.transform.localPosition = new Vector3((float)(xBuprint) * Base.SIZE_OF_CELL + Base.HALF_OF_CELL, (float)(yBuprint) * Base.SIZE_OF_CELL + Base.HALF_OF_CELL, 0.0f); // отмечаем данную ячейку как занятую для добавления новых объектов descriptor.FreeSpaceMap[xBuprint, yBuprint] = false; // сохраняем отметку о построенной дороге m_roadObjectMap[xBuprint * x, yBuprint *y] = true; } } } // обновляем правила дорог UpdateRulesForBlock(x, y, descriptor.RoadConnections); }