Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question 2D Area of Effect Script - OverlapCircle not working

Discussion in 'Physics' started by MichaelABC, Mar 7, 2022.

  1. MichaelABC

    MichaelABC

    Joined:
    Jan 25, 2020
    Posts:
    69
    I meant to loop through all the colliders inside the range of this overlapCircle and do something (EnemyDeath) if the colliding objects meet some specific requirements (have a script attached). However what happens is that the overlapCircle range gets ignored and instead the EnemyDeath occurs when the box collider of the object with the script collides with the box collider of the enemy. Not at all what I intended.

    Code (CSharp):
    1. public float circleRadius = 6f;
    2.      private void CheckForDestructible()
    3.      {
    4.          Collider2D[] overlappingColliders = Physics2D.OverlapCircleAll(transform.position, circleRadius);
    5.          foreach (Collider2D colliding in overlappingColliders)
    6.          {
    7.              if (canExplode == true)
    8.              {
    9.                  var newExplosion = Instantiate(explosionFX, transform.position, Quaternion.identity);
    10.                  Destroy(newExplosion, 0.5f);
    11.                  Destroy(gameObject);
    12.                  if (colliding.GetComponent<EnemyHealth>())
    13.                  {
    14.                      Debug.Log("boom, I killed an enemy");
    15.                      colliding.GetComponent<EnemyHealth>().EnemyDead();
    16.                  }
    17.              }
    18.          }
    19.      }
     
  2. MichaelABC

    MichaelABC

    Joined:
    Jan 25, 2020
    Posts:
    69
    So apparently I found out a solution: if instead of passing the public float circleRadius I write a numeric value directly for OverlpaCircleAll (radius) it's working fine. Why is this though? Doesn't make much sense to me.
     
  3. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,177
    Your title is saying that the Unity call "OverlapCircle" is not working. Are you suggesting that or are you asking about why your code isn't working?

    That cannot be true. You've got something else going on in your code here.

    Look in the inspector to see what value it has. It won't be the default of 6 if you've changed it as it'll be serialized as it's a public field.

    Nothing in Unity decides whether a value like radius is coming from a constant value or a field, that's handled by C# itself so if this were the case, Unity would be completely broken which is why I'm telling you this isn't possible.

    Also, your code is very rough. For starters, you use "OverlapCircleAll" which produces a new array for you to just throw it away to the GC for waste. Just use any 2D physics query that doesn't have the "All" or "NonAlloc" suffix so use "OverlapCircle" and pass it an array or better still a List<T> and reuse it.

    You also perform this query and not even use the results unless "canExplode" is true. Don't even perform the query if that's the case otherwise this is even more waste.

    I would also suggest that if your Enemy or things with the "EnemyComponent" are on their own layer then filter by that layer in the query too otherwise you're detecting all colliders on all layers.

    I would also not destroy stuff until you've finished working on it. Above it might not be a problem but you need to be careful here.

    Do the least work otherwise you'll pay for it later as it all adds up.
     
    MichaelABC likes this.
  4. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,177
  5. MichaelABC

    MichaelABC

    Joined:
    Jan 25, 2020
    Posts:
    69
    MelvMay likes this.
  6. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,177
    FYI: You cannot delete it but you can report it and ask for it to be deleted. :)
     
  7. MichaelABC

    MichaelABC

    Joined:
    Jan 25, 2020
    Posts:
    69
    Yeah, I had to check it in the inspector for its value. I should have thought of it, I feel very stupid.

    I will try a different OverlapCircle and see if I can make it work as you say. About "canExplode" I forgot to remove it from this example, but it's something I am actually triggering from another script. Good idea to filter by layer for sure.

    I am always very careless when destroying objects too soon (and get some errors for it), sometimes is more or less obvious how to work around this. Some work to do.
     
  8. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,177
    Oh so it was the serialized value? Easy mistake to make TBH.

    Use OverlapCircle. Scroll down to the final overload that takes a "List<Collider2D>" list. You can create this list when your script starts and just reuse it. Then you won't get lots of waste and can still iterate it. Note that the call returns you a count of how many too so you can do "if (OverlapCircle(.....) > 0)" etc.

    Note that using "List<T>" on all physics queries is better than using arrays because for arrays you have to size them and if they're too small you'll loose results whereas if they're too large you waste memory. Lists will change their capacity automatically so you can ignore all that.

    To add again though, every single 2D physics query that returns multiple results provides this so it's worth using them.
     
    MichaelABC likes this.
  9. MichaelABC

    MichaelABC

    Joined:
    Jan 25, 2020
    Posts:
    69
    Code (CSharp):
    1.  
    2.  
    3. public List<Collider2D> overlappingDestructibles;
    4.  
    5. private void CheckForDestructible()
    6.     {
    7.         overlappingDestructibles.Add(Physics2D.OverlapCircle(transform.position, circleRadius));
    8.  
    9.         foreach (Collider2D colliding in overlappingDestructibles)
    10.         {
    11.             if (canExplode == true)
    12.             {
    13.                 var newExplosion = Instantiate(explosionFX, transform.position, Quaternion.identity);
    14.                 Destroy(newExplosion, 0.5f);
    15.                 Destroy(gameObject);
    16.                 if (colliding.GetComponent<EnemyHealth>())
    17.                 {
    18.                     Debug.Log("boom, I damaged an enemy");
    19.                     EnemyHealth enemyHealthScript = colliding.GetComponent<EnemyHealth>();
    20.                     enemyHealthScript.TakeDamage(explosionDamage);
    21.                 }
    22.             }
    23.         }
    24.     }
    Is this what you meant? It's seems that it's working fine! :)
     
  10. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,177
    No. Also your code about won't work if OverlapCircle returns NULL as you'll add it and get a NullReferenceException when you try to process it.

    As I said:
    There's an OverlapCircle where you pass in your list to the call i.e.
    Code (CSharp):
    1. public static int OverlapCircle(Vector2 point, float radius, ContactFilter2D contactFilter, List<Collider2D> results);
    If you've not used ContactFilter2D before then use "new ContactFilter2D().NoFilter()" or place it in a public field and set it in the inspector. It's a super useful way to filter what you get.
     
  11. MichaelABC

    MichaelABC

    Joined:
    Jan 25, 2020
    Posts:
    69
    Oh yeah this is about making that filtre to pass only the destructible objects yeah (right?)

    Where would this line sit, in the last example I gave? like line 4?
     
  12. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,177
    Well that's part of it but the main point which is what I thought you were asking about is using the OverlapCircle where you pass in the list.
     
  13. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,177
    No, like this:

    Code (CSharp):
    1. private List<Collider2D> overlappingDestructibles = new List<Collider2D>();
    2.  
    3. private void CheckForDestructible()
    4. {
    5.     // There no need to go any further here and waste our time if we cannot "explode".
    6.     if (!canExplode)
    7.         return;
    8.  
    9.     // This could be a public field and configured in the Inspector.
    10.     // We can use this to filter by layer, contact normals, triggers etc.
    11.     // Here we're doing no filtering at all so everything is returned.
    12.     var contactFilter = new ContactFilter2D().NoFilter();
    13.  
    14.     // Technically you don't need to check the return count because if there are no results, the list will be empty!
    15.     if (Physics2D.OverlapCircle(transform.position, circleRadius, contactFilter, overlappingDestructibles) > 0)
    16.      {
    17.         foreach (Collider2D colliding in overlappingDestructibles)
    18.         {
    19.             var newExplosion = Instantiate(explosionFX, transform.position, Quaternion.identity);
    20.             Destroy(newExplosion, 0.5f);
    21.             Destroy(gameObject);
    22.  
    23.             if (colliding.GetComponent<EnemyHealth>())
    24.             {
    25.                 Debug.Log("boom, I damaged an enemy");
    26.                 EnemyHealth enemyHealthScript = colliding.GetComponent<EnemyHealth>();
    27.                 enemyHealthScript.TakeDamage(explosionDamage);
    28.             }
    29.         }
    30.     }
    31. }
     
    MichaelABC likes this.
  14. MichaelABC

    MichaelABC

    Joined:
    Jan 25, 2020
    Posts:
    69
    I am probably implementing this the wrong way because I get this error:

    upload_2022-3-16_19-23-2.png

    Should have I declared it somewhere else?
     
  15. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,177
    The error is that it expects a semi-colon because it's missing in the previous line but so did I because I didn't type this code into Unity, only the forum. I'll edit my post for future viewers. :)

    Understand though, I'm NOT trying to write your code though, just give you an idea on how to use this physics query by passing in a list. I do not understand what your code expects or that surrounding it.
     
    MichaelABC likes this.
  16. MichaelABC

    MichaelABC

    Joined:
    Jan 25, 2020
    Posts:
    69
    You are right, that was an easy one. I should really rely on myself for that kind of things. Troubleshooting 101
     
    MelvMay likes this.
  17. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,177
    Just to reiterate for you, every single physics query allows for this non-allocating pattern where you need multiple results. I added it to all of them many years ago. It can massively help performance wise.