BoxCastAll to get all objects within RTS style selection

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:

// Selection
if(Input.GetMouseButtonDown(0))
    startSelectionDrag = Input.mousePosition;

if(startSelectionDrag != Vector2.zero)
    endSelectionDrag = Input.mousePosition;

selectionBox = new Rect(startSelectionDrag.x, Screen.height - startSelectionDrag.y, endSelectionDrag.x - startSelectionDrag.x, -1 * ((Screen.height - startSelectionDrag.y) - (Screen.height - endSelectionDrag.y))); 

if(Input.GetMouseButtonUp(0))
{
    RaycastHit[] hits = Physics.BoxCastAll(
        Camera.main.ScreenToWorldPoint(selectionBox.center),
        selectionBox.size / 2,
        transform.forward,
        Quaternion.LookRotation(transform.forward)
    );

    if(hits.Length == 0)
    {
        DeselectAll();
    }
    else
    {
        DeselectAll();
        foreach(RaycastHit hit in hits)
        {
            Debug.Log(hit.transform.name);
            GameObject go = hit.transform.gameObject;
            if(go.transform.gameObject.GetComponent<Entity>() != null)
            {
                selectedUnits.Add(go);
                go.GetComponent<Entity>().Select(true);
            }
        }
    }

    startSelectionDrag = Vector2.zero;
    endSelectionDrag = Vector2.zero;
    selectionBox = new Rect(0, 0, 0, 0);
}

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!

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

Hi! I tried using Gizmos.DrawCube, but when doing the following:

void OnDrawGizmos()
{
    Gizmos.color = new Color(1, 0, 0, 0.5F);
    Gizmos.DrawCube(Camera.main.ScreenToWorldPoint(selectionBox.center), selectionBox.size);
}

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. :frowning:

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!

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 :frowning:

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.

1 Like

Oh wow, that is a REALLY good idea… let me try

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

This selection works:
2919416--215560--Untitled1.png

And this doesn’t
2919416--215561--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?

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.

Thanks, that would be great :smile: Ill try a few things in the mean time!

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.

        // Multiply by this to convert Screen distance to World distance
        float pixelsToWorld = camera.orthographicSize / (screenHeight / 2f);

        // Get BoxCast center and size by converting the rect's center, width, and height from screen to world space
        Vector2 boxCenter = camera.ScreenToWorldPoint(selectionRect.center);
        float boxWidth = selectionRect.size.x * pixelsToWorld;
        float boxHeight = selectionRect.size.y * pixelsToWorld;
        Vector2 boxSize = new Vector2(boxWidth, boxHeight);

        Debug.Log(boxCenter + " ; " + boxSize);

        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.

1 Like

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)

1 Like