Voxel 3D рдпреВрдирд┐рдЯреА рдЧреЗрдо рдореЗрдВ рдП * рдкрд╛рде рдлрд╛рдЗрдВрдбрд┐рдВрдЧ рдПрд▓рдЧреЛрд░рд┐рджрдо

рдкрд░рд┐рдЪрдп


рдЕрдкрдиреЗ рдЦреЗрд▓ рдХреЛ рд╡рд┐рдХрд╕рд┐рдд рдХрд░рддреЗ рд╕рдордп, рдореИрдВ рдкрд╣рд▓реА рдПрдирдкреАрд╕реА рдмрдирд╛рдиреЗ рдХреЗ рдореБрджреНрджреЗ рдкрд░ рдкрд╣реБрдВрдЪ рдЧрдпрд╛ред рдФрд░ рд╕рд╡рд╛рд▓ рдЙрдарддрд╛ рд╣реИ рдХрд┐ рдПрдирдкреАрд╕реА рдХреЛ рджреАрд╡рд╛рд░ рдХреЗ рдЪрд╛рд░реЛрдВ рдУрд░ рдХреИрд╕реЗ рдмрдирд╛рдпрд╛ рдЬрд╛рдП, рди рдХрд┐ "рдЗрд╕рдореЗрдВ рдЬрд╛рдПрдВ"ред


рдЗрдВрдЯрд░рдиреЗрдЯ рдкрд░ рдЪрдврд╝рдХрд░, рдореБрдЭреЗ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдПрд▓реНрдЧреЛрд░рд┐рджрдо рдорд┐рд▓реЗ:


  • рд╡рд┐рд╕реНрддреГрдд рдЦреЛрдЬ (BFS, рдЪреМрдбрд╝рд╛рдИ-рдкреНрд░рдердо рдЦреЛрдЬ)
  • рджрд┐рдЬреНрдХреНрд╕реНрдЯреНрд░рд╛ рдХрд╛ рдПрд▓реНрдЧреЛрд░рд┐рдердо (рджрд┐рдХреНрдЬрд╕реНрддреНрд░)
  • рдПрдХ рд╕реНрдЯрд╛рд░ "рдПрдХ рддрд╛рд░рд╛рдВрдХрди рдХреЗ рд╕рд╛рде"
  • рдкрд╣рд▓реЗ рд╕рд░реНрд╡рд╢реНрд░реЗрд╖реНрда рдореИрдЪ рджреНрд╡рд╛рд░рд╛ рдЦреЛрдЬреЗрдВ (рд╕рд░реНрд╡рд╢реНрд░реЗрд╖реНрда-рдкреНрд░рдердо рдЦреЛрдЬ)
  • рдЖрдИрдбреАрдП ( рдкреБрдирд░рд╛рд╡реГрддрд┐ рдЧрд╣рд░реАрдХрд░рдг рдХреЗ рд╕рд╛рде)
  • рдХреВрджреЛ рдмрд┐рдВрджреБ рдЦреЛрдЬ

рдФрд░ рдореИрдВрдиреЗ рдЕрдкрдиреЗ A * рдХреЛ voxel 3D рдЧреНрд░рд┐рдб рдкрд░ рд▓рд╛рдЧреВ рдХрд░рдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд░рдиреЗ рдХрд╛ рдирд┐рд░реНрдгрдп рд▓рд┐рдпрд╛ред



рдореЗрд░реЗ рдЦреЗрд▓ рдХрд╛ рдирдореВрдирд╛ рдирдХреНрд╢рд╛

рдЫрд╡рд┐


рдПрд▓реНрдЧреЛрд░рд┐рдердо рд╡рд┐рд╡рд░рдг


рдкреНрд░рд╛рд░рдВрднрд┐рдХ рдЪрд░рдг рд╕реЗ рдЕрдВрддрд┐рдо рдПрдХ рддрдХ рдЬрд╛рдиреЗ рд╡рд╛рд▓реЗ рд╕рднреА рд░рд╛рд╕реНрддреЛрдВ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдП * рдЪрд░рдг рджрд░ рдЪрд░рдг рджрд┐рдЦрддрд╛ рд╣реИ, рдЬрдм рддрдХ рдХрд┐ рдпрд╣ рдиреНрдпреВрдирддрдо рдПрдХ рди рдорд┐рд▓ рдЬрд╛рдПред рд╕рднреА рд╕реВрдЪрд┐рдд рдЦреЛрдЬ рдПрд▓реНрдЧреЛрд░рд┐рджрдо рдХреА рддрд░рд╣, рд╡рд╣ рдкрд╣рд▓реЗ рдЙрди рдорд╛рд░реНрдЧреЛрдВ рдХреЛ рджреЗрдЦрддрд╛ рд╣реИ рдЬреЛ рд▓рдХреНрд╖реНрдп рдХреЗ рд▓рд┐рдП "рдкреНрд░рддреАрдд" рд╣реЛрддреЗ рд╣реИрдВред рд▓рд╛рд▓рдЪреА рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рд╕реЗ, рдЬреЛ рдкрд╣рд▓реЗ рд╕рд░реНрд╡рд╢реНрд░реЗрд╖реНрда рдореИрдЪ рдХреЗ рд▓рд┐рдП рдЦреЛрдЬ рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рднреА рд╣реИ, рдпрд╣ рдЗрд╕ рддрдереНрдп рд╕реЗ рдкреНрд░рддрд┐рд╖реНрдард┐рдд рд╣реИ рдХрд┐ рдЬрдм рдПрдХ рд╢реАрд░реНрд╖ рдХрд╛ рдЪрдпрди рдХрд░рддреЗ рд╣реИрдВ, рддреЛ рдЗрд╕реЗ рдзреНрдпрд╛рди рдореЗрдВ рд░рдЦрддреЗ рд╣реБрдП, рдЕрдиреНрдп рдЪреАрдЬреЛрдВ рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдкреВрд░реЗ рдкрде рдиреЗ рдЗрд╕реЗ рдпрд╛рддреНрд░рд╛ рдХреАред


рд╡рд┐рдХрд┐рдкреАрдбрд┐рдпрд╛ рдкрд░ A * рдХреЗ рдХрд╛рд░реНрдп рдХрд╛ рджреГрд╢реНрдп



рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди


рдЪреВрдВрдХрд┐ рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рдХреЛ "рдиреЛрдбреНрд╕" рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ - рдкрде рдХрд╛ рдирд┐рд░реНрдзрд╛рд░рдг рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЕрдВрдХ, рд╣рдо рдиреЛрдб рдХреА рд╡рд░реНрдЧ рд╕рдВрд░рдЪрдирд╛ рд▓рд┐рдЦрддреЗ рд╣реИрдВ:


рдиреЛрдб рдХреЛрдб:
public enum EMoveAction { walk, jump, fall, swim }; public class PathPoint { //   public Vector3 point { get; set; } //    public float pathLenghtFromStart { get; set; } //     public float heuristicEstimatePathLenght { get; set; } //     public float estimateFullPathLenght { get { return this.heuristicEstimatePathLenght + this.pathLenghtFromStart; } } //   public EMoveAction moveAction = EMoveAction.walk; //      public PathPoint cameFrom; } 

рдЫреЛрдЯреЗ рд╡рд░реНрдЧ рдХреЗ рдирд┐рд░реНрдорд╛рдгрдХрд░реНрддрд╛:
  private PathPoint NewPathPoint(Vector3 point, float pathLenghtFromStart, float heuristicEstimatePathLenght, EMoveAction moveAction) { PathPoint a = new PathPoint(); a.point = point; a.pathLenghtFromStart = pathLenghtFromStart; a.heuristicEstimatePathLenght = heuristicEstimatePathLenght; a.moveAction = moveAction; return a; } private PathPoint NewPathPoint(Vector3 point, float pathLenghtFromStart, float heuristicEstimatePathLenght, EMoveAction moveAction, PathPoint ppoint) { PathPoint a = new PathPoint(); a.point = point; a.pathLenghtFromStart = pathLenghtFromStart; a.heuristicEstimatePathLenght = heuristicEstimatePathLenght; a.moveAction = moveAction; a.cameFrom = ppoint; return a; } 

рдЕрдЧрд▓рд╛, рдкрде рдЦреЛрдЬ рд╕реЗрдЯрд┐рдВрдЧреНрд╕ рдХреА рд╕рдВрд░рдЪрдирд╛ рдХрд╛рдо рдЖрдПрдЧреА:


рдкрде рдЦреЛрдЬ рд╕реЗрдЯрд┐рдВрдЧ рдХреЛрдб:
  public struct SPathFinderType { //   , , ,   public bool walk, jump, fall, swim; //   ,  public int maxFallDistance, jumpHeight, jumpDistance; //   public int characterHeight; //    public static SPathFinderType normal() { SPathFinderType n = new SPathFinderType(); n.walk = true; n.jump = true; n.fall = true; n.swim = false; n.maxFallDistance = 1; n.jumpHeight = 1; n.jumpDistance = 0; n.characterHeight = 1; return n; } } 

рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, "рд╡рд┐рд╢реНрд╡" рдорд╛рдирдЪрд┐рддреНрд░ рдмреНрд▓реЙрдХреЛрдВ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЬрд╛рдирдХрд╛рд░реА рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдкреНрд░рдХрд╛рд░ рдХрд╛ рдбреЗрдЯрд╛рдмреЗрд╕ рд╡рд░реНрдЧ рд╣реИред рдЖрдкрдХреЗ рд▓рд┐рдП рдЕрд▓рдЧ рддрд░реАрдХреЗ рд╕реЗ рд▓рд╛рдЧреВ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред


рдорд╛рд░реНрдЧ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рдкрде рдЦреЛрдЬ рдХрд╛рд░реНрдп рдХрд╛ рдкрд░рд┐рдгрд╛рдо:
  public List<PathPoint> GetPathToTarget(Vector3 beginPoint, Vector3 targetPoint, World worldData, SPathFinderType pfType) { List<PathPoint> path = new List<PathPoint>(); //     List<PathPoint> openPoints = new List<PathPoint>(); //    List<PathPoint> closedPoints = new List<PathPoint>(); //      openPoints.Add(NewPathPoint(beginPoint, 0, GameLogic.Distance(beginPoint, targetPoint), EMoveAction.walk)); //   closedPoints.Add(openPoints[0]); //       openPoints = ClosePoint(0, openPoints, closedPoints, worldData, pfType, targetPoint); // " "    bool stopFlag = true; //        float maxEstimatePath = 1500; int maxNodes = 6000; while (stopFlag) { //        int minIndex = GetMinEstimate(openPoints); if (openPoints.Count > 0) if (openPoints[minIndex].estimateFullPathLenght < maxEstimatePath) { //   closedPoints.Add(openPoints[minIndex]); //       minIndex openPoints = ClosePoint(minIndex, openPoints, closedPoints, worldData, pfType, targetPoint); } else { //      //       closedPoints.Add(openPoints[minIndex]); openPoints.RemoveAt(minIndex); } //      if (FinishFounded(closedPoints)) { Debug.Log(" !"); path = GetPathToTarget(closedPoints); stopFlag = false; //      } if (openPoints.Count <= 0) stopFlag = false; //       if ((openPoints.Count>= maxNodes) ||(closedPoints.Count>= maxNodes)) stopFlag = false; //      } Debug.Log("Nodes created "+ closedPoints.Count.ToString()); //    DrawPath(openPoints, Color.green, 6f); DrawPath(closedPoints, Color.blue, 6f); DrawPath(path, Color.red, 6f); return path; } 

GetMinEstimate
  //        private int GetMinEstimate(List<PathPoint> points) { int min = 0; for (int i = 0; i < points.Count; i++) { if (points[i].estimateFullPathLenght < points[min].estimateFullPathLenght) min = i; } return min; } 

DrawPath
  //      public void DrawPath(List<PathPoint> points, Color c, float time) { for (int i = 0; i < points.Count; i++) { if (points[i].cameFrom != null) Debug.DrawLine(points[i].point, points[i].cameFrom.point, c, time); } } 

FinishFounded
  //     private bool FinishFounded(List<PathPoint> points) { for (int i = 0; i < points.Count; i++) { if (points[i].heuristicEstimatePathLenght <= 0) return true; } return false; } 

GetPathToTarget
  //        private List<PathPoint> GetPathToTarget(List<PathPoint> points) { List<PathPoint> path = new List<PathPoint>(); int targetIndex = 0; for (int i = 0; i < points.Count; i++) { if (points[i].heuristicEstimatePathLenght <= 0) targetIndex = i; } PathPoint ppoint = new PathPoint(); ppoint = points[targetIndex]; while (ppoint.pathLenghtFromStart > 0) { path.Add(ppoint); ppoint = ppoint.cameFrom; } path.Reverse(); return path; } 

ClosePoint


рдХреНрд▓реЛрдЬрдкреЙрдк рдлрд╝рдВрдХреНрд╢рди рдкреВрд░реА рддрд░рд╣ рд╕реЗ рд╡рд┐рд╢реНрд╡ рд╡рд░реНрдЧ рдХреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдкрд░ рдирд┐рд░реНрднрд░ рдХрд░рддрд╛ рд╣реИ, рдпрд╣ рдЗрд╕рдореЗрдВ рд╕реЗ рдЦреБрд▓реЗ рдмрд┐рдВрджреБрдУрдВ рдХреА рд╕реВрдЪреА рдореЗрдВ рд╕рднреА рд╕рдВрднрд╛рд╡рд┐рдд рдкрде рдЬреЛрдбрд╝рддрд╛ рд╣реИ рдФрд░ рдЗрд╕ рд╕реВрдЪреА рд╕реЗ рд╡рд░реНрддрдорд╛рди рдмрд┐рдВрджреБ рдХреЛ рд╣рдЯрд╛рддрд╛ рд╣реИ (рдЗрд╕реЗ рдмрдВрдж рдХрд░рддрд╛ рд╣реИ)ред рдореИрдВ рдкрд╣рд▓реЗ рдЪрд╛рд░ рджрд┐рд╢рд╛рдУрдВ рдореЗрдВ рдЕрдкрдиреЗ "рд╕рдорд╛рдкрди рдмрд┐рдВрджреБ" рдХрд╛ рдПрдХ рдЙрджрд╛рд╣рд░рдг рджреВрдВрдЧрд╛ред


рдХреЛрдб рдХрд╛ рдмрдбрд╝рд╛ рдвреЗрд░ рдЪреЗрддрд╛рд╡рдиреА
  private List<PathPoint> ClosePoint(int index, List<PathPoint> openPoints, List<PathPoint> closedPoints, World worldData, SPathFinderType pfType, Vector3 targetPoint) { List<PathPoint> newOpenPoints = openPoints; PathPoint lastPoint = openPoints[index]; //     if (pfType.walk) //        if (CanStand(new Vector3(lastPoint.point.x, lastPoint.point.y, lastPoint.point.z), pfType.characterHeight, worldData)) { // --------------------------------------------------------------- //north // /|\ // | //      if (!InList(closedPoints, new Vector3(lastPoint.point.x + 1, lastPoint.point.y, lastPoint.point.z))) //     if (!InList(newOpenPoints, new Vector3(lastPoint.point.x + 1, lastPoint.point.y, lastPoint.point.z))) //     if (CanStand(new Vector3(lastPoint.point.x + 1, lastPoint.point.y, lastPoint.point.z), pfType.characterHeight, worldData)) { newOpenPoints.Add(NewPathPoint(new Vector3(lastPoint.point.x + 1, lastPoint.point.y, lastPoint.point.z) , lastPoint.pathLenghtFromStart + GetTravelCost(new Vector3(lastPoint.point.x + 1, lastPoint.point.y, lastPoint.point.z), worldData, pfType.characterHeight) , GameLogic.Distance(new Vector3(lastPoint.point.x + 1, lastPoint.point.y, lastPoint.point.z), targetPoint) , EMoveAction.walk , lastPoint)); } // south // | // \|/ //      if (!InList(closedPoints, new Vector3(lastPoint.point.x - 1, lastPoint.point.y, lastPoint.point.z))) //     if (!InList(newOpenPoints, new Vector3(lastPoint.point.x - 1, lastPoint.point.y, lastPoint.point.z))) //     if (CanStand(new Vector3(lastPoint.point.x - 1, lastPoint.point.y, lastPoint.point.z), pfType.characterHeight, worldData)) { newOpenPoints.Add(NewPathPoint(new Vector3(lastPoint.point.x - 1, lastPoint.point.y, lastPoint.point.z) , lastPoint.pathLenghtFromStart + GetTravelCost(new Vector3(lastPoint.point.x - 1, lastPoint.point.y, lastPoint.point.z), worldData, pfType.characterHeight) , GameLogic.Distance(new Vector3(lastPoint.point.x - 1, lastPoint.point.y, lastPoint.point.z), targetPoint) , EMoveAction.walk , lastPoint)); } // east // ----> // //      if (!InList(closedPoints, new Vector3(lastPoint.point.x, lastPoint.point.y, lastPoint.point.z + 1))) //     if (!InList(newOpenPoints, new Vector3(lastPoint.point.x, lastPoint.point.y, lastPoint.point.z + 1))) //     if (CanStand(new Vector3(lastPoint.point.x, lastPoint.point.y, lastPoint.point.z + 1), pfType.characterHeight, worldData)) { newOpenPoints.Add(NewPathPoint(new Vector3(lastPoint.point.x, lastPoint.point.y, lastPoint.point.z + 1) , lastPoint.pathLenghtFromStart + GetTravelCost(new Vector3(lastPoint.point.x, lastPoint.point.y, lastPoint.point.z + 1), worldData, pfType.characterHeight) , GameLogic.Distance(new Vector3(lastPoint.point.x, lastPoint.point.y, lastPoint.point.z + 1), targetPoint) , EMoveAction.walk , lastPoint)); } // west // <---- // //      if (!InList(closedPoints, new Vector3(lastPoint.point.x, lastPoint.point.y, lastPoint.point.z - 1))) //     if (!InList(newOpenPoints, new Vector3(lastPoint.point.x, lastPoint.point.y, lastPoint.point.z - 1))) //    if (CanStand(new Vector3(lastPoint.point.x, lastPoint.point.y, lastPoint.point.z - 1), pfType.characterHeight, worldData)) { newOpenPoints.Add(NewPathPoint(new Vector3(lastPoint.point.x, lastPoint.point.y, lastPoint.point.z - 1) , lastPoint.pathLenghtFromStart + GetTravelCost(new Vector3(lastPoint.point.x, lastPoint.point.y, lastPoint.point.z - 1), worldData, pfType.characterHeight) , GameLogic.Distance(new Vector3(lastPoint.point.x, lastPoint.point.y, lastPoint.point.z - 1), targetPoint) , EMoveAction.walk , lastPoint)); } } newOpenPoints.RemoveAt(index); return newOpenPoints; } 

рдЕрдиреБрдХреВрд▓рди


рдмрд╕ рдкреНрд░рд╛рд░рдВрдн рд╕реЗ рд╡рд░реНрддрдорд╛рди рдмрд┐рдВрджреБ рддрдХ рдкрде рдХреЛ рд╡рд┐рднрд╛рдЬрд┐рдд рдХрд░рдХреЗ, рд╣рдо рдХрдИ рдмрд╛рд░ рдиреЛрдбреНрд╕ рдХреА рд╕рдВрдЦреНрдпрд╛ рдХрдо рдХрд░рддреЗ рд╣реИрдВ рдФрд░ рдЗрд╕реЗ рдЕрдзрд┐рдХ рд▓рд╛рд▓рдЪреА рдмрдирд╛рддреЗ рд╣реИрдВред


 return this.heuristicEstimatePathLenght + this.pathLenghtFromStart /2; 

рдкрд░рд┐рдгрд╛рдо


рдкреЗрд╢реЗрд╡рд░реЛрдВ:


  • рдЦреБрд▓реА рдЬрдЧрд╣реЛрдВ рдкрд░ рддреНрд╡рд░рд┐рдд рдЦреЛрдЬред
  • рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рдмрд╣реБрдореБрдЦреА рдкреНрд░рддрд┐рднрд╛

рд╡рд┐рдкрдХреНрд╖:


  • рдорд╛рд░реНрдЧ рдХреА рдЧрдгрдирд╛ рдХреЗ рд▓рд┐рдП рдЗрд╕реЗ рдмрд╣реБрдд рдЕрдзрд┐рдХ рдореЗрдореЛрд░реА рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред

рдЧреНрд░реАрди рдиреЛрдбреНрд╕ рдХреА рдПрдХ рдЦреБрд▓реА рд╕реВрдЪреА рджрд┐рдЦрд╛рддрд╛ рд╣реИ, рд▓рдХреНрд╖реНрдп рдХреЗ рд▓рд┐рдП рд▓рд╛рд▓ рдорд╛рд░реНрдЧ, рдиреАрд▓реЗ рдмрдВрдж рдиреЛрдбреНрд╕ред


рдЕрдиреБрдХреВрд▓рди рд╕реЗ рдкрд╣рд▓реЗ рдкреНрд░рд╛рдкреНрдд рдорд╛рд░реНрдЧ:



рдЕрдиреБрдХреВрд▓рди рдХреЗ рдмрд╛рдж рдкреНрд░рд╛рдкреНрдд рдорд╛рд░реНрдЧ:



рд╕рд╛рд╣рд┐рддреНрдп


https://tproger.ru/articles/pathfindings/
https://ru.wikipedia.org/wiki/A*

Source: https://habr.com/ru/post/hi416737/


All Articles