0
\$\begingroup\$

What is a good way of implementing a RTS selection box for tiles on a grid? I'm making a game with some similarities to Evil Genius, the building in particular. In that game you click one place, drag the cursor somewhere else and something similar to the GIF below shows.

To do so a grid and a way of translating mousePosition to the correct cell is needed. Then a set of for loops (one for x and one for y) for executing the necessary action on each cell. My main problem is accounting for the direction of dragging, and in particular when shrinking. My current solution (hastebin here) is rather crude, and I would like a more elegant solution for a more civilized age. Hopefully someone else can think of a easier way of accounting for drag direction, I surely can't.

enter image description here

Any help appreciated.

\$\endgroup\$
  • \$\begingroup\$ Because your selection volume isn't one prefab, but several, I think the way you're doing it is going to be pretty darn close to the best you're going to get: creating a list of the current prefabs and calculating the difference between the need and have, and spawning new ones or destroying old ones as needed. You should be able to do it just based off the mouse coords, rather than having to predict the drag direction. \$\endgroup\$ – Draco18s Dec 11 '15 at 15:02
  • \$\begingroup\$ I don't see how I can do this just based on mouse coordinates. \$\endgroup\$ – larsekje Dec 11 '15 at 15:41
  • \$\begingroup\$ Well, if you save where the mouse started and compare to where the mouse is, that gives you a rectangular area, doesn't it? \$\endgroup\$ – Draco18s Dec 11 '15 at 15:58
  • \$\begingroup\$ Yes, but I don't see how I can use the rectangular area to access each cell within said area. \$\endgroup\$ – larsekje Dec 11 '15 at 16:05
  • \$\begingroup\$ Your starting coordinates are just offsets to variables used in a standard for loop. \$\endgroup\$ – Draco18s Dec 11 '15 at 16:10
0
\$\begingroup\$

The approach I would take is to track the selected squares and only add/remove squares as necessary when the drag-to location changes.

On option to making that efficient is a data structure which has good insertion and deletion times such as a R-B tree.

Another option is to maintain the selection in duplicate storage, one in row vector form and one in column vector form. This might be better because as you move you will always be losing or adding a row or column segment, assuming you can transform one to the other efficiently.

Another option is to use an interval tree to track the selected zones.

\$\endgroup\$
-1
\$\begingroup\$

For anyone interested, here is the solution I (with great help from Draco18s) came up with:

void SelectionBox() {

    //Get end position
    Vector3 endCoordinate = FindCoordinate(FindHitPoint(), CellSize());
    int xEnd = (int)endCoordinate.x;
    int zEnd = (int)endCoordinate.z;

    //Return if no change in mouse position
    if (previousX == xEnd && previousZ == zEnd) return;

    temporarySelectedCells.Clear();

    //Get corners of selection rectangle
    int left, right, bottom, top;

    if (xStart > xEnd) {
        left = xEnd;
        right = xStart;
    } else if (xStart < xEnd) {
        left = xStart;
        right = xEnd;
    } else {
        left = xEnd;
        right = left;
    }

    if(zStart > zEnd) {
        bottom = zEnd;
        top = zStart;
    } else if(zStart < zEnd) {
        bottom = zStart;
        top = zEnd;
    } else {
        bottom = zEnd;
        top = bottom;
    }

    // Loop through selected cells and mark as selected
    for(int x = left; x <= right; x++) {
        for (int z = bottom; z <= top; z++) { 
            Cell cell = FindCell(x, z);
            AddToSelection(cell);

        }
    }

    // Check if selected cells are still needed (inside selection rectangle)
    for (int i = currentCells.Count - 1; i >= 0; i--) {
        Cell cell = currentCells.ElementAt(i);
        if (!temporarySelectedCells.Contains(cell)  && !selectedCells.Contains(cell)) {
            RemoveFromSelection(cell);
        }
    }

    //Set current position as previous for next frame
    previousX = xEnd;
    previousZ = zEnd;

}
\$\endgroup\$
  • \$\begingroup\$ Glad I could help. :) \$\endgroup\$ – Draco18s Dec 14 '15 at 15:14

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.