public List <Vector2Int> FindPath(Vector2Int startCellPos, Vector2Int destCellPos, bool checkObject = false, int maxDist = 10) { List <Pos> path = new List <Pos>(); // 점수 매기기 // F = G + H // F = 최종 점수 (작을수록 좋음.) // G = 시작점에서 해당 좌표까지 비용 // H = 목적지에서 얼마나 가까운지 // 방문 여부 HashSet <Pos> closeList = new HashSet <Pos>(); // CloseList // (y,x) 가는 길을 한 번이라도 발견했나? // 발견x => maxVal // 발견o => F = G + H Dictionary <Pos, int> openList = new Dictionary <Pos, int>(); Dictionary <Pos, Pos> parent = new Dictionary <Pos, Pos>(); // 오픈 리스트에 있는 정보 중에서, 가장 좋은 후보를 빠르게 뽑기 위한 도구 (Heap 대신) PriorityQueue <PQNode> pq = new PriorityQueue <PQNode>(); Pos pos = Cell2Pos(startCellPos); Pos dest = Cell2Pos(destCellPos); // 시작점 발견 (예약 진행) openList.Add(pos, 10 * (Math.Abs(dest.Y - pos.Y) + Math.Abs(dest.X - pos.X))); pq.Push(new PQNode() { F = (Math.Abs(dest.Y - pos.Y) + Math.Abs(dest.X - pos.X)), G = 0, Y = pos.Y, X = pos.X }); parent.Add(pos, pos); while (pq.Count > 0) { // 제일 좋은 후보를 찾는다. PQNode pqNode = pq.Pop(); Pos node = new Pos(pqNode.Y, pqNode.X); // 동일한 좌표를 여러 경로로 찾아서, 더 빠른 경로로 인하여 이미 close에 담긴경우 스킵 if (closeList.Contains(node)) { continue; } // 방문. closeList.Add(node); // 목적지다. if (node.Y == dest.Y && node.X == dest.X) { break; } // 상하좌우 등 이동할 수 있는 좌표인지 확인하고 예약 for (int i = 0; i < _deltaY.Length; i++) { Pos next = new Pos(node.Y + _deltaY[i], node.X + _deltaX[i]); // 너무 멀면 스킵 if (Math.Abs(pos.Y - next.Y) + Math.Abs(pos.X - next.X) > maxDist) { continue; } // 유효 범위를 벗어나면 스킵 // 벽으로 막혀도. if (next.Y != dest.Y || next.X != dest.X) { if (CanGo(Pos2Cell(next), checkObject) == false) { continue; } } if (closeList.Contains(next)) { continue; } int g = 0; // node.G + _cost[i]; int h = 10 * ((dest.Y - next.Y) * (dest.Y - next.Y) + (dest.X - next.X) * (dest.X - next.X)); // 다른 경로에서 더 빠른 길 찾으면 스킵 int value = 0; if (!openList.TryGetValue(next, out value)) { value = Int32.MaxValue; } if (value < g + h) { continue; } // 예약 진행 if (openList.TryAdd(next, g + h) == false) { openList[next] = g + h; } pq.Push(new PQNode() { F = g + h, G = g, Y = next.Y, X = next.X }); if (parent.TryAdd(next, node) == false) { parent[next] = node; } } } return(CalcCellPathFromParent(parent, dest)); }
Pos Cell2Pos(Vector2Int cell) { // CellPos -> ArrayPos return(new Pos(MaxY - cell.y, cell.x - MinX)); }
public bool ApplyMove(GameObject gameObject, Vector2Int dest, bool checkObjects = true, bool collision = true) { if (gameObject.Room == null) { return(false); } if (gameObject.Room.Map != this) { return(false); } PositionInfo posInfo = gameObject.PosInfo; if (CanGo(dest, checkObjects) == false) { return(false); } if (collision) { { int x = posInfo.PosX - MinX; int y = MaxY - posInfo.PosY; if (_objects[y, x] == gameObject) { _objects[y, x] = null; } } { int x = dest.x - MinX; int y = MaxY - dest.y; _objects[y, x] = gameObject; } } // Zone GameObjectType type = ObjectManager.GetObjectTypeById(gameObject.Id); if (type == GameObjectType.Player) { Player player = (Player)gameObject; Zone now = gameObject.Room.GetZone(gameObject.CellPos); Zone after = gameObject.Room.GetZone(dest); if (now != after) { now.Players.Remove(player); after.Players.Add(player); } } else if (type == GameObjectType.Monster) { Monster monster = (Monster)gameObject; Zone now = gameObject.Room.GetZone(gameObject.CellPos); Zone after = gameObject.Room.GetZone(dest); if (now != after) { now.Monsters.Remove(monster); after.Monsters.Add(monster); } } else if (type == GameObjectType.Projectile) { Projectile projectile = (Projectile)gameObject; Zone now = gameObject.Room.GetZone(gameObject.CellPos); Zone after = gameObject.Room.GetZone(dest); if (now != after) { now.Projectiles.Remove(projectile); after.Projectiles.Add(projectile); } } // 실제 좌표 이동 posInfo.PosX = dest.x; posInfo.PosY = dest.y; return(true); }