RTS Building Selection Box

S

squirrelah

Guest
upload_2019-8-19_20-0-48.png

I am trying to recreate something very similar to what starcraft/warcraft series has as their UI in the lower right-hand corner and I just wanted some opinions on what the most efficient way to do this would be.

The main features I would like is basically whenever a specific type of unit is selected different commands will be available. A lot of units will have the same commands(attack, move, etc) but "Builder" units will be able to click or press a hotkey to open up more options(effectively options to place different kinds of buildings).

as far as I can tell there are basically two ways to do this.

1. Have a bunch of logic that determines when units are selected, it determines which options are available(so if you have a builder and a non-builder, does it let you open up the building menu?). This seems like it is going to be mandatory either way I go.


2.a Have 15 different(assuming I am using a 3x5 grid as the picture above shows, and I am using all available space) point_in_rectangle functions running at the same time, and constantly updating(in accordance to the camera moving). And then having a global.left_pressed variable, with a controller object so that if it is in the point_in_rectangle and global.left_pressed the option would be chosen(or if they press the appropriate hotkey).


2.b Or I can have 15 different objects(or instances of the same object but with slightly more complicated code) that are created/destroyed based on what the player has chosen. And I would use the Left-pressed Event for each instance/object. For example, if they have a builder and open the building box, it will destroy all the instances from the previous menu and create new ones that if left pressed(or hotkey is pressed) it will initiate the process of constructing a building.

These objects will also have to follow the player around based on how they are moving the map in order to stay attached the to GUI.



I genuinely can't tell which one would be harder to do or which one would be more efficient from an optimization standpoint. I'm also slightly less concerned about optimization although it is important and more worried about making sure that the controls are as fluid as they can be, and there's no way for someone to press a button and something not run quick enough for it to register in time. I don't think there will be a difference in this regard but I'd hate to start building it and then realize halfway through the way I am doing it there's going to be an inherent lag or something.
 

TheouAegis

Member
Well personally I like making things ridiculously difficult for the sake of logic. lol I'd assign all units a TYPE identifier, allowing every unit to have multiple types. Taking the original Starcraft for example, I'd have something like
enum types {
attacker = 1,
gather = 2,
builder = 4,
burrow = 8,
underground = 16,
skill1 = 32,
skill2 = 64,
skill3 = 128
}
And it would keep going for everything that would effect what buttons are available. You'd assign multiple types using the | operator between them.
TYPE = types.attacker|types.gather|types.builder|types.burrow
This would be what a zerg drone might look like. You don't need to assign all the types right away either. As certain abilities are learned, you can add them to units. For example, if drones can't burrow right away, then the code could be changed to something like
TYPE = types.attacker|types.gather|types.builder|(types.burrow * abilityZergBurrow)

For determining what buttons to show, you could then set your starting position variable to 0 (to denote you are figuring out what the first button, if any, will be). Check if the TYPE is set to a particular value, render the button if it is and then increase the position. Your buttons themselves would just be values you save in an array (we'll call it BUTTONS[0...14] for this example).
BUTTONS = 0; //clear the BUTTONS array
var button_pos = 0;
var n = TYPE;
var i = 1;
while n != 0 {

if n & 1 {
if i < 6 //so if we are not looking at skill1,skill2 or skill3
BUTTONS[button_pos++] = i;
else
BUTTONS[button_pos++] = 6 + mySkills[i-6];
n = n >> 1;
i++;
}
Then you'd have an array listing all the button sprites that would need to be rendered in order, with entry 0 being no sprite. Skill icons would be included as well. Each unit would have a mySkills array storing the skill index for each skill it owns (starting from 0).
 
S

squirrelah

Guest
Thanks, that makes sense i think i am going to do something really similar to that.
 

rytan451

Member
Here's how I would do it:
I'd have a single ControlPanel object. Units that are controllable have a controlPanel array 15 items long associated with their type, and each of the array elements is associated with one of the fifteen squares. I also use loads of scripts, which is for maintainability, scalability, and ease of reading.

When the player clicks in the control panel area (I'd check with point_in_rectangle in the Global Mouse Pressed event), I would then do this:

Code:
/// @desc ControlPanel Global Mouse Pressed

if point_in_rect(mouse_x, mouse_y, x, y, x + width, y + height) {
  var button_x = floor((mouse_x - x)*inv_button_width)
  var button_y = floor((mouse_y - y)*inv_button_height)

  var selected = get_selected()
  execute_action(selected, selected.button_action[button_x + 5 * button_y])
}
This makes button pressing effectively free (excepting get_selected(), but that should be also relatively fast). Considering that this can happen at most once per frame (probably much less), any effort spent on optimising should be spent elsewhere.

For hotkeys:

Code:
/// @desc ControlPanel Any Key Pressed

var selected = get_selected()

var len = ds_list_size(selected.hotkey_actions)

for (var i = 0; i < len; i++) {
  if hotkey_get_active(selected.hotkeys[| i]) {
    var action = selected.hotkey_actions[? selected.hotkey[| i]]
    execute_action(selected, action)
    hotkey_set_activated(selected.hotkeys[| i])
  }
}
I would abstract execute_action(selected, action) to a script, as well as get_selected(). You could place more logic into get_selected() or execute_action(), and easily change it without having to comb through code. On the plus side, like StarCraft 2, you can make get_selected return an abstract Group, then you can use the most appropriate member of the Group to perform an action like Psionic Storm, Snipe, or Fungal Growth rather than just any member. Likewise, you can use it to make all members of the group perform an action like Burrow, Takeoff/Land, or Psionic Transfer rather than just one. You can also make get_selected() return the current active group, much like in StarCraft 2, in which you can press [tab] to change which unit type selected is casting a spell.

Names are, of course, up to you.
 
Top