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

Enemies getting stuck on wall when using MoveTowards

Discussion in 'Scripting' started by Oir_the_tentacular, Dec 29, 2021.

  1. Oir_the_tentacular

    Oir_the_tentacular

    Joined:
    Nov 26, 2021
    Posts:
    76
    Hello all!

    I have some basic enemy wandering commands which has my enemies pick a random coordinate within 1 unit of them every second, walk towards it, wait a bit, then pick a new coordinate and walk towards it and repeat.

    This works perfectly except for when the enemy happens to RNG a coordinate that is behind a wall/collider. This is because my MoveTowards command is inside a while loop that has them continuously moving towards their randomly chosen vector until they get there. This means that if the enemy cannot get to their randomly chosen location, they will forever be in the while loop, walking towards their unreachable destination.

    The choosing of a random vector within 1 unit and the movement are inside a Wander() coroutine. Wander() is in void update() and is called whenever the player is not in the enemy's chase radius.

    To solve this issue of the enemies getting stuck, I attempted to use OnCollisionEnter2D StopCoroutine(Wander()). I figured this would stop the enemy from moving, then the void update would start up Wander() once again which would give the enemy a new random Vector which would hopefully lead them away from the wall. If not, it would simply stop the coroutine again and start it until a Vector leading away from the wall was chosen.

    However, this causes some strange behaviour. Sometimes, it works perfectly. Sometimes, the enemies hit the wall, stop, and do not start wandering again (seems like Wander() is no longer being called). I'm not sure if this is because the enemy is still colliding with the wall when they stop moving which makes the Coroutine keep being stopped via my OnCollisionEnter2D StopCoroutine(Wander()) or if it's something else.

    Here is the code in question:

    Code (CSharp):
    1.     void Update()
    2.     {
    3.         CheckDistance();
    4.     }
    5.  
    6.  
    7.     void CheckDistance()
    8.     {
    9.         if (Vector3.Distance(target.position, transform.position) >= chaseRadius)
    10.         {
    11.             if (!isWandering) //prevent coroutine from being called multiple times
    12.             {
    13.                 StartCoroutine(Wander());
    14.             }
    15.         }
    16.     }
    17.  
    18.  
    19.     IEnumerator Wander()
    20.     {
    21.         isWandering = true;
    22.  
    23.         yield return new WaitForSeconds(1);
    24.  
    25.         wanderDestination = Random.insideUnitCircle * wanderRadius;
    26.         //pick a random vector within 1 unit
    27.         initialPosition = transform.position;
    28.  
    29.         while(transform.position != initialPosition + wanderDestination)
    30.         //keep moving until enemy reaches randomly chosen destination
    31.         {
    32.             this.spriteRenderer = this.GetComponent<SpriteRenderer>();
    33.             this.spriteRenderer.flipX = (initialPosition + wanderDestination).x > this.transform.position.x;
    34.             //flip sprite to match direction enemy is wandering towards
    35.  
    36.             anim.SetBool("Move", true); //play walking animation loop
    37.             transform.position = Vector3.MoveTowards(transform.position, initialPosition + wanderDestination, moveSpeed * Time.deltaTime);
    38.             //move towards randomly chosen destination within 1 unit
    39.             yield return null;
    40.         }
    41.         anim.SetBool("Move", false);
    42.         isWandering = false;
    43.         //stop movement animation and allow coroutine to be called again, leading to a new vector being chosen.
    44.     }
    45.  
    46.     private void OnCollisionEnter2D(Collision2D collision)
    47.     {
    48.         StopCoroutine(Wander());
    49.         isWandering = false;
    50.         // stop wander coroutine when enemy hits a collider. Obviously not working but the hope
    51.         // was that it would allow the wander coroutine to be called again so it could pick a new
    52.         // random destination, hopefully leading away from the wall.
    53.     }
    And here's perhaps the weirdest part. The enemies who are nowhere even near a wall sometimes just get stuck in their while loops as well, as if they're forever trying to reach their destination even when there's nothing blocking their way.

    Would appreciate some insight as to what I've done wrong. Once again, the 2 main problems are:

    1. Enemies will forever be stuck moving towards their wander destination if it's behind a collider and they can't reach it (i.e. I need a way to "reset" and pick a new destination when the enemy hits a collider).

    2. Enemies will sometimes just get stuck in the while loop as if they can't reach their destination, even when there are no obstructions.

    Thank you very much for your time!

    UPDATE:

    Issue number 2 is solved by making the while loop broken as long as the enemy gets close to the destination as opposed to exactly the destination (seems like an issue with unity's ability to move the enemy to the EXACT correct location down to the 5th decimal, so just break the while loop as long as the enemy gets within 0.1 units or something).

    Still getting some weird behaviour when enemies bump into walls such as suddenly speeding up, changing direction, and otherwise moving erratically but at least they're not getting stuck anymore :l
     
    Last edited: Dec 29, 2021
  2. adehm

    adehm

    Joined:
    May 3, 2017
    Posts:
    369
    You can draw a line, Physics.Linecast(), from the center of the enemy to above the desired move position and if it return true find another position as you would know there is a wall. You could even use it to test directions in order to find a spot around the wall to walk and still go to the destination.
     
  3. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,529
    Rule#1: If it moves in 2D physics, move the Rigidbody2D using it's API.

    The role of the Rigidbody2D is to update the Transform. You shouldn't be touching it.

    You move via the transform. Next time the physics simulation runs it sees you've changed it so it has to read your change and stomp over what the Rigidbody2D is set to. It then simulates and it might find itself overlapped because you explicitly teleported into overlap so it solves the overlap and writes to the Transform and you start this process all over again.

    Physics tries to stop overlaps or other issues. You are fighting the physics systems attempt.

    Please note, that's a 3D physics call.
     
    Kurt-Dekker likes this.
  4. Oir_the_tentacular

    Oir_the_tentacular

    Joined:
    Nov 26, 2021
    Posts:
    76
    Thank you for the response! I swapped to using rigidbody.movePosition and everything seems to be working a lot better!

    There are transform.position movement commands littered throughout my code. I don't actually ever want my enemies/players to be teleporting - I was just using transform.position to create the illusion of movement because I didn't know about movePosition. If I always want my rigidbodies to be detecting collisions, would it be good practice to basically rework all my transform.position movement commands to incorporate rigidbody.moveposition instead?

    Thank you once again for your time!
     
    Last edited: Dec 30, 2021
  5. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,529
    Again, I'm asked this question in a thousand different ways each year but, without me adding techo-babble, I'll just say, refer to Rule#1 above. :)

    In short: Use a Rigidbody2D and its API to effect physics moves.
     
    Oir_the_tentacular likes this.