public List <PathFinderNode> FindPath(IVec3 start, IVec3 end) { lock (this) { HighResolutionTime.Start(); // Is faster if we don't clear the matrix, just assign different values for open and close and ignore the rest // I could have user Array.Clear() but using unsafe code is faster, no much but it is. //fixed (PathFinderNodeFast* pGrid = tmpGrid) // ZeroMemory((byte*) pGrid, sizeof(PathFinderNodeFast) * 1000000); mFound = false; mStop = false; mStopped = false; mCloseNodeCounter = 0; mOpenNodeValue += 2; mCloseNodeValue += 2; mOpen.Clear(); mClose.Clear(); #if DEBUGON if (mDebugProgress && PathFinderDebug != null) { PathFinderDebug(0, 0, start.x, start.y, PathFinderNodeType.Start, -1, -1); } if (mDebugProgress && PathFinderDebug != null) { PathFinderDebug(0, 0, end.x, end.y, PathFinderNodeType.End, -1, -1); } #endif mLocation = (start.y << mGridYLog2) + start.x; mEndLocation = (end.y << mGridYLog2) + end.x; mCalcGrid[mLocation].G = 0; mCalcGrid[mLocation].F = mHEstimate; mCalcGrid[mLocation].PX = (ushort)start.x; mCalcGrid[mLocation].PY = (ushort)start.y; mCalcGrid[mLocation].Status = mOpenNodeValue; mOpen.Push(mLocation); while (mOpen.Count > 0 && !mStop) { mLocation = mOpen.Pop(); //Is it in closed list? means this node was already processed if (mCalcGrid[mLocation].Status == mCloseNodeValue) { continue; } mLocationX = (ushort)(mLocation & mGridXMinus1); mLocationY = (ushort)(mLocation >> mGridYLog2); #if DEBUGON if (mDebugProgress && PathFinderDebug != null) { PathFinderDebug(0, 0, mLocation & mGridXMinus1, mLocation >> mGridYLog2, PathFinderNodeType.Current, -1, -1); } #endif if (mLocation == mEndLocation) { mCalcGrid[mLocation].Status = mCloseNodeValue; mFound = true; break; } if (mCloseNodeCounter > mSearchLimit) { mStopped = true; mCompletedTime = HighResolutionTime.GetTime(); return(null); } if (mPunishChangeDirection) { mHoriz = (mLocationX - mCalcGrid[mLocation].PX); } //Lets calculate each successors for (int i = 0; i < (mDiagonals ? 8 : 4); i++) { mNewLocationX = (ushort)(mLocationX + mDirection[i, 0]); mNewLocationY = (ushort)(mLocationY + mDirection[i, 1]); mNewLocation = (mNewLocationY << mGridYLog2) + mNewLocationX; if (mNewLocationX >= mGridX || mNewLocationY >= mGridY) { continue; } // Unbreakeable? if (mGrid[mNewLocationX, mNewLocationY] == 0) { continue; } if (mHeavyDiagonals && i > 3) { mNewG = mCalcGrid[mLocation].G + (int)(mGrid[mNewLocationX, mNewLocationY] * 2.41); } else { mNewG = mCalcGrid[mLocation].G + mGrid[mNewLocationX, mNewLocationY]; } if (mPunishChangeDirection) { if ((mNewLocationX - mLocationX) != 0) { if (mHoriz == 0) { mNewG += Math.Abs(mNewLocationX - end.x) + Math.Abs(mNewLocationY - end.y); } } if ((mNewLocationY - mLocationY) != 0) { if (mHoriz != 0) { mNewG += Math.Abs(mNewLocationX - end.x) + Math.Abs(mNewLocationY - end.y); } } } //Is it open or closed? if (mCalcGrid[mNewLocation].Status == mOpenNodeValue || mCalcGrid[mNewLocation].Status == mCloseNodeValue) { // The current node has less code than the previous? then skip this node if (mCalcGrid[mNewLocation].G <= mNewG) { continue; } } mCalcGrid[mNewLocation].PX = mLocationX; mCalcGrid[mNewLocation].PY = mLocationY; mCalcGrid[mNewLocation].G = mNewG; switch (mFormula) { default: case HeuristicFormula.Manhattan: mH = mHEstimate * (Math.Abs(mNewLocationX - end.x) + Math.Abs(mNewLocationY - end.y)); break; case HeuristicFormula.MaxDXDY: mH = mHEstimate * (Math.Max(Math.Abs(mNewLocationX - end.x), Math.Abs(mNewLocationY - end.y))); break; case HeuristicFormula.DiagonalShortCut: int h_diagonal = Math.Min(Math.Abs(mNewLocationX - end.x), Math.Abs(mNewLocationY - end.y)); int h_straight = (Math.Abs(mNewLocationX - end.x) + Math.Abs(mNewLocationY - end.y)); mH = (mHEstimate * 2) * h_diagonal + mHEstimate * (h_straight - 2 * h_diagonal); break; case HeuristicFormula.Euclidean: mH = (int)(mHEstimate * Math.Sqrt(Math.Pow((mNewLocationY - end.x), 2) + Math.Pow((mNewLocationY - end.y), 2))); break; case HeuristicFormula.EuclideanNoSQR: mH = (int)(mHEstimate * (Math.Pow((mNewLocationX - end.x), 2) + Math.Pow((mNewLocationY - end.y), 2))); break; case HeuristicFormula.Custom1: IVec3 dxy = new IVec3(); dxy.Set(Math.Abs(end.x - mNewLocationX), Math.Abs(end.y - mNewLocationY), 0); //### fix z parameter int Orthogonal = Math.Abs(dxy.x - dxy.y); int Diagonal = Math.Abs(((dxy.x + dxy.y) - Orthogonal) / 2); mH = mHEstimate * (Diagonal + Orthogonal + dxy.x + dxy.y); break; } if (mTieBreaker) { int dx1 = mLocationX - end.x; int dy1 = mLocationY - end.y; int dx2 = start.x - end.x; int dy2 = start.y - end.y; int cross = Math.Abs(dx1 * dy2 - dx2 * dy1); mH = (int)(mH + cross * 0.001); } mCalcGrid[mNewLocation].F = mNewG + mH; #if DEBUGON if (mDebugProgress && PathFinderDebug != null) { PathFinderDebug(mLocationX, mLocationY, mNewLocationX, mNewLocationY, PathFinderNodeType.Open, mCalcGrid[mNewLocation].F, mCalcGrid[mNewLocation].G); } #endif //It is faster if we leave the open node in the priority queue //When it is removed, it will be already closed, it will be ignored automatically //if (tmpGrid[newLocation].Status == 1) //{ // //int removeX = newLocation & gridXMinus1; // //int removeY = newLocation >> gridYLog2; // mOpen.RemoveLocation(newLocation); //} //if (tmpGrid[newLocation].Status != 1) //{ mOpen.Push(mNewLocation); //} mCalcGrid[mNewLocation].Status = mOpenNodeValue; } mCloseNodeCounter++; mCalcGrid[mLocation].Status = mCloseNodeValue; #if DEBUGON if (mDebugProgress && PathFinderDebug != null) { PathFinderDebug(0, 0, mLocationX, mLocationY, PathFinderNodeType.Close, mCalcGrid[mLocation].F, mCalcGrid[mLocation].G); } #endif } mCompletedTime = HighResolutionTime.GetTime(); if (mFound) { mClose.Clear(); int posX = end.x; int posY = end.y; int posZ = end.z; PathFinderNodeFast fNodeTmp = mCalcGrid[(end.y << mGridYLog2) + end.x]; PathFinderNode fNode; fNode.F = fNodeTmp.F; fNode.G = fNodeTmp.G; fNode.H = 0; fNode.PX = fNodeTmp.PX; fNode.PY = fNodeTmp.PY; fNode.PZ = fNodeTmp.PZ; fNode.X = end.x; fNode.Y = end.y; fNode.Z = end.z; while (fNode.X != fNode.PX || fNode.Y != fNode.PY) { mClose.Add(fNode); #if DEBUGON if (mDebugFoundPath && PathFinderDebug != null) { PathFinderDebug(fNode.PX, fNode.PY, fNode.X, fNode.Y, PathFinderNodeType.Path, fNode.F, fNode.G); } #endif posX = fNode.PX; posY = fNode.PY; posZ = fNode.PZ; fNodeTmp = mCalcGrid[(posY << mGridYLog2) + posX]; fNode.F = fNodeTmp.F; fNode.G = fNodeTmp.G; fNode.H = 0; fNode.PX = fNodeTmp.PX; fNode.PY = fNodeTmp.PY; fNode.PZ = fNodeTmp.PZ; fNode.X = posX; fNode.Y = posY; fNode.Z = posZ; //### fix for pathfinder to work in Z } mClose.Add(fNode); #if DEBUGON if (mDebugFoundPath && PathFinderDebug != null) { PathFinderDebug(fNode.PX, fNode.PY, fNode.X, fNode.Y, PathFinderNodeType.Path, fNode.F, fNode.G); } #endif mStopped = true; return(mClose); } mStopped = true; return(null); } }
public List <PathFinderNode> FindPath(IVec3 start, IVec3 end) { Profiler.Start("Pathfinder.FindPath"); HighResolutionTime.Start(); PathFinderNode parentNode; bool found = false; int gridX = mGrid.GetGridWidth(); int gridY = mGrid.GetGridHeight(); mStop = false; mStopped = false; mOpen.Clear(); mClose.Clear(); #if DEBUGON if (mDebugProgress && PathFinderDebug != null) { PathFinderDebug(0, 0, start.x, start.y, PathFinderNodeType.Start, -1, -1); } if (mDebugProgress && PathFinderDebug != null) { PathFinderDebug(0, 0, end.x, end.y, PathFinderNodeType.End, -1, -1); } #endif sbyte[,] direction; if (mDiagonals) { direction = new sbyte[8, 2] { { 0, -1 }, { 1, 0 }, { 0, 1 }, { -1, 0 }, { 1, -1 }, { 1, 1 }, { -1, 1 }, { -1, -1 } } } ; else { direction = new sbyte[4, 2] { { 0, -1 }, { 1, 0 }, { 0, 1 }, { -1, 0 } } }; parentNode.G = 0; parentNode.H = mHEstimate; parentNode.F = parentNode.G + parentNode.H; parentNode.X = start.x; parentNode.Y = start.y; parentNode.Z = start.z; parentNode.PX = parentNode.X; parentNode.PY = parentNode.Y; parentNode.PZ = parentNode.Z; mOpen.Push(parentNode); while (mOpen.Count > 0 && !mStop) { parentNode = mOpen.Pop(); #if DEBUGON if (mDebugProgress && PathFinderDebug != null) { PathFinderDebug(0, 0, parentNode.X, parentNode.Y, PathFinderNodeType.Current, -1, -1); } #endif if (parentNode.X == end.x && parentNode.Y == end.y) { mClose.Add(parentNode); found = true; break; } if (mClose.Count > mSearchLimit) { mStopped = true; Profiler.Stop("Pathfinder.FindPath"); return(null); } if (mPunishChangeDirection) { mHoriz = (parentNode.X - parentNode.PX); } //Lets calculate each successors for (int i = 0; i < (mDiagonals ? 8 : 4); i++) { PathFinderNode newNode = new PathFinderNode(); newNode.X = parentNode.X + direction[i, 0]; newNode.Y = parentNode.Y + direction[i, 1]; newNode.Z = 0; //### FIX FOR PATHFINDING TO WORK IN Z if (newNode.X < 0 || newNode.Y < 0 || newNode.X >= gridX || newNode.Y >= gridY) { continue; } int newG; if (mHeavyDiagonals && i > 3) { newG = parentNode.G + (int)(mGrid.GetBlockedValue(newNode) * 2.41); } else { newG = parentNode.G + mGrid.GetBlockedValue(newNode); } if (newG == parentNode.G) { //Unbrekeable continue; } if (mPunishChangeDirection) { if ((newNode.X - parentNode.X) != 0) { if (mHoriz == 0) { newG += 20; } } if ((newNode.Y - parentNode.Y) != 0) { if (mHoriz != 0) { newG += 20; } } } int foundInOpenIndex = -1; for (int j = 0; j < mOpen.Count; j++) { if (mOpen[j].X == newNode.X && mOpen[j].Y == newNode.Y) { foundInOpenIndex = j; break; } } if (foundInOpenIndex != -1 && mOpen[foundInOpenIndex].G <= newG) { continue; } int foundInCloseIndex = -1; for (int j = 0; j < mClose.Count; j++) { if (mClose[j].X == newNode.X && mClose[j].Y == newNode.Y) { foundInCloseIndex = j; break; } } if (foundInCloseIndex != -1 && mClose[foundInCloseIndex].G <= newG) { continue; } newNode.PX = parentNode.X; newNode.PY = parentNode.Y; newNode.PZ = parentNode.Z; newNode.G = newG; switch (mFormula) { default: case HeuristicFormula.Manhattan: newNode.H = mHEstimate * (Math.Abs(newNode.X - end.x) + Math.Abs(newNode.Y - end.y)); break; case HeuristicFormula.MaxDXDY: newNode.H = mHEstimate * (Math.Max(Math.Abs(newNode.X - end.x), Math.Abs(newNode.Y - end.y))); break; case HeuristicFormula.DiagonalShortCut: int h_diagonal = Math.Min(Math.Abs(newNode.X - end.x), Math.Abs(newNode.Y - end.y)); int h_straight = (Math.Abs(newNode.X - end.x) + Math.Abs(newNode.Y - end.y)); newNode.H = (mHEstimate * 2) * h_diagonal + mHEstimate * (h_straight - 2 * h_diagonal); break; case HeuristicFormula.Euclidean: newNode.H = (int)(mHEstimate * Math.Sqrt(Math.Pow((newNode.X - end.x), 2) + Math.Pow((newNode.Y - end.y), 2))); break; case HeuristicFormula.EuclideanNoSQR: newNode.H = (int)(mHEstimate * (Math.Pow((newNode.X - end.x), 2) + Math.Pow((newNode.Y - end.y), 2))); break; case HeuristicFormula.Custom1: IVec3 dxy = new IVec3(); dxy.Set(Math.Abs(end.x - newNode.X), Math.Abs(end.y - newNode.Y), 0); //### fix z int Orthogonal = Math.Abs(dxy.x - dxy.y); int Diagonal = Math.Abs(((dxy.x + dxy.y) - Orthogonal) / 2); newNode.H = mHEstimate * (Diagonal + Orthogonal + dxy.x + dxy.y); break; } if (mTieBreaker) { int dx1 = parentNode.X - end.x; int dy1 = parentNode.Y - end.y; int dx2 = start.x - end.x; int dy2 = start.y - end.y; int cross = Math.Abs(dx1 * dy2 - dx2 * dy1); newNode.H = (int)(newNode.H + cross * 0.001); } newNode.F = newNode.G + newNode.H; #if DEBUGON if (mDebugProgress && PathFinderDebug != null) { PathFinderDebug(parentNode.X, parentNode.Y, newNode.X, newNode.Y, PathFinderNodeType.Open, newNode.F, newNode.G); } #endif //It is faster if we leave the open node in the priority queue //When it is removed, all nodes around will be closed, it will be ignored automatically //if (foundInOpenIndex != -1) // mOpen.RemoveAt(foundInOpenIndex); //if (foundInOpenIndex == -1) mOpen.Push(newNode); } mClose.Add(parentNode); #if DEBUGON if (mDebugProgress && PathFinderDebug != null) { PathFinderDebug(0, 0, parentNode.X, parentNode.Y, PathFinderNodeType.Close, parentNode.F, parentNode.G); } #endif } mCompletedTime = HighResolutionTime.GetTime(); if (found) { PathFinderNode fNode = mClose[mClose.Count - 1]; for (int i = mClose.Count - 1; i >= 0; i--) { if (fNode.PX == mClose[i].X && fNode.PY == mClose[i].Y || i == mClose.Count - 1) { #if DEBUGON if (mDebugFoundPath && PathFinderDebug != null) { PathFinderDebug(fNode.X, fNode.Y, mClose[i].X, mClose[i].Y, PathFinderNodeType.Path, mClose[i].F, mClose[i].G); } #endif fNode = mClose[i]; } else { mClose.RemoveAt(i); } } mStopped = true; mClose.RemoveAt(0); // we have duplicate path information PathFinderNode endPFN = new PathFinderNode(); endPFN.PX = end.x; endPFN.PY = end.y; mClose.Add(endPFN); Profiler.Stop("Pathfinder.FindPath"); return(mClose); } mStopped = true; Profiler.Stop("Pathfinder.FindPath"); return(null); }