RTS Unit Selection with a Perspective Camera

Currently I have this code to select Units using an orthographic camera (following CodeMonkey ECS RTS tutorials on youtube, linked here) (Yes, my project does uses the DOTS framework, but this question should be monobehaviour relevant).

The code:

Entities.ForEach((Entity entity, ref Translation translation, ref SelectableComponent selectionData) =>
             {
                 float3 entityPosition = translation.Value;
                 if (entityPosition.x >= lowerLeftPosition.x &&
                 entityPosition.z >= lowerLeftPosition.y &&
                 entityPosition.x <= UpperRightPosition.x &&
                 entityPosition.z <= UpperRightPosition.y)
                 {
                     PostUpdateCommands.AddComponent(entity, new SelectedComponent());  
                 }      
             });

This just creates a box where the camera’s near clipping plane starts and checks to see which entities (objects) are within the box. My question is how do I get the “ground” plane on a perspective camera, because the near clipping plane is so geometrically small? I could raycast to the ground to get the depth, but I wonder if there is a better way to go about it.

PS: I do not expect nor require DOTS syntax, so please answer in classic monobehavour if it suites you.

Nothing wrong with this. Should be plenty performant.

Near clipping plane has nothing to do with it. Use the camera to get a ray into the scene where the mouse is, then either Raycast it to the ground geometry, or to a mathematical Plane object that is where you want to consider the ground. Planes have a Raycast method that is completely separate from physics: it is purely mathematical based on the ray and the plane.

From where the finger/mouse/touch went down, mark that as one corner of your rect, then as it drags, mark that as the opposite corner of your rect.

1 Like

I didn’t know about the plane raycast, and I got it working. :smile: My next related issue is drawing a selection box on mouse drag. I can use the start/ end point of the invisible selection plane area to scale a transparent Sprite, but I’d prefer if the box was drawn in front of camera, rather than on the ground. Additionally, I saw many “OnGUI” solutions, but I have heard that is very unperforment, so I’m trying to avoid those.

I’d like to point out that it’s 100x easier to convert unit positions to screen space and check based on that than it is to try and raycast and figure it out from there. It creates a more consistent user experience, too.

1 Like

@MadboyJames_1 You can also make a second camera, colocated where the current camera is (usually as a peer to a GameObject parented above it that you move in lieu of moving one camera), and make it draw objects that are in a special layer that the main camera does NOT draw. This lets you cleanly overlay a scaled-up sprite for your selection area. The overlay camera draws the sprite regardless of what “pokes through” from the ground.

But what @StarManta says is also very valid: it might be best to draw the scaling-up rectangle with a simple 2D UI canvas (you need to convert to the coordinates of the UI screen, which can be fiddly, but just google for it), then raycast from the corners to see which units you selected, if they get a little glow around them at the same time you are dragging the rubberband rectangle.

1 Like

Okay. raycasting to the plane is working very well for me, when all the units are on the ground. If a unit is hovering/ flying, then I’d need to draw a circle on the ground to mark its “selectable” position, which is non-ideal (The circle on the ground itself is fine, but only being able to select the unit from the circle, rather than its body, is not fine). So I’ll see if I can get the units to convert to screen space.

If you decide to use a Canvas/UI to draw a rectangle in screenspace, to ease up entanglements with the rest of your UI, you can make a special canvas that only turns itself on during selection, and it could be set to handle whatever scaling is convenient for your mouse/drag stuff. I think I would keep it completely separate from any other UI.

1 Like

Understood, and got it working! Another question: How would you suggest drawing the unit highlights? If the game becomes a multiplayer RTS (which is the plan), I need to show what player1 is selecting to player1 only, rather than attaching a “Selected” circle mesh to the units (which is my current solution), and showing what player1 is selecting to the world. I’m partial to circular bases around units (starcraft/ Blizzard RTS games in general style), so my current mesh gives me the graphic I want, just not the visibility. I am unfamiliar with culling masks and other camera tricks, If you would be able to point me in the direction of how to accomplish UI culling per player camera, I would appreciate it.

Also thank you both for being such a help so far! :slight_smile:

I think the answer to this would be highly-dependent on your networking code. Not sure what you’re using and I am not up to speed on any of the current Unity networking solutions, but basically make sure anything and everything related to selection (and indeed ALL UI state) doesn’t get sent over the network.

1 Like

Makes sense, Thanks!