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

Question Question about arrays and loops

Discussion in 'Scripting' started by DrVanillaCookie, Sep 13, 2023.

  1. DrVanillaCookie

    DrVanillaCookie

    Joined:
    Aug 23, 2021
    Posts:
    39
    Here's my new problem: I have an object which consists of many child objects, all of them with sprite renderers. Then there's a ray coming from the mouse position (mainCamera.ScreenPointToRay(mousePosition). The camera is perspective so it can't be ScreenToWorldPoint. The script with a raycast is in the parent object.

    Here's what's supposed to happen: if that ray intersects with ANY of the sprites, a certain function runs; if the ray hits NONE of the sprites, another function.

    My idea is to have an array of SpriteColliders and do foreach loop, here's what I got so far:
    Code (CSharp):
    1. private SpriteRenderer[] spriteRenderers;
    2. Ray ray = mainCamera.ScreenPointToRay(mousePosition);
    3.  
    4. foreach (SpriteRenderer sr in spriteRenderers)
    5.         {
    6.             if (sr.bounds.IntersectRay(ray))
    7.             {
    8.                 HighlightON();              
    9.                 break;
    10.             }
    11.             else
    12.             {
    13.                 HighlightOFF();
    14.             }
    15.         }
    It works, but not particularly well, since sometimes both functions run at the same time and it's not good. I'm not even sure it's the right approach, but I've tried lots of things, my brain kind of done. Plus I'm not very good at loops.

    It looks I can make the ray detect the FIRST sprite it hits and then break and go back to start of the loop, which is what I need, but I'm not sure how to check if the ray doesn't detect anything in the array. Or in this case, how to check if none of the sprites in the array has a ray intersecting any of them.

    I'm open to all sorts of suggestions, however I can't use colliders (long story).

    I'm considering adding a script to every child object that does raycasting and sends a bool (or smth) to the parent, but it seems costly, since there may be quite a lot of child objects.
     
  2. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,378
    I'll start by saying the fastest way to get your existing code working is to have a bool flag defaulted to false. Set it true if an intersection occurs and break. Then after the loop is done call based on the flag.

    Code (csharp):
    1. Ray ray = mainCamera.ScreenPointToRay(mousePosition);
    2. bool hit = false;
    3.  
    4. foreach (SpriteRenderer sr in spriteRenderers)
    5. {
    6.     if (sr.bounds.IntersectRay(ray))
    7.     {
    8.         hit = true;
    9.         break;
    10.     }
    11. }
    12.  
    13. if (hit)
    14.     HighlightON();
    15. else
    16.     HighlightOFF();
    (you could even make a Highlight function and pass in the bool for on/off)

    With that said the larger your sprite collection, the more work you're going to do. Also the bounds of the renderer may not actually be the sprite itself since the sprite might have negative space.

    I'm not exactly certain of your needs in your game, but there is likely better ways to accomplish this. For example a physics based raycast or the ilk if you need this to be more dynamic rather than needing this array of all sprites. You could then have colliders with shape to it to account for negative space, and it doesn't matter how many sprites exist, it scales just fine (well it scales with the performance of the physics system).
     
    Last edited: Sep 13, 2023
    Bunny83 likes this.
  3. DrVanillaCookie

    DrVanillaCookie

    Joined:
    Aug 23, 2021
    Posts:
    39
    This was one of things I've tried, your example doesn't have false condition though, and it looks like the hardest part. Here's what I got:
    Code (CSharp):
    1. foreach (SpriteRenderer sr in spriteRenderers)
    2.         {
    3.             if (sr.bounds.IntersectRay(ray))
    4.             {
    5.                 isHighlighted = true;
    6.                 break;
    7.             }
    8.             else
    9.             {
    10.                 isHighlighted = false;          
    11.             }
    12.         }
    13.  
    14.         if (isHighlighted)
    15.         {
    16.             Debug.Log("on");
    17.             HighlightON();
    18.         }
    19.         else
    20.         {      
    21.             Debug.Log("off");
    22.             HighlightOFF();
    23.         }
    It visually works, however when debugging you can see both on and off conditions run at the same time. And I need only one object in the scene to be on (due to sprite.bounds picking all the sprites it can get).

    EDIT: Okay, I'm dumb, your code is correct, I forgot to put hit = false in the update, only as a field. However it still returns both on and off. I'll have to think of some other method to have only one object on.


    You're right, sprite.bounds does pick transparent areas of the sprite which sucks and I haven't yet found a way to deal with this.

    I got another thread on this: https://forum.unity.com/threads/2d-colliders-in-a-3d-environment.1484382/

    Short story is: 2D sprites (some animated) tilted towards the camera (perspective) in a 3D environment. Every object has to be interactable with a mouse. 2D colliders doesn't work since they can't be tilted and 3D won't be as perfect as a sprite shape (I haven't learned about meshes yet).

    So for now I'm trying to make this sprite.bounds method work. I'm thinking of introducing a count for every hit in the array, and if it's > 0 it's on. Still thinking about this.
     
    Last edited: Sep 14, 2023
  4. DrVanillaCookie

    DrVanillaCookie

    Joined:
    Aug 23, 2021
    Posts:
    39
    I realized that making a simple 3D Box Collider isn't going to be much less inaccurate then using this sprite.bound method, so it all makes no sense pretty much. Now I need to figure out how to use sprite as mesh collider.
     
  5. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,563
    No, look again. It ASSUMES false, then only if a test succeeds does it become true.

    That's required because you are iterating a collection, and you do not care if any one single item does NOT connect, but you DO care if ANY of them DO connect.

    Lord's approach is identical to how I would do it.
     
  7. DrVanillaCookie

    DrVanillaCookie

    Joined:
    Aug 23, 2021
    Posts:
    39
    Nah, 2D colliders wouldn't help. To make a long story short, I had tilted sprite objects in a 3D world and I couldn't for the life of me figure how to make them interactable with a mouse. The sprite.bound method was one my attempts, but it didn't work out.

    So today I finally figured things out, plus MelvMay made a neat tool to make a mesh out of any 2D collider. If you're interested, here's the thread: https://forum.unity.com/threads/my-little-rant-on-colliders.1492319/
     
  8. DrVanillaCookie

    DrVanillaCookie

    Joined:
    Aug 23, 2021
    Posts:
    39
    Yep, I added a little EDIT after I took a better look. Anyway this whole sprite.bound thing didn't quite work out, but at least I got some experience with loops.