Search Unity

Resolved Detecting collision before spawning/instantiating not working.

Discussion in '2D' started by MorseOtter, Oct 13, 2022.

  1. MorseOtter

    MorseOtter

    Joined:
    Apr 27, 2020
    Posts:
    7
    Hi, I'm trying to spawn items on a circumference around my player without it colliding with other gameobjects with a collider2D attached to it. To detect if a spawnPoint is free of collision before instantiating my item on the circumference, I used Physics2D.OverlapCircle on the possible spawnPoint. However, my current code doesn't work and I can't seem to figure why. My items still instantiate on the circumference but do not care about any colliders and overlap with each other still.

    Code (CSharp):
    1. public float ItemSpawnRadius = 1.5f;
    2. public float ItemCollisionCheckRadius = 0.5f;
    3.  
    4. public void SpawnItem()
    5.     {
    6.         bool itemSpawned = false;
    7.  
    8.         while (!itemSpawned)
    9.         {
    10.             Vector2 spawnPoint = (Vector2)_playerTransform.position + Random.insideUnitCircle.normalized * ItemSpawnRadius;
    11.             if (!Physics2D.OverlapCircle(spawnPoint, ItemCollisionCheckRadius, default))
    12.             {
    13.                 Instantiate (itemPrefab, spawnPoint, Quaternion.identity);
    14.                 itemSpawned = true;
    15.             }
    16.             else
    17.             {
    18.                 Debug.Log("Items collided!");
    19.             }
    20.         }
    21.     }
    Screen Shot 2022-10-12 at 7.37.10 PM.png
     
  2. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,499
    Have you looked at the docs for Physics2D.CircleCast ? For LayerMask (int) where each bit indicates a layer, you're passing default; the default of int is obviously zero (no bits on). It'll always return false if you ask it to not check any layers.

    Whatever layers the Colliders the "itemPrefab" is on is what you should be using. Either that or check all layers using Physics2D.AllLayers. The value of this int is ~0 (all bits on).

    NOTE: Your code here is "dangerous". If it ever gets to point where it cannot find anything then you'll lock the main-thread along with your game. It'll also take longer and longer as the area becomes populated.
     
  3. MorseOtter

    MorseOtter

    Joined:
    Apr 27, 2020
    Posts:
    7
    Thank you for replying! All the items in my scene that has a collider2D which i wish to detect are indeed on the default layer, and so are the items prefab I want to instantiate. I have looked into Physics2D.CircleCast but I'm not quite sure what my Vector2 direction would be. I'm aware that my code is quite dangerous but I'm not sure how to break out of it unless my bool returns true. Perhaps looping it through an int = x amount tries (let's say 50) until that number runs out could be good? then if no spawnPoint is available simply don't instantiate. I hope I'm not talking too much nonsense. I'm still an amateur so if you can kindly show how i can fix this code that would be tremendously helpful.
     
  4. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,499
    I already did above! ;) Did you read the part where I said try using "Physics2D.AllLayers" and that using "default" is wrong?

    The Unity "default" layer has nothing to do with the C# "default" keyword. Trying not to turn this into a C# tutorial, default means use the default of the value. The value there is an int. The default of int is zero.
     
  5. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,499
    So this...
    Code (CSharp):
    1. if (!Physics2D.OverlapCircle(spawnPoint, ItemCollisionCheckRadius, Physics2D.AllLayers))
    ... or just don't specify that argument at all and just pass point and radius and it'll work or as I said, the layer that your prefab is on. Just don't use "default" because that's not appropriate there and is nothing related to the Unity default layer.
     
  6. MorseOtter

    MorseOtter

    Joined:
    Apr 27, 2020
    Posts:
    7
    Pardon me. Yes, I have tries with Physics2D.AllLayers, int 0 and without the argument too but it crashed the game when I tried with Physics2D.AllLayers or if I don't pass an argument.
     
  7. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,499
    I presume you don't mean "crash" as in you get a crash report but instead it enters an infinite loop and locks the main thread as I stated above would happen if it cannot find a place to put your prefab.

    Using that argument won't crash anything. It'll be because it's always hitting something and so it never exits your loop as I mentioned before.

    You should debug it.
     
  8. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,499
    Also, that fact that you tried passing zero means you have a misunderstanding of what layers and layer masks are so I would highly suggest you find a tutorial on that before you proceed. Using stuff you simply don't understand is going to be painful. Guess coding is hard work!

    Search for "using unity layer masks" on YouTube. There's hundreds of videos about it. With that knowledge, you'll figure this out. :)
     
  9. MorseOtter

    MorseOtter

    Joined:
    Apr 27, 2020
    Posts:
    7
    Sorry for the inconvenience due to my lack of knowledge. Thank you for pointing out what I should learn still!
     
  10. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,499
    No inconvenience at all and no need to be sorry! There's nothing wrong with not understanding but sometimes you just have to point out that the best course of action is to go learn a specific thing. :)

    In the meantime, I'd try this which limits how many times it'll try:
    Code (CSharp):
    1. public float ItemSpawnRadius = 1.5f;
    2. public float ItemCollisionCheckRadius = 0.5f;
    3.  
    4. const int RetryThreshold = 10;
    5.  
    6. public void SpawnItem()
    7. {
    8.     for(var n = 0; n < RetryThreshold; ++n)
    9.     {
    10.         Vector2 spawnPoint = (Vector2)_playerTransform.position + Random.insideUnitCircle.normalized * ItemSpawnRadius;
    11.  
    12.         if (!Physics2D.OverlapCircle(spawnPoint, ItemCollisionCheckRadius, Physics2D.AllLayers))
    13.         {
    14.             Instantiate (itemPrefab, spawnPoint, Quaternion.identity);
    15.             Debug.Log("Spawned");
    16.             return;
    17.         }
    18.     }
    19.  
    20.     Debug.Log("Could not find a place to spawn.");
    21. }
    22.  
     
  11. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,499
    I thought I'd post this separately but "ItemSpawnRadius" is very, very tiny. It's only 1.5 so a very small area. You're checking for things that are 0.5 in size. You'll only get a few of those at best in that area. Surely it should be a lot larger than that!

    You can visualise this by adding this to your class which will draw a sphere at the player position with your spawn radius:
    Code (CSharp):
    1. public float ItemSpawnRadius = 1.5f;
    2. public float ItemCollisionCheckRadius = 0.5f;
    3.  
    4. const int RetryThreshold = 10;
    5.  
    6. // Draw the spawn area.
    7. private void OnDrawGizmos()
    8. {
    9.     Gizmos.color = Color.red;
    10.     Gizmos.DrawSphere(_playerTransform.position, ItemSpawnRadius);
    11. }
    12.  
    13. public void SpawnItem()
    14. {
    15.     for(var n = 0; n < RetryThreshold; ++n)
    16.     {
    17.         Vector2 spawnPoint = (Vector2)_playerTransform.position + Random.insideUnitCircle.normalized * ItemSpawnRadius;
    18.  
    19.         if (!Physics2D.OverlapCircle(spawnPoint, ItemCollisionCheckRadius, Physics2D.AllLayers))
    20.         {
    21.             Instantiate (itemPrefab, spawnPoint, Quaternion.identity);
    22.             Debug.Log("Spawned");
    23.             return;
    24.         }
    25.     }
    26.  
    27.     Debug.Log("Could not find a place to spawn.");
    28. }
    Also, you can get 2D physics to always draw the gizmos of any colliders you create by going into "Project Settings > Physics 2D > Gizmos" and check "Always Show Colliders". This means you don't have to select them for them to be drawn. This can help you understand what's in your scene.

    Hope this helps.
     
    Last edited: Oct 13, 2022
  12. MorseOtter

    MorseOtter

    Joined:
    Apr 27, 2020
    Posts:
    7
    I truly appreciate your help and advices! Thank you for elaborating the RetryTreshold loop. That is indeed necessary. I have already implemented the Gizmos function but i certainly didn't know about "Always Show Colliders". I think you have provided everything I need to look upon and figure it out on my own now. Thank you for your patience and time for my thread! You're awesome!
     
    MelvMay likes this.
  13. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,499
    Always feel free to ask!

    Good luck with it.