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

Dynamic Path-finding Script having problems

Discussion in 'Scripting' started by MajorSmurf, Sep 3, 2018.

  1. MajorSmurf

    MajorSmurf

    Joined:
    Feb 1, 2018
    Posts:
    16
    So I was nice and happy and watched my little AI run round the map like happy little camper and than I went oohhh I know lets allow them to switch targets and bam got an error. fixed it. BAM! got another error fixed that one and everything was fine till I created a spawner script and now I've got an error that I just don't know where to start the fix.

    So a little back story I'm using Sebastian Lague code and adapting it to my needs so if you're familiar with his YouTube videos than you might get a better understanding of the error but I'll try and go into as much detail but if I miss something just hit me with an imaginary hammer not a real one.... I rather like my head the shape it is.





    As you can see from the error, It doesn't make alot sense to me as it clearly has a target. This completly breaks it's ability to move and it only affects units that are created/instantiated through the Spawner script shown below as I get no error for AI units that are already in the map. It also clearly shows a target has been selected in the screenshot I have above. So I'm a little confused but maybe I'm missing something obvious like usual. Below is all the scripts that I believe to be connected to this issue.

    Spawner Script

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class SpawnerController : MonoBehaviour
    6. {
    7.     public GameObject enemyPrefab;
    8.     public int maxSpawn;
    9.  
    10.     public float waitForSeconds = 3f;
    11.  
    12.     // Use this for initialization
    13.     void Start ()
    14.     {
    15.         StartCoroutine (Spawn ());
    16.     }
    17.    
    18.     // Update is called once per frame
    19.     void Update ()
    20.     {
    21.        
    22.     }
    23.  
    24.     IEnumerator Spawn()
    25.     {
    26.         for (int i = 1; i <= maxSpawn; i++)
    27.         {
    28.             Debug.Log (maxSpawn);
    29.             var checkResult = Physics.OverlapSphere(gameObject.transform.position, 0.1f);
    30.             if (checkResult.Length == 0)
    31.             {
    32.                 Debug.Log ("no collisions detected");
    33.                 gameObject.transform.position = new Vector3 (Random.Range (0, 10), 0.8f, Random.Range (0, 10));
    34.                 Instantiate (enemyPrefab, gameObject.transform.position, Quaternion.identity);
    35.                 yield return new WaitForSeconds (waitForSeconds);
    36.             }
    37.  
    38.             if (checkResult.Length > 0)
    39.             {
    40.                 Debug.Log (checkResult.Length);
    41.                 gameObject.transform.position = new Vector3 (Random.Range (0, 10), 2, Random.Range (0, 10));
    42.                 yield return new WaitForSeconds (waitForSeconds);
    43.             }
    44.  
    45.             if (i == maxSpawn)
    46.             {
    47.                 StopCoroutine (Spawn ());
    48.             }
    49.         }
    50.         yield return new WaitForSeconds (waitForSeconds);
    51.     }
    52. }
    53.  
    Unit Script

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class Unit : MonoBehaviour
    6. {
    7.     const float minPathUpdateTime = .2f;
    8.     const float pathUpdateMoveThreshold = .5f;
    9.  
    10.     public Transform target;
    11.     public float speed = 5f;
    12.     public float turnDst = 5f;
    13.     public float turnSpeed = 3f;
    14.     public float stoppingDst = 10;
    15.  
    16.     Path path;
    17.  
    18.     TargetSelection targetSelection;
    19.  
    20.     void Start()
    21.     {
    22.         targetSelection = GetComponent<TargetSelection> ();
    23.         StartCoroutine (UpdatePath ());
    24.     }
    25.  
    26.     void Update()
    27.     {
    28.         target = targetSelection.targetSelect.transform;
    29.     }
    30.  
    31.     public void OnPathFound(Vector3[] waypoints, bool pathSuccessful)
    32.     {
    33.         if (pathSuccessful)
    34.         {
    35.             path = new Path(waypoints, transform.position, turnDst, stoppingDst);
    36.             StopCoroutine("FollowPath");
    37.             StartCoroutine("FollowPath");
    38.         }
    39.     }
    40.  
    41.     IEnumerator UpdatePath()
    42.     {
    43.         if (Time.timeSinceLevelLoad < .3f)
    44.         {
    45.             yield return new WaitForSeconds (.3f);
    46.         }
    47.  
    48.         //target = targetSelection.targetSelect.transform;
    49.            
    50.         PathRequestManager.RequestPath (new PathRequest(transform.position, target.position, OnPathFound));
    51.  
    52.         float sqrMoveThreshold = pathUpdateMoveThreshold * pathUpdateMoveThreshold;
    53.         Vector3 targetPosOld = target.position;
    54.  
    55.         while (true)
    56.         {
    57.             yield return new WaitForSeconds (minPathUpdateTime);
    58.             if ((target.position - targetPosOld).sqrMagnitude > sqrMoveThreshold)
    59.             {
    60.                 PathRequestManager.RequestPath (new PathRequest(transform.position, target.position, OnPathFound));
    61.                 targetPosOld = target.position;
    62.             }
    63.         }
    64.     }
    65.  
    66.     IEnumerator FollowPath()
    67.     {
    68.         bool followingPath= true;
    69.         int pathIndex = 0;
    70.         transform.LookAt (path.lookPoints [0]);
    71.  
    72.         float speedPercent = 1;
    73.  
    74.         while (followingPath)
    75.         {
    76.             Vector2 pos2D = new Vector2 (transform.position.x, transform.position.z);
    77.             while (path.turnBoundaries [pathIndex].HasCrossedLine (pos2D))
    78.             {
    79.                 if (pathIndex == path.finishLineIndex)
    80.                 {
    81.                     followingPath = false;
    82.                     break;
    83.                 }
    84.                 else
    85.                 {
    86.                     pathIndex++;
    87.                 }
    88.             }
    89.  
    90.             if (followingPath)
    91.             {
    92.                 if (pathIndex >= path.slowDownIndex && stoppingDst > 0)
    93.                 {
    94.                     speedPercent = Mathf.Clamp01 (path.turnBoundaries [path.finishLineIndex].DistanceFromPoint (pos2D) / stoppingDst);
    95.                     if (speedPercent < 0.01f)
    96.                     {
    97.                         followingPath = false;
    98.                     }
    99.                 }
    100.  
    101.  
    102.                 Quaternion targetRotation = Quaternion.LookRotation (path.lookPoints [pathIndex] - transform.position);
    103.                 transform.rotation = Quaternion.Lerp (transform.rotation, targetRotation, Time.deltaTime * turnSpeed);
    104.                 transform.Translate (Vector3.forward * Time.deltaTime * speed * speedPercent, Space.Self);
    105.             }
    106.             yield return null;
    107.         }
    108.     }
    109.  
    110.     public void OnDrawGizmos()
    111.     {
    112.         if (path != null)
    113.         {
    114.             path.DrawWithGizmos ();
    115.         }
    116.     }
    117. }
    118.  
    Target Selection
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using UnityEngine;
    5.  
    6. public class TargetSelection : MonoBehaviour
    7. {
    8.     public Transform targetSelect;
    9.     public Transform startTarget;
    10.     public Transform player;
    11.  
    12.     private GameObject playerObject;
    13.     private GameObject startObject;
    14.     public GameObject closestTarget;
    15.     public float aggroRange;
    16.  
    17.     void Start()
    18.     {
    19.         startObject = GameObject.FindGameObjectWithTag ("patrol");
    20.         playerObject = GameObject.FindGameObjectWithTag ("player");
    21.         player = (Transform)playerObject.transform;
    22.         startTarget = (Transform)startObject.transform;
    23.         targetSelect = startTarget.transform;
    24.     }
    25.     void Update ()
    26.     {
    27.         if (targetSelect == null)
    28.         {
    29.             targetSelect = startTarget.transform;
    30.         }
    31.  
    32.         if (targetSelect != null)
    33.         {
    34.             if (Vector3.Distance (transform.position, player.transform.position) <= aggroRange)
    35.             {
    36.                 targetSelect = player.transform;
    37.             }
    38.  
    39.             if (Vector3.Distance (transform.position, player.transform.position) > aggroRange)
    40.             {
    41.                 closestTarget = (from xp in GameObject.FindGameObjectsWithTag ("xp")
    42.                                 orderby Vector3.Distance(transform.position, xp.transform.position)
    43.                                 select xp).FirstOrDefault ();
    44.  
    45.                 targetSelect = (Transform)closestTarget.transform;
    46.  
    47.                 if (targetSelect == null)
    48.                 {
    49.                     targetSelect = startTarget.transform;
    50.                 }
    51.  
    52.             }
    53.         }
    54.     }
    55. }
    56.  
    I know this is a lot of information/code to look at and some of the stuff most likely will trigger you to no end and for that I apologize. For any coding veterans looking at this I recommend closing your eyes but in all seriously I just need some help tracking the issue I can only give my thanks for any help and I'll try and answer any additional questions.

    Thankfully
    MajorSmurf

    P.s Can't stress enough how thankful I will be to any help. so thank you again in advance :)
     
  2. CubicCBridger

    CubicCBridger

    Joined:
    Apr 19, 2017
    Posts:
    44
    try sprinkling through debug.log's throughout the scripts just before where the errors are happening.

    e.g

    Code (CSharp):
    1. Debug.Log($"{this.name} in routine X with field Y set to {Y}")
    This will help you narrow down what is happening (alternatively/additionally set break points and step through the code)
     
  3. MajorSmurf

    MajorSmurf

    Joined:
    Feb 1, 2018
    Posts:
    16
    Ok so I'm not sure if this is the problem but it just hit me that the Coroutine isn't waiting the 3 seconds so the for loop is spawning 3 prefabs at the same time which i think might be breaking parts of the pathfinding as it can only request pathfinding every .3f of a second so by having two or more call for it. I think its breaking due to this. So any ideas why my Coroutine isn't working on my spawner script shown above.

    Ill keep checking to see if i spot anything else through debugs but this is the only thing I've spotted so far. and some of the other errors seem to be unity engine script errors which isn't a great sign.
     
  4. CubicCBridger

    CubicCBridger

    Joined:
    Apr 19, 2017
    Posts:
    44
    And you've definitely only got one spawner? You could have a static int which you increment by one in the start method, and log it in the debug log to check you always only have one to check you aren't spawning an entity that has its own spawner script. Also I'm pretty sure once your for loop in your coroutine finishes you don't need the stop coroutine command or the yield return, it will automatically stop as there is no more code to execute.
     
  5. MajorSmurf

    MajorSmurf

    Joined:
    Feb 1, 2018
    Posts:
    16
    Yep I only have one but I think my coding for my Spawner is a bit un-optimized for what I want. I want it to do the following. Check how many AI are on the field and when one dies it will spawn another in a random location on the map. I also wanted it so that the for loop plays once than waits 3 seconds and moves location before spawning another avoid a fps drop etc when spawning in an enemy. But the coroutine doesn't stop so am I using yield return new WaitforSeconds wrong cause I checked other couroutines I've made and they work fine.

    For my other issue of the pathfinding would it easier to use a foreach loop to search for each gameobject instead of ordering them via system.linq. I only did this because i thought it would cleaner and would avoid long code each time the for loop plays. But maybe a foreach loop is easier and might fix my problems once I get the Spawner script working.
     
  6. MajorSmurf

    MajorSmurf

    Joined:
    Feb 1, 2018
    Posts:
    16
    Ok so I am 100% sure that my pathfinding only breaks on objects spawned in through the spawner script which is confusing to me. If i just place an enemy on the map it will work fine. It will find the closest target so now my question is rather simple what changes a prefab from being different to an object that exists at the start of the game running. So I can only assume its something to do with the way prefabs are handled rather than my code being completely wrong. Which is annoying :L. Any ideas on what makes a prefab so different?
     
  7. MajorSmurf

    MajorSmurf

    Joined:
    Feb 1, 2018
    Posts:
    16
    Ok so the spawner script has now started waiting the three seconds but I've figured out the problem for the enemy AI is the fact is that they hit into the xp target and destroy it on contact but instead of finding the path to the next closest they get stuck because for some reason they lock onto a single target and refuse to update the target even though in the editor it says which target they are after...... I've found myself in a right mess xD. At this point it might be easier to show a small video of whats happening to help show the problem cause it gets difficult to explain in words :3 sorry for wasting your time but its hard to nail this one on the head cause it only affects prefabs... If i just duplicate them on the map its fine but spawning them in after the game starts seems to break them after they pathfind to the first one. so its not so much the pathfinding breaking it's just locked into a single location.
     
  8. MajorSmurf

    MajorSmurf

    Joined:
    Feb 1, 2018
    Posts:
    16
    OK so i finally fixed it thanks for helping :3
     
  9. CubicCBridger

    CubicCBridger

    Joined:
    Apr 19, 2017
    Posts:
    44
    Haha I didn't do much, maybe you could post what you did to fix it in case anyone in the future has a a similar problem?
     
  10. MajorSmurf

    MajorSmurf

    Joined:
    Feb 1, 2018
    Posts:
    16
    and let all my hard work go to waste never! :3..... nah I'll post it. Its rather simple you know which makes me angry to no end but at the same time I laugh like madman.

    updated Scripts:

    TargetSelection

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using UnityEngine;
    5.  
    6. public class TargetSelection : MonoBehaviour
    7. {
    8.     public Transform targetSelect;
    9.     public Transform startTarget;
    10.     public Transform player;
    11.  
    12.     private GameObject playerObject;
    13.     private GameObject startObject;
    14.     public GameObject closestTarget;
    15.     public float aggroRange;
    16.  
    17.     void OnEnable()
    18.     {
    19.         startObject = GameObject.FindGameObjectWithTag ("patrol");
    20.         playerObject = GameObject.FindGameObjectWithTag ("player");
    21.         player = (Transform)playerObject.transform;
    22.         startTarget = (Transform)startObject.transform;
    23.         targetSelect = startTarget.transform;
    24.     }
    25.     void Update ()
    26.     {
    27.         if (targetSelect == null)
    28.         {
    29.             gameObject.GetComponent<Unit> ().TargetSelected (startTarget);
    30.         }
    31.  
    32.         if (targetSelect != null)
    33.         {
    34.             if (Vector3.Distance (transform.position, player.transform.position) <= aggroRange)
    35.             {
    36.                 targetSelect = player.transform;
    37.                 gameObject.GetComponent<Unit> ().TargetSelected (targetSelect);
    38.             }
    39.  
    40.             if (Vector3.Distance (transform.position, player.transform.position) > aggroRange)
    41.             {
    42.                 closestTarget = (from xp in GameObject.FindGameObjectsWithTag ("xp")
    43.                                 orderby Vector3.Distance(transform.position, xp.transform.position)
    44.                                 select xp).FirstOrDefault ();
    45.  
    46.                 if (closestTarget != null)
    47.                 {
    48.                     targetSelect = (Transform)closestTarget.transform;
    49.                     gameObject.GetComponent<Unit> ().TargetSelected (targetSelect);
    50.                 }
    51.  
    52.                 if (closestTarget == null)
    53.                 {
    54.                     targetSelect = startTarget.transform;
    55.                     gameObject.GetComponent<Unit> ().TargetSelected (targetSelect);
    56.                 }
    57.             }
    58.         }
    59.     }
    60. }
    61.  
    Unit Script

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class Unit : MonoBehaviour
    6. {
    7.     const float minPathUpdateTime = .2f;
    8.     const float pathUpdateMoveThreshold = .5f;
    9.  
    10.     public Transform target;
    11.     public float speed = 5f;
    12.     public float turnDst = 5f;
    13.     public float turnSpeed = 3f;
    14.     public float stoppingDst = 10;
    15.  
    16.     Path path;
    17.  
    18.     TargetSelection targetSelection;
    19.  
    20.     void Start()
    21.     {
    22.         targetSelection = GetComponent<TargetSelection> ();
    23.         target = targetSelection.startTarget;
    24.         StartCoroutine (UpdatePath ());
    25.     }
    26.        
    27.     public void TargetSelected(Transform _target)
    28.     {
    29.         this.target = _target;
    30.     }
    31.  
    32.     public void OnPathFound(Vector3[] waypoints, bool pathSuccessful)
    33.     {
    34.         if (pathSuccessful)
    35.         {
    36.             path = new Path(waypoints, transform.position, turnDst, stoppingDst);
    37.             StopCoroutine("FollowPath");
    38.             StartCoroutine("FollowPath");
    39.         }
    40.     }
    41.  
    42.     IEnumerator UpdatePath()
    43.     {
    44.         if (Time.timeSinceLevelLoad < .3f)
    45.         {
    46.             yield return new WaitForSeconds (.3f);
    47.         }
    48.  
    49.         //target = targetSelection.targetSelect.transform;
    50.            
    51.         PathRequestManager.RequestPath (new PathRequest(transform.position, target.position, OnPathFound));
    52.  
    53.         float sqrMoveThreshold = pathUpdateMoveThreshold * pathUpdateMoveThreshold;
    54.         Vector3 targetPosOld = target.position;
    55.  
    56.         while (true)
    57.         {
    58.             yield return new WaitForSeconds (minPathUpdateTime);
    59.             if ((target.position - targetPosOld).sqrMagnitude > sqrMoveThreshold)
    60.             {
    61.                 PathRequestManager.RequestPath (new PathRequest(transform.position, target.position, OnPathFound));
    62.                 targetPosOld = target.position;
    63.             }
    64.         }
    65.     }
    66.  
    67.     IEnumerator FollowPath()
    68.     {
    69.         bool followingPath= true;
    70.         int pathIndex = 0;
    71.         transform.LookAt (path.lookPoints [0]);
    72.  
    73.         float speedPercent = 1;
    74.  
    75.         while (followingPath)
    76.         {
    77.             Vector2 pos2D = new Vector2 (transform.position.x, transform.position.z);
    78.             while (path.turnBoundaries [pathIndex].HasCrossedLine (pos2D))
    79.             {
    80.                 if (pathIndex == path.finishLineIndex)
    81.                 {
    82.                     followingPath = false;
    83.                     break;
    84.                 }
    85.                 else
    86.                 {
    87.                     pathIndex++;
    88.                 }
    89.             }
    90.  
    91.             if (followingPath)
    92.             {
    93.                 if (pathIndex >= path.slowDownIndex && stoppingDst > 0)
    94.                 {
    95.                     speedPercent = Mathf.Clamp01 (path.turnBoundaries [path.finishLineIndex].DistanceFromPoint (pos2D) / stoppingDst);
    96.                     if (speedPercent < 0.01f)
    97.                     {
    98.                         followingPath = false;
    99.                     }
    100.                 }
    101.  
    102.  
    103.                 Quaternion targetRotation = Quaternion.LookRotation (path.lookPoints [pathIndex] - transform.position);
    104.                 transform.rotation = Quaternion.Lerp (transform.rotation, targetRotation, Time.deltaTime * turnSpeed);
    105.                 transform.Translate (Vector3.forward * Time.deltaTime * speed * speedPercent, Space.Self);
    106.             }
    107.             yield return null;
    108.         }
    109.     }
    110.  
    111.     public void OnDrawGizmos()
    112.     {
    113.         if (path != null)
    114.         {
    115.             path.DrawWithGizmos ();
    116.         }
    117.     }
    118. }
    119.  
    So the fix was simple. The unit script was loading in with no target and attempting to run through with no target but this causes the script to break so all that was required was to give it a target to start walking towards but requesting a path to another object. I added in a new public void which takes in target from the targetselection because this looks nicer than what I had in the older script.