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

Help With Creating A 3D RTS Style Unit Selection Box

Discussion in 'Scripting' started by MattNJ, Jun 1, 2019.

  1. MattNJ

    MattNJ

    Joined:
    Mar 12, 2019
    Posts:
    1
    How would you go about creating a real time strategy game style unit selection box for a 3D game in Unity?
     
  2. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    (This assume that you're talking about dragging a marquee area to select multiple units simultaneously.)

    There are a couple of different ways, and it really depends on how your units are managed. Most of them are going to involve raycasts of some sort. To start with, the basic "single unit selection". When you click the screen (single click), your input controller may raycast outward from the camera's position/orientation in the direction of the pixels you clicked. You can view an easy example of this in the documentation here. This is how the single unit selection tends to work in a lot of games- raycast out from the Camera to hit triggers/colliders on a certain layer, return the first result, and "select" it, maybe through some sort of IInteractable or ISelectable interface for a Component on the unit.

    When trying to select multiple units, it get a bit more complicated, but still more or less along the same route. One way would involve caching the point where the click first occurs, this is the "first corner", x1/y1, then continuing to detect (don't actually stop until MouseUp "finishes" the selection) and each update moving the "last corner", x2/y2. So x1/y1 are determined by the first click, and x2/y2 are determined by where you release the drag. You then calculate the positions of the second and third corners, like so:
    Code (csharp):
    1. (x1, y1) -- (x2, y1)
    2.    -            -
    3.    -            -
    4. (x1, y2) -- (x2, y2)
    ... and use these to generate a hitbox area in the game world, and see what units its encompassing. An invisible "selection" GameObject with a Trigger area (that you move around setting its Transform position and collider area manually) works for this, or you can use Physics.OverlapBox instead, or you can just use the calculations to do a Physics.BoxCastAll from the camera that will cover the dragged area, from the center point. If you have a unit manager that tracks the positions of all of your units in the game, you can even just iterate over that list and run the overlap calculations yourself (if unitPosition.x >= x1 && <= x2, unitPosition.y >= y1 and <= y2, type of thing), instead of using the Physics system.

    I don't really have any sort of complete example to show you or anything, but hopefully this gives you some ideas.
     
    Last edited: Jun 1, 2019
    MattNJ likes this.
  3. blu3drag0n

    blu3drag0n

    Joined:
    Nov 9, 2018
    Posts:
    94
    This is an old post, but when I was looking for a solution to get a selection rectangle drawing in game mode, also in a build version, then there were not really satisfying answers. Most tried via some assets packages or either Line Renderer or Shaders, which I found to be overloaded for what I was trying to achieve.

    I finally managed to build it myself with very few code and want to share for everyone else googling in the future, enjoy:
    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. /// <summary>
    4. /// Draws a selection rectangle on the left mouse button down & dragging
    5. ///
    6. /// You only need an InputReceiverManager that basically tracks
    7. /// - if the left mouse button is currently down and saves it in "LeftMouseButtonDown"
    8. /// - saves the initial click position when mouse button was clicked  and saves it in "InitialMousePositionOnLeftClick"
    9. /// - updates the current mouse position and saves it in "CurrentMousePosition"
    10. ///
    11. /// </summary>
    12. public class SelectionRectangleDrawer : MonoBehaviour
    13. {
    14.     //set color via inspector for the selection rectangles filler color
    15.     public Color SelectionRectangleFillerColor;
    16.     //set color via inspector for the selection rectangles border color
    17.     public Color SelectionRectangleBorderColor;
    18.     //set selection rectangles  border thickness
    19.     public int SelectionRectangleBorderThickness = 2;
    20.     private Texture2D _selectionRectangleFiller;
    21.     private Texture2D _selectionRectangleBorder;
    22.     private bool _drawSelectionRectangle;
    23.     private float _x1, _x2, _y1, _y2;
    24.     private Vector2 pos1, pos2;
    25.     void Start()
    26.     {
    27.         _selectionRectangleFiller = new Texture2D(1, 1);
    28.         _selectionRectangleFiller.SetPixel(0, 0, SelectionRectangleFillerColor);
    29.         _selectionRectangleFiller.Apply();
    30.         _selectionRectangleFiller.wrapMode = TextureWrapMode.Clamp;
    31.         _selectionRectangleFiller.filterMode = FilterMode.Point;
    32.         _selectionRectangleBorder = new Texture2D(1, 1);
    33.         _selectionRectangleBorder.SetPixel(0, 0, SelectionRectangleBorderColor);
    34.         _selectionRectangleBorder.Apply();
    35.         _selectionRectangleBorder.wrapMode = TextureWrapMode.Clamp;
    36.         _selectionRectangleBorder.filterMode = FilterMode.Point;
    37.     }
    38.     void Update()
    39.     {
    40.         if (InputReceiverManager.Instance.LeftMouseButtonDown && !Mathf.Approximately(Vector2.Distance(InputReceiverManager.Instance.CurrentMousePosition,
    41.                                                                                                        InputReceiverManager.Instance.InitialMousePositionOnLeftClick), 0f))
    42.             _drawSelectionRectangle = true;
    43.         else if (!InputReceiverManager.Instance.LeftMouseButtonDown && _drawSelectionRectangle)
    44.             _drawSelectionRectangle = false;
    45.     }
    46.     private void OnGUI()
    47.     {
    48.         if (_drawSelectionRectangle)
    49.             drawSelectionRectangle();
    50.     }
    51.     private void drawSelectionRectangle()
    52.     {
    53.         pos1 = InputReceiverManager.Instance.InitialMousePositionOnLeftClick;
    54.         pos2 = InputReceiverManager.Instance.CurrentMousePosition;
    55.         //check initial mouse position on X axis versus dragging mouse position
    56.         if (pos1.x < pos2.x)
    57.         {
    58.             _x1 = pos1.x;
    59.             _x2 = pos2.x;
    60.         }
    61.         else
    62.         {
    63.             _x1 = pos2.x;
    64.             _x2 = pos1.x;
    65.         }
    66.         //check initial mouse position on Y axis versus dragging mouse position
    67.         if (pos1.y < pos2.y)
    68.         {
    69.             _y1 = pos1.y;
    70.             _y2 = pos2.y;
    71.         }
    72.         else
    73.         {
    74.             _y1 = pos2.y;
    75.             _y2 = pos1.y;
    76.         }
    77.         //filler
    78.         GUI.DrawTexture(new Rect(_x1, Screen.height - _y1, _x2 - _x1, _y1 - _y2), _selectionRectangleFiller, ScaleMode.StretchToFill);
    79.         //top line
    80.         GUI.DrawTexture(new Rect(_x1, Screen.height - _y1, _x2 - _x1, -SelectionRectangleBorderThickness), _selectionRectangleBorder, ScaleMode.StretchToFill);
    81.         //bottom line
    82.         GUI.DrawTexture(new Rect(_x1, Screen.height - _y2, _x2 - _x1, SelectionRectangleBorderThickness), _selectionRectangleBorder, ScaleMode.StretchToFill);
    83.         //left line
    84.         GUI.DrawTexture(new Rect(_x1, Screen.height - _y1, SelectionRectangleBorderThickness, _y1 - _y2), _selectionRectangleBorder, ScaleMode.StretchToFill);
    85.         //right line
    86.         GUI.DrawTexture(new Rect(_x2, Screen.height - _y1, -SelectionRectangleBorderThickness, _y1 - _y2), _selectionRectangleBorder, ScaleMode.StretchToFill);
    87.     }
    88. }
    89.  
    90.  
    91.  
    Now project your units positions transform location into screenspace and see whats overlapping or which units is contained in the rectangle.