Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

List RemoveAt cause unity freeze

Discussion in 'Scripting' started by Xhitman, Apr 13, 2018.

  1. Xhitman

    Xhitman

    Joined:
    Oct 30, 2015
    Posts:
    452
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class PathFinding
    6. {
    7.     // move from A to B
    8.     public static List<Vector2> Find(int[,] tileMap, Vector2 A, Vector2 B)
    9.     {      
    10.         List<Vector2> pathTileOpen = new List<Vector2>();
    11.         List<Vector2> pathTileOpenBackTrace = new List<Vector2>();   // for back trace
    12.         List<int> pathTileOpenBackTraceScore = new List<int>();
    13.         List<int> pathTileOpenScore = new List<int>();
    14.         List<Vector2> pathTileClose = new List<Vector2>();
    15.        
    16.         pathTileOpenBackTrace.Clear();
    17.         pathTileOpenBackTraceScore.Clear();
    18.         pathTileOpen.Clear();
    19.         pathTileClose.Clear();
    20.                
    21.         Vector2 currentTile = A;      
    22.         List<Vector2> adjacentTile = new List<Vector2>();      
    23.  
    24.         int score;
    25.         int lowestScoreIndex;
    26.  
    27.         // First, build up the path tile that can arrive destination
    28.  
    29.         do
    30.         {      
    31.             adjacentTile.Clear();
    32.  
    33.             // add adjacent tile to pathTileOpen          
    34.             // 4 adjacent tile to current tile
    35.             // Make sure the adjacent tile is not outside the titleMap                            
    36.  
    37.             if (currentTile.x < tileMap.GetLength(0))
    38.                 adjacentTile.Add(new Vector2(currentTile.x + 1, currentTile.y));
    39.             if (currentTile.x >0)
    40.                 adjacentTile.Add(new Vector2(currentTile.x - 1, currentTile.y));
    41.             if(currentTile.y<tileMap.GetLength(1))
    42.                 adjacentTile.Add(new Vector2(currentTile.x, currentTile.y + 1));
    43.             if (currentTile.y >0)
    44.                 adjacentTile.Add(new Vector2(currentTile.x, currentTile.y - 1));
    45.                        
    46.  
    47.             // if adjacent tile include destination, break loop
    48.  
    49.             if (adjacentTile.Contains(B))
    50.                 break;
    51.  
    52.             // remove non-walkable tile
    53.             for (int i = 0; i< adjacentTile.Count ; i++)
    54.                 if (tileMap[(int)adjacentTile[i].x, (int)adjacentTile[i].y] != (int)MapTileValue.Empty)
    55.                 {
    56.                     Debug.Log(adjacentTile[i]);
    57.                     adjacentTile.RemoveAt(i);
    58.                     i--;
    59.                 }
    60.                        
    61.  
    62.             // adjacent title that repeat with path tile will be removed and not consider                      
    63.             for (int i = adjacentTile.Count - 1; i >= 0; i--)
    64.                 if (pathTileClose.Contains(adjacentTile[i]))              
    65.                     adjacentTile.RemoveAt(i);                  
    66.                
    67.  
    68.             for (int i = adjacentTile.Count - 1; i >= 0; i--)
    69.             {
    70.                 if (!pathTileOpen.Contains(adjacentTile[i]) && adjacentTile[i]!=A) // dont take point A as adjacent tile
    71.                 {
    72.                     pathTileOpen.Add(adjacentTile[i]);
    73.                     pathTileOpenBackTrace.Add(adjacentTile[i]);
    74.  
    75.                     // Caculate Score for each adjacent tile
    76.                     // Score = Movement Cost + Estimated Movement Cost
    77.  
    78.                     score = (int)(Mathf.Abs(adjacentTile[i].x - A.x) + Mathf.Abs(adjacentTile[i].y - A.y)) + (int)(Mathf.Abs(adjacentTile[i].x - B.x) + Mathf.Abs(adjacentTile[i].y - B.y));
    79.  
    80.                     pathTileOpenScore.Add(score);
    81.                     pathTileOpenBackTraceScore.Add(score);
    82.                 }
    83.  
    84.             }
    85.  
    86.             lowestScoreIndex = LowestScoreIndex(pathTileOpenScore);        
    87.  
    88.             currentTile = pathTileOpen[lowestScoreIndex];    
    89.  
    90.             // add lowest score to pathTileClose
    91.  
    92.             pathTileClose.Add(currentTile);      
    93.  
    94.             // remove lowest score tile from pathTileOpen
    95.  
    96.             pathTileOpen.RemoveAt(lowestScoreIndex);
    97.             pathTileOpenScore.RemoveAt(lowestScoreIndex);
    98.  
    99.         } while (pathTileOpen.Count !=0);
    100.  
    101.  
    102.         // Second, use the open path for back trace from destination to start point
    103.  
    104.         return BackTrace(pathTileOpenBackTrace, pathTileOpenBackTraceScore, B, A);
    105.                
    106.        
    107.     }
    108.  
    109.     // back trace B to A in the exisitng path tile close
    110.     static List<Vector2> BackTrace(List<Vector2> openTile, List<int> score, Vector2 B, Vector2 A)
    111.     {
    112.         List<Vector2> backTraceResult = new List<Vector2>();
    113.         Vector2 currentTile;
    114.  
    115.         List<Vector2> adjacentTile = new List<Vector2>();
    116.         int lowestEstimatedCost, lowestEstimatedCostIndex;
    117.  
    118.         currentTile = B;
    119.        
    120.         do
    121.         {
    122.  
    123.             // Get 4 adjacent Tile, tile boundary dont matter because the tile out of the edge will never be the ptc member
    124.  
    125.             adjacentTile.Clear();
    126.  
    127.             adjacentTile.Add(new Vector2(currentTile.x + 1, currentTile.y));
    128.             adjacentTile.Add(new Vector2(currentTile.x - 1, currentTile.y));
    129.             adjacentTile.Add(new Vector2(currentTile.x, currentTile.y + 1));
    130.             adjacentTile.Add(new Vector2(currentTile.x, currentTile.y - 1));      
    131.  
    132.             if (adjacentTile.Contains(A))
    133.                 return backTraceResult;
    134.  
    135.             // remove tile not in the open tile list          
    136.             for (int i = adjacentTile.Count - 1; i >= 0; i--)
    137.                 if (!openTile.Contains(adjacentTile[i]))
    138.                 {
    139.                     adjacentTile.RemoveAt(i);
    140.                     i = adjacentTile.Count;
    141.                 }
    142.  
    143.                     // Set a value for lowest estimated cost, so it can used to compare in loop
    144.                     // only use the estimated cost, because there will be problem if use the total score
    145.  
    146.                     lowestEstimatedCost = (int)(Mathf.Abs(adjacentTile[0].x - A.x) + Mathf.Abs(adjacentTile[0].y - A.y));
    147.             lowestEstimatedCostIndex = 0;
    148.                        
    149.             for (int i = adjacentTile.Count - 1; i >= 0; i--)
    150.             {
    151.                 if (lowestEstimatedCost > (int)(Mathf.Abs(adjacentTile[i].x - A.x) + Mathf.Abs(adjacentTile[i].y - A.y)))
    152.                     lowestEstimatedCostIndex = i;
    153.             }
    154.  
    155.             backTraceResult.Add(adjacentTile[lowestEstimatedCostIndex]);
    156.  
    157.             currentTile = adjacentTile[lowestEstimatedCostIndex];          
    158.            
    159.         } while (true);
    160.  
    161.         //return backTraceResult;
    162.  
    163.     }
    164.  
    165.     static int LowestScoreIndex(List<int> score)
    166.     {
    167.         // Use the final open list title to start comparison
    168.         int lowest = score[score.Count-1];
    169.         int lowestIndex =score.Count-1;
    170.  
    171.         //Debug.Log(lowestIndex);
    172.         //Debug.Log(lowest);
    173.  
    174.         for(int i=score.Count; i>0 ;i--)
    175.         {
    176.  
    177.         //   Debug.Log(i-1);
    178.    
    179.             if (lowest > score[i-1])
    180.             {
    181.                 lowest = score[i-1];
    182.                 lowestIndex = i-1;
    183.             }
    184.         }
    185.  
    186.         return lowestIndex;
    187.  
    188.     }
    189. }
    190.  
    I know the error come from " adjacentTile.RemoveAt(i)", because no error when i delete it.


    Code (CSharp):
    1.   // remove non-walkable tile
    2.             for (int i = 0; i< adjacentTile.Count ; i++)
    3.                 if (tileMap[(int)adjacentTile[i].x, (int)adjacentTile[i].y] != (int)MapTileValue.Empty)
    4.                 {
    5.                     Debug.Log(adjacentTile[i]);
    6.                     adjacentTile.RemoveAt(i);
    7.                     i--;
    8.                 }
     
  2. Schneider21

    Schneider21

    Joined:
    Feb 6, 2014
    Posts:
    3,510
    Check your if conditional in that second block of code you shared. If not there, check all your other loops using Debug.Log() or break points. The problem is likely not with RemoveAt, but rather your loops are never finishing, which locks up the process.
     
  3. CubicCBridger

    CubicCBridger

    Joined:
    Apr 19, 2017
    Posts:
    44

    1. Code (CSharp):
      1. for (int i = 0; i< adjacentTile.Count ; i++)
      2.     if (tileMap[(int)adjacentTile[i].x, (int)adjacentTile[i].y] != (int)MapTileValue.Empty)
      3.      {
      4.          Debug.Log(adjacentTile[i]);
      5.          adjacentTile.RemoveAt(i);
      6.           i--;
      7.      }
      the for loop is incrementing the index 'i' while you are decrementing it. This is probably causing the for loop to go forever. You probably shouldn't be changing the value of i from within the for loop
     
    Schneider21 likes this.
  4. Xhitman

    Xhitman

    Joined:
    Oct 30, 2015
    Posts:
    452
    adjacentTile always start at 4, and only 2 tiles value is not empty in this 50x50 tile map

    I dont understand how it can become a endless loop.
     
  5. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,711
    Place a breakpoint inside the loop, attach the debugger and run it. Then you can see if it really is exiting or not.

    Also, a very important point about Unity3D: if you call Debug.Log(), you will not see it if you never exit the loop. The editor window is updated on the same thread that runs your scripts, so you won't see anything if your code locks up.
     
    Schneider21 likes this.
  6. bobisgod234

    bobisgod234

    Joined:
    Nov 15, 2016
    Posts:
    1,042
    Attach Visual Studio, then run the project. When it freezes, pause execution ("Break All"), and it should take you straight to the problematic loop. You can then step through the code and inspect the local variables, which should help diagnose the problem.
     
  7. Xhitman

    Xhitman

    Joined:
    Oct 30, 2015
    Posts:
    452

    No compatible code running

    I think the problem is not removeAt, because studio can still continue after running the final removeAt code(I count the number of debug.log by delete removeAt in success run )

     
    Last edited: Apr 13, 2018
  8. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,711
    Put a break; statement at the end of the loop to make it terminate early. I think you will find the lockup goes away.