This is update is all about the RTS player controller I’ve been developing sprinkled with a couple camera things. So let’s get started.

NOTE: My implementation is partially based on this post.

Box Select

The majority of the work has gone into box select for units. This requires a few steps.

  1. Differentiating between clicks and box selection.
  2. Rendering the box selection screen space UI.
  3. Querying for all units inside the box.
  4. Of the units in the box, determine what to select.
  5. Of the selected units, determine the lowest common denominator of unit actions.

Clicks and Drag Select

The first part is pretty simple. A click is basically a key down followed by a key up within a certain amount of time. Drag select is gated by a minimum pixel distance. If the mouse hasn’t moved very far, we just consider it a regular click.

Render Box Select UI

This part is as simple as calling GUI.DrawTexture(). One call for drawing the background of the whole box and 4 more to draw the border. A white texture is used so that it absorbs all of the color tint when GUI.color is assigned.

Querying For Units

My original thought for checking if a unit was within the box was to do a shape cast. However, Unity doesn’t support doing a frustum cast, or even constructing a frustum that doesn’t match a camera in the scene. I could have done the math and constructed the data structures myself, but that didn’t feel like it was worth the effort.

Taking a hint from the post linked above, I decided to go to the viewport space conversion. However, this meant that I couldn’t just query for what units where inside the shape with a simple call. I had to manually iterate over units and check if they were in the bounds. Obviously I don’t want to iterate over all the units in the scene, since only units that are on the screen are potential selections. To keep track of which units are on screen, I utilize Unity’s OnBecameVisible() and OnBecameInvisible() callbacks.

Now that the search space has been reduced to a list of only potential candidates, we need to check if they are in our box. The article above only checks against the unit’s position, but I wanted something a bit better. Unity only keeps track of an object’s AABB. Transforming the AABB to viewport space yielded some wonky results, and I thought I could do something a little more performant. In most RTSs, selected units are usually represented with a ring around them. I figured this would be a good way to detect selection as well. I generate a 2D circle at the base of the unit and convert points along that circle into viewport space. I then check if any of the rays that make up the circle intersect with the viewport bounds. This method seemed to work fairly well. For longer units, using an ellipse would be more accurate.

Unit Selection

Not all units are made equal. Units are prioritized as such:

If a unit has a lower priority than the current selection, it is discarded. Units with higher priority will invalidate the entire selection and the list will be reduced to the new, highest priority unit. When doing box select, only owned units will result in a selection of more than one unit. Any priority less than that will pick the first unit encountered.

Unit Actions

This step only applies when selecting owned units. The final step is to populate the UI with the intersection of all the unit actions of the selected units. Essentially:

 1 2 3 4 5 6 7 8 910
List<Actions> actions = units[0].actions;

for (int i = 1; i < units.Length; ++i) {
	for (int j = 0; j < actions.Count; ++j) {
		if (!units[i].actions.Contains(actions[j])) {
			actions.RemoveAt(j);
			--j;
		}
	}
}

The GIF below was taken before the Unit Actions segment was finished, but it shows off the essential parts.

Camera

My camera is nothing fancy. Every movement, whether it’s panning, zooming, or rotating, is done with a simple spring calculation. I have a minimum speed that all springs operate under so that they don’t slow down too much and eventually get to their destination in a reasonable time. I had panning written already, I just added support for zooming and rotating.

My camera mimics the StarCraft 2 camera style. If I have a need for a more control over the camera, adding that should be easy. I already have the core movement, it’s more input interpretation.