Search Unity

  1. Unity 2020.1 has been released.
    Dismiss Notice
  2. We are looking for feedback on the experimental Unity Safe Mode which is aiming to help you resolve compilation errors faster during project startup.
    Dismiss Notice
  3. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice

Unity3D multiple objects user rectangle selection

Discussion in 'Scripting' started by Hellfire01, Jul 28, 2020.

  1. Hellfire01

    Hellfire01

    Joined:
    Jul 14, 2016
    Posts:
    9
    I wish to allow the user to select multiple units by click and drag as most RTS do. The issue I have is that the tutorials are either in 2D or only use the transform.position such as this one : https://sharpcoderblog.com/blog/unity-3d-rts-style-unit-selection.

    Using the transform.position will work for most cases but will not allow the user to select easily as selecting the head or feet of a unit. It also means I have to do an other script using raycasts for single click selections as it is almost impossible to get the single point of the transform.position with one click.

    How can I do a proper rectangle selection for a 3D game ?

    Examples of game that do it are Age of Empires 3, Supreme Commander 2, Dawn of War, ... I, however, do not know if they all use the same technique.

    By looking at 3D RTS games I own, I noticed that they use a simplified hitbox ( usually a cube or capsule ) and somehow collided it with the user's selection rectangle.

    The best solution I could think about would be to create a temporary volume using the camera's clipping planes and the user's rectangle selection but I do not know where to start to do that nor do I know if it's the best possible solution for my problem.
     
  2. AnthonySharp

    AnthonySharp

    Joined:
    Apr 11, 2017
    Posts:
    88
    If it were me I would try:
    • Get raycast of position where player started dragging (https://docs.unity3d.com/Manual/CameraRays.html).
    • Get raycast of position where player finished dragging.
    • From these two Vector3s you can construct a 3D cube.
    • Treat it like a 2D plane (i.e. ignore the height of the cube);
    • For each unit: if the unit is inside the 2D plane, select it.
     
  3. Hellfire01

    Hellfire01

    Joined:
    Jul 14, 2016
    Posts:
    9
    The issue would be that if you selected the upper part of a unit, the box would end up being behind the unit rather than on it ( for a camera at a 45° angle for example )
    I think I am going to end up trying a few different things before I end up with a satisfying answer
     
  4. AnthonySharp

    AnthonySharp

    Joined:
    Apr 11, 2017
    Posts:
    88
    Not sure what you mean :p If you're worried about the raycast colliding with the unit itself and producing weird results maybe you could use layer masks to exclude units.

    EDIT: hmm actually upon taking a look at it in practice I'm not sure if that approach would actually work since the 2d screen doesn't correspond well to the 3d world... back to the drawing board
     
    Last edited: Jul 29, 2020
  5. AnthonySharp

    AnthonySharp

    Joined:
    Apr 11, 2017
    Posts:
    88
    EDIT EDIT: Okay so technically speaking the way I suggested is one way of doing it, but it is not what OP is looking for. The way I suggested is how old games like Populous: The Beginning (amazing game - about time someone made a Unity re-make) used to do unit selection, but it's not how most modern RTS games work. E.g. what I suggested was this:

    1.jpg

    This creates a 2D selection plane (represented in blue) and anything within that plane is selected. This worked in games like Populous because it would draw a 2D square on the 3D terrain of that plane as projected onto the terrain. But below you can see how this method does not actually match-up with what the user does on the screen like it does in modern RTS games:

    2.jpg

    The dodgy cube drawing makes it hard to see, because it looks like the blue cube is above the terrain (when it's actually below/projecting down into the terrain), but you can see how the pikeman to the top-right of the blue horse wouldn't be selected in the 3D cube way of doing it, whereas he would in the "screenspace" way of doing it, which is what you're looking for.

    What you're trying to do essentially boils down mathematically to: is the unit in question rendered within a certain rectangle on the screen? And the answer to that problem is basically just to use Camera.WorldToScreenPoint on every unit (preferably not on the units which are culled because they are not on the screen for performance reasons) and figure out whether they are in a selection rectangle. At least, I think that would work...
     
    Last edited: Jul 29, 2020
  6. Hellfire01

    Hellfire01

    Joined:
    Jul 14, 2016
    Posts:
    9
    Thank you for your reply
    I already use Camera.WorldToScreenPoint but I would prefer to avoid calling it for all of the corners of all of the Units ( in order to ensure an easy selection for the user )
    In the end, this is the method I am trying to implement, should it work, I will release it as a free Unity asset :
    1 - I use the Camera.WorldToScreenPoint such as shown in this code : ( works well ) https://sharpcoderblog.com/blog/unity-3d-rts-style-unit-selection
    2 - in order to get the units that do not have the center of their bounding box within the selection yet are overlapping with the user's selection, I am following this tutorial / dev blog :
    ( a total of 9 videos on that subject )

    I do think I am going to be tweaking a lot until I get a satisfying solution to my problem and then release it once finished as I did see that most of the free tools for RTS unit selection go for the simple solution and do not give a very pleasant user experience.
     
  7. AnthonySharp

    AnthonySharp

    Joined:
    Apr 11, 2017
    Posts:
    88
    It's an interesting problem. I suppose for a 3D RTS game the only realistic way is to use something like WorldToScreenPoint. The only question is how. The best solution I think which I'm sure you've already thought of (and I think it's also similar to the one the guy was discussing in the video) is to give each unit a selection mesh that determines its possible selection bounds. E.g:

    cubes.jpg

    Amazing drawing skillz. Each unit has its own invisible selection mesh. When a dragging selection takes place, each vertex of each on-screen unit's selection mesh is WorldToScreenPointed; if any of the vertices are in the selection rectangle, the unit is selected.

    This might cause unexpected results. For example, even selecting the tiniest part of a unit might select it so long as one of its vertices are included. But you could fix this with supplementary logic, such as for instance perhaps requiring at least two vertices for each unit in order to be selected, or you could require that the 1 vertex that is selected is at least X pixels inside the edge of the selection square (maybe if X was say 5-20 pixels). I guess it's the sort of thing that needs tweaking to feel right. But sadly it can never be perfect, since it would be impractical to do a per-pixel raycast.

    The use of the selection meshes also saves you processing power since it means that you might not have to use the complex mesh colliders of the models at all; rather you can just use a simple cube, or any other mesh for that matter.

    You could even make a tool that auto-generates the selection meshes (cubes).

    The only other alternative I can think of is to do raycasts at say 5 pixel intervals, selecting everything that is hit ... but that's gonna take some processing power. For example, a full screen selection at 1920x1080 (nevermind 4k etc) would use 384*216 raycasts (82,944 raycasts). That's gonna cause an unhappy CPU/GPU.

    CRAZY THOUGHT (no idea if this would work), what if you projected an invisible 3D trigger/collider cuboid out of the camera so that its edges/faces perfectly aligned with the selection rectangle. Keep it there for 1/2 frames. THEN, if an object collided with the cube, it would be selected. I'm not sure if that would actually be any faster than using the selection meshes (it might actually be slower, since you don't have the benefit of the simpler selection mesh physics), but it might just be much easier to implement and crazy accurate (although, perhaps too accurate?). Just because something's accurate doesn't mean it feels good I suppose.
     
    Last edited: Aug 2, 2020
  8. Hellfire01

    Hellfire01

    Joined:
    Jul 14, 2016
    Posts:
    9
    No I did not think of it like that and it's an elegant solution x)
    The only issue I have with it would be that it cannot be standalone, you need to combine it with raycasts in order to allow single unit selection.

    This is what I am trying to do x) You can see it here :
    ( it's in the playlist I am trying to use )

    I am going to go along ( for now at least ) with my 3D mesh + collider. My hope is that by using very simple colliders + using a layer for the collider meshes is going to make it a bit less heavy on the CPU and should allow me be have a usable solution ( without having a frame loss I mean )

    Should it not work I will give your method a try as I do think it's a great idea x)

    Anyway, no matter what I choose to do, it's probably going to be a compromise. The selection will obviously run at 60 fps or more no problem as a standalone but I may need to redo it as the selection starts to compete for CPU resources with the rest of the game

    BTW : Supreme commander 2 ( of witch you made a screen ) possibly uses a solution close to what I described. I tried selecting very very close to the units and found out they do use a simple collider ( possibly a capsule or a cube ). It's noticeable when you click on the head or arms but cannot select or when you select by clicking between legs.
    However I dot not know if they use a selection mesh with a collider, raycasts, a WorldToScreen trick or a mix
     
  9. AnthonySharp

    AnthonySharp

    Joined:
    Apr 11, 2017
    Posts:
    88

    Maybe they don't even have separate selection meshes ... I suppose each unit's collider is probably just a cube/capsule anyways since that saves a lot of performance when you have hundreds if not thousands of units.

    My guess would be that they use raycasts for clicks but a mesh for selections.

    It would be interesting to know whether the WorldToScreen method or the 3D mesh method (where the mesh comes out of the camera) is faster, but I guess since selection is something which happens quickly and inoften it doesn't really matter ;) In that case the mesh method is probably better since it's easier to implement.

    Good luck!
     
  10. Hellfire01

    Hellfire01

    Joined:
    Jul 14, 2016
    Posts:
    9
    I think so too


    Thanks x)
    This is leagues harder than what I thought is way going to be. I do not think I will be implementing many methods, just the one I think works best x)
     
  11. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    2,398
    Completely off the cuff:

    What if you just do a Physics.BoxCastAll from the camera through the 2d selection box into the world and see what selectable things it hits?
     
  12. Hellfire01

    Hellfire01

    Joined:
    Jul 14, 2016
    Posts:
    9
    I am basing this answer on my understanding that the box cannot be deformed ( aka, you cannot have a top smaller than the bottom ). And since my understanding of it is quite frankly limited, I may be wrong.

    The issue I have with the box is precisely that it is a box and this means that the selection would not be pleasant for a non isometric camera ( would work absolutely fine for Age of Empires 2 but not Age of Empires 3 ). This is why I am trying to use a custom mesh + the camera's clipping planes.
    If I did not, the selection would appear normal for the user but you would get this very very thin box extending from the camera to the ground and even of a unit would appear within the box it would not.

    An alternative would be to get the corners of the selection as projected by the camera on the ground to get the "real" sized selection box but this introduces an other problem : you would not be able to select a unit if it was in front of the selection rectangle as the boxes would not be intersecting at all. As a user you would try to select a unity using the upper part and it would not work. It would however work when selecting the lower part as the boxes would intersect.

    If my understanding of the Physics.BoxCastAll is incorrect, please correct me, using the custom mesh is a massive pain and is not very enjoyable x)
     
unityunity