0
\$\begingroup\$

Yo fellow game developers!

So recently gamedev became my favorite hobby and I started working on my RTS game module which would then let me create different RTS games with just few changes (W3 inpiration :3). Anyway I am new to optimalization since my previous games had quite serious optimalization issues so I need your help with a pieace of code!

I am having a mass selection of units with drawn rectangle on screen. I've got it all worked out but now I am wondering if there could be anything done about optimalization for cycling through list of units every time my mouse position changes even for a little bit.

This is inside my Update function. There is much more in there so i cut it out but this is the piece I am worried about:

    void Update(){
        //...previous code blah blah blah...
        if (Input.GetMouseButton(0)) {
            oldPos2 = point2;
            point2 = Input.mousePosition;
            if (oldPos2!=point2) {//performance optimalization
                Debug.Log ("Performing selection");
                if (Vector2.Distance(point1,point2) >= selBoxIniDist) {
                    isSelecting = true;
                    foreach (var item in Game.Units) {
                        if (IsWithinSelectionBounds (item.gameObject)) {
                            item.Selected = true;
                        } else {
                            item.Selected = false;
                        }
                    }
                }
            }
        }
        //...rest of the code here...
    }

So as i told you, there is no problem with the code. I just need your opinion if there could be any changes especialy to this part of the code. Or maybe general advices for optimalization.

Even tho I am checking every update if the mouse moved (so I dont cycle every update) I still feel like checking every "milimeter" 5 times if any of the units are contained in the viewport rectangle is a little bit overkill just for the selecting units. At the end of selection I usualy end up with around less than 100 cycles.

What do you think?

EDIT: I might as well add a method that checks if the unit is in the viewport bounds and a utility method that calculate the bounds. Just so you why I am worried about performance.

Checking if bounds contain unit:

public bool IsWithinSelectionBounds( GameObject gameObject )
    {
        if (!isSelecting) {
            return false;
        }

        var camera = Camera.main;
        var viewportBounds = Utilities.GetViewportBounds( camera, point1, point2);

        return viewportBounds.Contains(camera.WorldToViewportPoint(gameObject.transform.position));
    }

Static utilities:

public static Bounds GetViewportBounds( Camera camera, Vector2 screenPosition1, Vector2 screenPosition2 )
{
    var cam = Camera.main;
    var v1 = cam.ScreenToViewportPoint( screenPosition1 );
    var v2 = cam.ScreenToViewportPoint( screenPosition2 );
    var min = Vector3.Min( v1, v2 );
    var max = Vector3.Max( v1, v2 );
    min.z = camera.nearClipPlane;
    max.z = camera.farClipPlane;

    var bounds = new Bounds();
    bounds.SetMinMax( min, max );
    return bounds;
}
\$\endgroup\$
2
\$\begingroup\$

As your edit mentions, culling these checks to things outside of your frustrum is a good start; which is essentially free because only those being animated are selectable. You may consider finding a way to store the list of references to the things you just drew to make this nearly free at the cost of a few KBs of memory.


The next thing is you only have to actually check FROM a single direction. That is to say that the units that are selected near the Right and Lower bounds (in most cases, what bounds your mouse is currently manipulating) are likely to be released. Thus you can imagine those within 100 pixels of these bounds are much more volatile than those 200 pixels away, and so forth.

One could thusly imagine that you could use a pair of Stack data structures, one that adds units from Starting Y to Finishing Y order, and another that adds units from Starting X to Finishing X order. These stacks would mean that you pop off units that are now out of your boundary and that as soon as you can no longer pop the next unit off you know that all other units are guaranteed to be within your boundary; negating their calculations.

Admittedly, this is a lot to go through for some cycles, but it probably looks pretty darned cool in action :p


It's worth noting you're losing a gigantic amount of cycles by casting your units from World to Viewport rather than the other way around. Each one those is series of Matrix Multiplies which are very expensive (relative to other code.) If you casted your two mouse coords (START and CURRENT) you would only ever do Two projections of points per frame. This is O(1) rather than O(n). Quite an improvement.


Lastly, two things to mention about your algorithm itself:

***You're calling a relatively expensive function of Distance on your two coordinates. I'm not sure why you're doing this given that you can just bounds check your units. The reason this is expensive is the Sqrt that is implicit in the call. Not huge, but it may be completely unnecessary (at least how I imagine I'd do it in the future.)

***Why are you selecting units when the user hasn't released the mouse button? Is this simply to draw a box around the soon-to-be selected units or similar? You can actually get rid of this entire check by the logic of most RTS out there and simply do this work in a pixel or vertex shader (outline or draw a box around the unit) and simply *only perform this check when the user releases their mouse-box-selection-click.

Best of luck! Hope this helps!

\$\endgroup\$
3
  • 1
    \$\begingroup\$ Yo thank you very much for your answer. It is a lot to process but I have got a lot of time on me atm. So just wanna clear out some things first. The distance check was to determine if user is selecting single unit but if he drags it little bit further apart from each other the group selecting begins. It was ment to save power when user doesnt want to group select. Also I was checking all the time when user dragged the mouse just because it looks better and it sometimes difficult to determine what units are gonna be in the selected area. (need to split this to 2 comments) ....... \$\endgroup\$ – Lucián Blažek May 15 '18 at 20:12
  • 1
    \$\begingroup\$ But as you said I am gonna drop it and only select the units after user releases the mouse button and also I am gonna try and use the screen to world points and cycle through units coordinates. And for the distance part I could do time check instead. Like "after click if time is greater than float beginSelecting" start selecting. \$\endgroup\$ – Lucián Blažek May 15 '18 at 20:15
  • \$\begingroup\$ The distance thing actually makes quite a bit of sense now. I could foresee adding something like that potentially as a ray-picking test for small values. Thanks for clarifying, glad you found some of my thoughts helpful \$\endgroup\$ – blurry May 15 '18 at 20:43
3
\$\begingroup\$

Unity internally organizes game objects in an AABB tree. This allows Unity to quickly get all game objects within an area without having to iterate every single object in the scene.

To make use of that, you might use Physics.OverlapBox or you could represent your selection-rectangle with a temporary BoxCollider and use the OnTriggerEnter and OnTriggerLeave events to set and unset the selection-flag.

\$\endgroup\$
1
  • \$\begingroup\$ Thank you very much for contribution. Definitely did not know about the AABB tree and will look into how that works. Also though about using physics overlapBox and boxcastall but at the end it seemed(and even more now when you pointed out the AABB) faster to calculate viewport and use .contains(transform). I will do some testing which one is faster and write it here. :) Thank you again. \$\endgroup\$ – Lucián Blažek Apr 29 '18 at 17:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.