- Home /
You are going to need a new approach. The code is very intensive and has multiple instances of recursion as well as too many ways it can break. Try splitting it up in to smaller chunks. Simplify!
Path finding freezes Unity
So I am trying to create a path finding code for a turn-based game in a grid environment, I rewrote my code a lot of times in different ways, but it always ends freezing Unity, and I can't find out whats wrong.
private void Move (GameObject t, GameObject c)
{
Debug.Log("Cheking at " + c.name);
if (t.gameObject.GetComponent<TileProps> ().walkable && !t.GetComponent<TileProps>().occupied) {
Collider[] nT = Physics.OverlapSphere(c.transform.position, tileSize);
List<GameObject> neighbors = new List<GameObject>();
foreach(Collider z in nT){
neighbors.Add(z.gameObject);
}
for(int i = 0; i < neighbors.Count; i++){
Debug.Log(neighbors[i].name);
if(!neighbors[i].GetComponent<TileProps>().walkable || neighbors[i].GetComponent<TileProps>().occupied || checkedTiles.Contains(neighbors[i])){
neighbors.Remove(neighbors[i]);
}
}
if(neighbors.Count > 0){
for(int i = 0; i < neighbors.Count; i++){
checkedTiles.Add(neighbors[i]);
tempList.Add(neighbors[i]);
if(neighbors[i] != t){
Debug.Log("Looping...");
Move(t, neighbors[i]);
}else{
if(tempList.Count < numOfTiles){
tilesToMove = tempList;
numOfTiles = tempList.Count;
tempList.RemoveRange(0, tempList.Count);
}
if(checkedTiles.Count == mapM.GetComponent<MapManager>().tiles.Count){
checkedTiles.RemoveRange(0, checkedTiles.Count);
Debug.Log("Path Calculated");
if(numOfTiles <= actionP){
StartCoroutine(Move2());
}else{
Debug.Log("Not enough action points.");
}
}
}
}
}else{
Debug.Log("Target is not walkable");
return;
}
}
}
Yes, I did many times, but I still don't know why the loop never ends
$$anonymous$$ove2() is the function that moves the character, I didn't included it in the question because I already tested it before and it worked without any problems, here's the code:
private IEnumerator $$anonymous$$ove2 ()
{
for (int y = 0; y < tilesTo$$anonymous$$ove.Count; y++) {
while (transform.position != tilesTo$$anonymous$$ove[y].transform.position) {
if (Vector3.Distance (transform.position, tilesTo$$anonymous$$ove [y].transform.position) < 0.2f && Vector3.Distance (transform.position, tilesTo$$anonymous$$ove [y].transform.position) != 0) {
transform.position = tilesTo$$anonymous$$ove [y].transform.position;
} else {
transform.position += (tilesTo$$anonymous$$ove [y].transform.position - transform.position).normalized * speed;
}
yield return new WaitForSeconds (0.01f);
}
}
actionP -= tilesTo$$anonymous$$ove.Count;
tilesTo$$anonymous$$ove.RemoveRange (0, tilesTo$$anonymous$$ove.Count);
}
It contains a while loop inside a for loop (thats quadruple nested inside a for loop of undefined size thats double nested).
Its relevant :P
Have you worked out how long each branch takes to execute?
What happens if after the yield numOfTiles > actionP or any of the other conditions to get to the coroutine fails? $$anonymous$$ove2 would never return, would it?
Answer by Bunny83 · Jul 23, 2015 at 08:14 AM
When your path is calculated and you start "StartCoroutine(Move2());" shouldn't you abort your Move method? A "break" or "return" would be appropriate once you started the coroutine. Keep in mind since you use a recursive approach once you exit the innermost "Move" method you get back to the point where you called that one in line 36. Since you're done with your path calculation you should make sure that all Move methods exit as soon as possible.
edit
Furthermore there are several things strange about your approach:
You use Physics.OverlapSphere which will include your current tile as well. You did not add the current tile to the checked list and didn't exclude it from the neighbors list as well. Since you call "Move" again for every "neighbor" you check the first tile twice.
Using OverlapSphere for every step in your pathfinding is very inefficient. You should store the immediate neighbor list inside your "TileProps" class. This information is usually static. You only need to set it up once at start of the game.
Unlike algorithms like "A*" you seem to search randomly for a path. You have alot strange abort conditions like one of your finish conditions is that the checkedtile count == the count of all available tiles.
Btw: There is "List.Clear()" ^^
Once your checkedTiles count hits the available tile count you clear the checkedTiles list, either start the actual moving or not but you continoue searching once you started Move2. Since you cleared the checkedTiles list every tile can be checked again. This will most likely continoue forever.
To sum this up: It's quite a mess ^^. Recursive approaches are dangerous and you easily loose track of the chain of execution. Even "A*" is usually implemented with a sorted queue. That doesn't prevent an infinite loop, but it's more obvious what the algorithm does and when it terminates (or not).
Keep in mind that you can use MonoDevelop to debug your Unity application. You can set breakpoints and step through your method and inspect the variables to see where it goes wrong.
Well, guess I will try to implement A* then, thanks for the help.
Answer by PAHeartBeat · Jul 23, 2015 at 10:33 AM
in lines 15-25 i think you need to run reverce for loop to remove elements from list. so it will populate correct list in case any item removed from the list. it should be like that
for(int i = neighbors.Count-1,i>=0; i--){
Debug.Log(neighbors[i].name);
if(!neighbors[i].GetComponent<TileProps>().walkable || neighbors[i].GetComponent<TileProps>().occupied || checkedTiles.Contains(neighbors[i])){
neighbors.Remove(neighbors[i]);
}
}
Secondly if you tring to use a* path finding style for your game you need to check these link https://tbswithunity3d.wordpress.com/2012/02/23/hexagonal-grid-path-finding-using-a-algorithm/ http://ledpup.blogspot.in/2011/06/unity-hexagons-and-path-finding.html
these links about a* path finding in hex grid but you can also use it nomral square grid via little bit change Tile
Class. Note: this scripts are not optimized or follw ART runtime. if you need to use your code iOS device you also need to remove uses of genrics and use specfic type in method FindPath
of Path
Class and Enqueue
, Dequeue
methods from PriorityQueue
Class
Happy coding bro,