Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

BoxCastAll to get all objects within RTS style selection

Discussion in 'Scripting' started by Zephni, Jan 13, 2017.

  1. Zephni

    Zephni

    Joined:
    Apr 23, 2015
    Posts:
    100
    The code below is for generating a selection box that works when clicking and dragging the mouse. I have tried to project a "BoxCastAll" raycast from the cameras perspective within the selection box to pickup all objects underneath it:

    Code (csharp):
    1. // Selection
    2. if(Input.GetMouseButtonDown(0))
    3.     startSelectionDrag = Input.mousePosition;
    4.  
    5. if(startSelectionDrag != Vector2.zero)
    6.     endSelectionDrag = Input.mousePosition;
    7.  
    8. selectionBox = new Rect(startSelectionDrag.x, Screen.height - startSelectionDrag.y, endSelectionDrag.x - startSelectionDrag.x, -1 * ((Screen.height - startSelectionDrag.y) - (Screen.height - endSelectionDrag.y)));
    9.  
    10. if(Input.GetMouseButtonUp(0))
    11. {
    12.     RaycastHit[] hits = Physics.BoxCastAll(
    13.         Camera.main.ScreenToWorldPoint(selectionBox.center),
    14.         selectionBox.size / 2,
    15.         transform.forward,
    16.         Quaternion.LookRotation(transform.forward)
    17.     );
    18.  
    19.     if(hits.Length == 0)
    20.     {
    21.         DeselectAll();
    22.     }
    23.     else
    24.     {
    25.         DeselectAll();
    26.         foreach(RaycastHit hit in hits)
    27.         {
    28.             Debug.Log(hit.transform.name);
    29.             GameObject go = hit.transform.gameObject;
    30.             if(go.transform.gameObject.GetComponent<Entity>() != null)
    31.             {
    32.                 selectedUnits.Add(go);
    33.                 go.GetComponent<Entity>().Select(true);
    34.             }
    35.         }
    36.     }
    37.  
    38.     startSelectionDrag = Vector2.zero;
    39.     endSelectionDrag = Vector2.zero;
    40.     selectionBox = new Rect(0, 0, 0, 0);
    41. }
    eg1.png

    Trouble is every time I make a selection it selects everything regardless of whether the box is overlapping the object or not. Have I used BoxCastAll wrong, or just sent it the wrong parameters?

    Thanks in advanced for any help!
     
  2. Joboto

    Joboto

    Joined:
    Sep 12, 2013
    Posts:
    64
    Hi there,

    Two things that might help you get to the bottom of this is firstly:


    Use Gizmos.DrawCube to create a cube in the Scene View that represents the last boxcast you did to see if where you think the box cast occurs is actually where it is occuring.

    Secondly perhaps use a LayerMask to restrict the selection to specific Layers.

    Kind Regards,
    Joe

    https://docs.unity3d.com/ScriptReference/Gizmos.DrawCube.html
     
  3. Zephni

    Zephni

    Joined:
    Apr 23, 2015
    Posts:
    100
    Hi! I tried using Gizmos.DrawCube, but when doing the following:

    Code (csharp):
    1. void OnDrawGizmos()
    2. {
    3.     Gizmos.color = new Color(1, 0, 0, 0.5F);
    4.     Gizmos.DrawCube(Camera.main.ScreenToWorldPoint(selectionBox.center), selectionBox.size);
    5. }
    The box isn't "rotational" so it's ignoring the fact that the camera is rotated, also the selectionbox is a Vector2 so not sure if that is causing other problems.

    The "specific layers" part you mentioned, doesn't seem to be the issue because it's selecting objects that aren't even within the "box", the because my code is checking for a specific class it is ignoring any non "Entity" object which is correct, it must be the raycast "position" or "direction" or "size" causing the issue. :(
     
  4. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,294
    I'm going to guess that it's your selection drag that's messed up, the box cast looks good.

    The easiest way to debug is to create the box. Log your settings:

    That'll give you a white cube that's positioned and scaled like your box cast. Move it forward on it's local axis to see what your box hits.


    Also, when you're done, replace the BoxCast with a BoxCastNonAlloc, so save on that memory!
     
  5. Zephni

    Zephni

    Joined:
    Apr 23, 2015
    Posts:
    100
    Thanks so far guys, I think the problem is to do with the "half extents" relating to the size of the selection box based on the screen rather than the "world" version of the rectangle. However I'm struggling to get the correct conversions.

    By the way I did try what you said Baste and the object was just MASSIVE so this pretty much confirms this :(
     
    Last edited: Jan 13, 2017
  6. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    We do this by converting the unit's position to screen space and seeing if that position lies inside the box. Then there's no need to cast into the world.
     
    Nigey likes this.
  7. Zephni

    Zephni

    Joined:
    Apr 23, 2015
    Posts:
    100
    Oh wow, that is a REALLY good idea... let me try
     
  8. Zephni

    Zephni

    Joined:
    Apr 23, 2015
    Posts:
    100
    Well, this is 100% in the right direction. It is almost working perfectly thank you very much, BUT:

    This selection works:
    Untitled1.png

    And this doesn't
    Untitled2.png

    Selecting the entire box obv also works. It seems like only part of it is counting, is this something to do with where the "point" position that is getting projected to the screen space is not where you'd expect?
     
  9. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Yep. All depends on where your transform is. I honestly forget how we compensated for this. When I get time I'll poke around in our selection manager.
     
  10. Zephni

    Zephni

    Joined:
    Apr 23, 2015
    Posts:
    100
    Thanks, that would be great :D Ill try a few things in the mean time!
     
  11. MrG

    MrG

    Joined:
    Oct 6, 2012
    Posts:
    363
    Resurrecting because I actually need the BoxCastAll solution, if anyone has managed to solve converting a Rect to the correct parameters. BoxCastAll can use layers to filter down to the kinds of things I want to select, and I don't want to use a mySelectable hashset with code on every possible thing that I might want.
     
  12. RamesesX

    RamesesX

    Joined:
    Aug 7, 2016
    Posts:
    1
    Code (CSharp):
    1.         // Multiply by this to convert Screen distance to World distance
    2.         float pixelsToWorld = camera.orthographicSize / (screenHeight / 2f);
    3.  
    4.         // Get BoxCast center and size by converting the rect's center, width, and height from screen to world space
    5.         Vector2 boxCenter = camera.ScreenToWorldPoint(selectionRect.center);
    6.         float boxWidth = selectionRect.size.x * pixelsToWorld;
    7.         float boxHeight = selectionRect.size.y * pixelsToWorld;
    8.         Vector2 boxSize = new Vector2(boxWidth, boxHeight);
    9.  
    10.         Debug.Log(boxCenter + " ; " + boxSize);
    11.  
    12.         RaycastHit2D[] hitUnits = Physics2D.BoxCastAll(boxCenter, boxSize, 0f, transform.forward);
    This should convert a screen space rect's center and size to world space. I made this in 2D though.
     
    ShadowOfEclipse likes this.
  13. look001

    look001

    Joined:
    Mar 23, 2017
    Posts:
    111
    For everybody instested in selecting not only the pivot but the whole unit, you can use the GeometryUtility.TestPlanesAABB function to test all units if their boundingbox is inside a rect (rts example)
     
    KelsoMRK likes this.