Formation movement example
by Stephen Zepp · in RTS Starter Kit · 11/11/2004 (6:39 pm) · 27 replies
This small set of script mods allows you to set a formation that your selected group of units will march into when you set a move destination.
Currently, the formation itself is not capable of being "oriented", meaning that they all line up to the right/down of the destination point. My next goal (which I will probably need some assistance on for the target formation decal) is to have the player able to right click and hold on the destination point, which displays a grid of destination points (one for each unit). While the mouse is held, the player can rotate the formation in the X-Y plane to change the orientation of the formation when they reach the destination. The move will commence when the mouse is released.
Anyway, here's the code:
in file /client/scripts/inputHandler.cs:
Replace the entire function "function GuiRTSTSCtrl::onRightMouseDownTerrain(%this, %x, %y, %z)" with
(con't next message)
Currently, the formation itself is not capable of being "oriented", meaning that they all line up to the right/down of the destination point. My next goal (which I will probably need some assistance on for the target formation decal) is to have the player able to right click and hold on the destination point, which displays a grid of destination points (one for each unit). While the mouse is held, the player can rotate the formation in the X-Y plane to change the orientation of the formation when they reach the destination. The move will commence when the mouse is released.
Anyway, here's the code:
in file
Replace the entire function "function GuiRTSTSCtrl::onRightMouseDownTerrain(%this, %x, %y, %z)" with
function GuiRTSTSCtrl::onRightMouseDownTerrain(%this, %x, %y, %z)
{
if(%this.selectionIncludesTeam)
// first shot at formations. hard code a formation for now, make it a selectable
// choice (global var) later
commandToServer('IssueMove', $Player::CurSquadFormation, %x, %y, %z);
}(con't next message)
#2
11/12/2004 (3:16 am)
Stephen, don't have time to look over this in detail yet, but it looks good at first glance. Awesome to start seeing stuff like this already! Thanks!
#3
For example, we recognized as well that you didn't want to be sending a move command for each and every unit across the net, and our model was to have a "squad leader" designated unit within each squad, and only give commands to him. Each of the members of his squad would "draw" off of the squad leader's issued command using very basic flocking algorithms (each unit adapts a specific command that the flock leader is doing, in a relative manner).
Instead of actually assigning a squad leader (who could die, requiring squad leader re-designation methods, and other possible issues), you give a single command to the selection itself, send it across the net, and then apply the same concept of flocking as we planned, so it was an extremely simple thing to implement--the hardest part was figuring out what math was available in TScript, and what the actual relative position algorithm would be!
Now, the next level of functionality exposed my lack of broad general TGE knowledge. Formations need to be oriented (rotated about the destination point) so that the player can line up his formation better against the terrain. The rotation itself is easy, but the issue lies in the method used to have the player set the orientation at the destination.
My plan is to have a set of (I think) decals? that are procedurally generated based on the formation selected, and if a player has 1 or more units selected and right clicks on terrain, this decal would display at the mouse pointer, superimposed on the terrain (very similar to the editor terrain adjustment brush). While the right mouse button is held down, y-axis mouse movement (left/right) would rotate the decal about the mouse point. When the player lets go of the mouse, the last rotation is applied to the destination move spots.
The problem for me is that I don't have the TGE experience yet to have that decal generated, and then displayed on the client. If anyone has any suggestions, or stock code to look at that is similar, I'd love to hear them!
11/12/2004 (3:53 am)
@Josh: Thanks. As I said in my review, you guys made pretty much the same decisions in a lot of ways as our design did, which meant that the last 6 months of design is meshing very well!For example, we recognized as well that you didn't want to be sending a move command for each and every unit across the net, and our model was to have a "squad leader" designated unit within each squad, and only give commands to him. Each of the members of his squad would "draw" off of the squad leader's issued command using very basic flocking algorithms (each unit adapts a specific command that the flock leader is doing, in a relative manner).
Instead of actually assigning a squad leader (who could die, requiring squad leader re-designation methods, and other possible issues), you give a single command to the selection itself, send it across the net, and then apply the same concept of flocking as we planned, so it was an extremely simple thing to implement--the hardest part was figuring out what math was available in TScript, and what the actual relative position algorithm would be!
Now, the next level of functionality exposed my lack of broad general TGE knowledge. Formations need to be oriented (rotated about the destination point) so that the player can line up his formation better against the terrain. The rotation itself is easy, but the issue lies in the method used to have the player set the orientation at the destination.
My plan is to have a set of (I think) decals? that are procedurally generated based on the formation selected, and if a player has 1 or more units selected and right clicks on terrain, this decal would display at the mouse pointer, superimposed on the terrain (very similar to the editor terrain adjustment brush). While the right mouse button is held down, y-axis mouse movement (left/right) would rotate the decal about the mouse point. When the player lets go of the mouse, the last rotation is applied to the destination move spots.
The problem for me is that I don't have the TGE experience yet to have that decal generated, and then displayed on the client. If anyone has any suggestions, or stock code to look at that is similar, I'd love to hear them!
#4
Hopefully by that time we'll have a separate resource area for the kit (/nudge GG).
11/12/2004 (4:30 am)
Instead of posting changes to this "resource" as they happen, now that it's moving along I'm going to finish up some more implementation, and then provide it as a resource instead of cut/paste code.Hopefully by that time we'll have a separate resource area for the kit (/nudge GG).
#5
Re: showing formations on the terrain, I'd just genk code from the editor. :) Other useful places to genk code: terrain lights, the selection circle, and the shadow rendering code.
(sorry to be so brief, busy busy busy here :)
11/12/2004 (12:01 pm)
Stephen, I'll probably create a resource section this weekend. Just for you, buddy. ;)Re: showing formations on the terrain, I'd just genk code from the editor. :) Other useful places to genk code: terrain lights, the selection circle, and the shadow rendering code.
(sorry to be so brief, busy busy busy here :)
#6
Using the buttons that John put together, I added click functionality to these commands (so you don't have to use your console to manually change the formation type).
Keep in mind, this is an example--with just these two formation buttons we've used up our screen space here. I'd suggest that if you add a lot of formations, have a pop-up gui that lets you pick and choose from all of them.
On to the change:
First, you must grab the new buttons that he made, you can get them Here.
Unzip the buttons into your /starter.RTS/client/ui/commands directory.
Next, open up playGui.gui in a text editor, and add the following two new guis, after the current set of 4 CM_ buttons (right after the CM_HOLD one):
NOTE: They should be in the same grouping space as the other 4 CM_ buttons, and should (but don't have to be) at the end of that block.
at the bottom of the same file, add in:
Save the file, and fire up the demo!
11/16/2004 (6:24 pm)
Gui update to this example:Using the buttons that John put together, I added click functionality to these commands (so you don't have to use your console to manually change the formation type).
Keep in mind, this is an example--with just these two formation buttons we've used up our screen space here. I'd suggest that if you add a lot of formations, have a pop-up gui that lets you pick and choose from all of them.
On to the change:
First, you must grab the new buttons that he made, you can get them Here.
Unzip the buttons into your /starter.RTS/client/ui/commands directory.
Next, open up playGui.gui in a text editor, and add the following two new guis, after the current set of 4 CM_ buttons (right after the CM_HOLD one):
new GuiBitmapButtonCtrl(CM_FormSquare) {
profile = "GuiDefaultProfile";
horizSizing = "left";
vertSizing = "top";
position = "77 7";
extent = "32 32";
minExtent = "8 2";
visible = "1";
command = "CommandMenu::onFormSquareClick();";
text = "Hold";
groupNum = "-1";
buttonType = "RadioButton";
bitmap = "./commands/boxform";
};
new GuiBitmapButtonCtrl(CM_FormDblLine) {
profile = "GuiDefaultProfile";
horizSizing = "left";
vertSizing = "top";
position = "77 42";
extent = "32 32";
minExtent = "8 2";
visible = "1";
command = "CommandMenu::onFormDoubleLineClick();";
text = "Hold";
groupNum = "-1";
buttonType = "RadioButton";
bitmap = "./commands/lineform";
};NOTE: They should be in the same grouping space as the other 4 CM_ buttons, and should (but don't have to be) at the end of that block.
at the bottom of the same file, add in:
function CommandMenu::onFormSquareClick()
{
$Player::CurSquadFormation = "FormSquare";
}
function CommandMenu::onFormDoubleLineClick()
{
$Player::CurSquadFormation = "FormDoubleLine";
}Save the file, and fire up the demo!
#7
To fix this, in the file client/scripts/mapHud.cs, scroll down to the very bottom, then replace the entire function
with
11/16/2004 (6:33 pm)
John also found something I missed: the hook into clicking on the mini-map while moving in formation.To fix this, in the file client/scripts/mapHud.cs, scroll down to the very bottom, then replace the entire function
function GuiMapHud::onRightMouseDown(%this, %worldPos)
{
commandToServer('IssueMove',
getWord(%worldPos, 0),
getWord(%worldPos, 1),
getWord(%worldPos, 2));
}with
function GuiMapHud::onRightMouseDown(%this, %worldPos)
{
commandToServer('IssueMove', $Player::CurSquadFormation, "true",
getWord(%worldPos, 0),
getWord(%worldPos, 1),
getWord(%worldPos, 2));
}
#8
11/18/2004 (4:56 am)
I try and compile the above source but it comes up with a Buffer Overrun Detected.. i have tried severl times and have reinstalled several time by to no avail... can someone PLEASE help me
#9
These files will be loaded by your executable when you start the application, and handled internally by TGE/RTS. Just make the changes, and then run the rts demo application and you'll be good to go!
11/18/2004 (5:47 am)
@Byron--these are script files, you don't need to compile them with a compiler, if that's what you mean!These files will be loaded by your executable when you start the application, and handled internally by TGE/RTS. Just make the changes, and then run the rts demo application and you'll be good to go!
#10
function serverCmdIssueMove(%client, %formation, %x, %y, %z) to
function serverCmdIssueMove(%client, %formation, %limitFormationSpeed, %x, %y, %z)
11/18/2004 (11:22 am)
The buffer over run might be due to you using the limit speed to slowest unit resource released earlier by Stephen. You will need to make sure you are passing the correct number of arguments to the function:function serverCmdIssueMove(%client, %formation, %x, %y, %z) to
function serverCmdIssueMove(%client, %formation, %limitFormationSpeed, %x, %y, %z)
#11
11/18/2004 (11:31 am)
@Shawn: Hmm, good point, I hadn't thought about that.
#12
11/18/2004 (11:49 am)
Thanks for taking the time to release the resources! You guys are tearing up the RTS pack over there, uncovering all sorts of goodies.
#13
11/18/2004 (12:12 pm)
@Shawn: Thanks! Now that it looks like we have this last bug cleared up, we're looking to move into the fun stuff in full force--so far it's just been my "end of the day, do something that works!" contributions ;)
#14
07/21/2005 (6:40 am)
Dont know if anyone is using this now but add this and you have your formation rotate right, oh and also I changed to one single line formation%center = "0 0 0";
// finding the center of the selected group
for (%i = 0; %i < %numUnitsSelected; %i++)
{
%center = VectorAdd(%center, %client.selection.getObject(%i).getPosition());
}
%center = VectorScale(%center, 1.0 / %numUnitsSelected);
//dawe rotate
%anglex = %x - getWord(%center, 0);
%angley = %y - getWord(%center, 1);
//echo("anglex " @ %anglex @ " angley " @ %angley);
%spacey = mSin(mAtan(%anglex, %angley));
%spacex = -mCos(mAtan(%anglex, %angley));
//dawe rotate
for(%i = 0; %i < %numUnitsSelected; %i++)
{
%obj = %client.selection.getObject(%i);
%dest = %x SPC %y SPC %z;
switch$(%formation)
{
case "FormDoubleLine":
// calculate number of columns.
/
/dawe %numColumns = mFloor(%numUnitsSelected / 2);
%numColumns = mFloor(%numUnitsSelected);
// find out if this count is even or odd
//if ( %i % 2 > 0 )
//%rowOffset = 0;
//else
%rowOffset = 1; // set column spacing
//dawe
%ColSpacing = 5 * %spacex;
%rowSpacing = 5 * %spacey;
// right now we will treat the destination position as the top left corner of the formation
// so rows are always to the positive. when we add in formation orientation, this will rotate
// about the destination
//%offset = ( mFloor(%i / 2) * %ColSpacing ) SPC ( %RowOffset * %RowSpacing ) SPC 0;
%offset = ( mFloor(%i) * %ColSpacing - %ColSpacing *%numUnitsSelected /2) SPC ( mFloor(%i) * %RowSpacing - %RowSpacing *%numUnitsSelected /2) SPC 0;
#15
12/16/2006 (7:49 am)
@David - might be smart not to post 2 update through each other. ive tried it out but i dont quite understand how the rotating works (im kind of new to this though). when I try to update back to the old double line it lines up as straight as without your update.
#16
05/04/2008 (5:00 pm)
Hey I know this is an old topic but I am trying to figure out how to make the formation a large circle instead of a square. Any suggestions? Thanks
#17
I havent tested this out at all. but to give you an idea:
first off, make commands to set the formation type to "formCircle", for example.
then find this:
and add a case "formCircle": to the switch$ block.
there you for example could calculate the angle in rads for the current unit:
hope this works for you.
05/05/2008 (3:18 am)
Hi Jesse,I havent tested this out at all. but to give you an idea:
first off, make commands to set the formation type to "formCircle", for example.
then find this:
function serverCmdIssueMove(%client, %formation, %x, %y, %z)
{
....
switch$(%formation)
{
}
}and add a case "formCircle": to the switch$ block.
there you for example could calculate the angle in rads for the current unit:
%rad = 2 * 3.14159265358979323846 * %i / %numUnitsSelected; // is there a define of pi in script? %vec = mCos(%rad) SPC mSin(%rad) SPC 0; %offset = vectorScale(%vec, %radius); // may be smart calculating %radius so it is dependant on %numUnitsSelected
hope this works for you.
#18
also trying to get ridd of the diagnal part of the linear formation, I want it only virtical or horizontal, it's just a training simulator so doesn't really need to be diagnal
05/07/2008 (10:55 am)
Thanks I'll play with it, also trying to get ridd of the diagnal part of the linear formation, I want it only virtical or horizontal, it's just a training simulator so doesn't really need to be diagnal
#19
05/07/2008 (11:13 am)
Hey the circle formula you gave is a great start, thanks, but I keep messing with it and all the units keep going into the same coordinate
#20
This is all I put:
05/07/2008 (2:17 pm)
Fixed it, but it is always the same size circle, it's not that important and is OK as is but something to maybe go back to later on down the road.This is all I put:
%rad = 2 * 3.14159265358979323846 * %i / %numUnitsSelected; %offset = mCos(%rad) * 15 SPC mSin(%rad) * 15 SPC 0;And it gives me a nice looking circle formation. Thanks! Now how would I get the linear formation to only give me virticle or horizontal but never diagonal? Thanks
Torque 3D Owner Stephen Zepp
Replace the entire function "function serverCmdIssueMove(%client, %x, %y, %z) with
function serverCmdIssueMove(%client, %formation, %x, %y, %z) { if (%client.selection.getObject(0).getTeam() != %client.getTeam()) return; %numUnitsSelected = %client.selection.getCount(); for (%i = 0; %i < ClientGroup.getCount(); %i++) { %cl = ClientGroup.getObject(%i); %cl.sendMoveEvent(%client.selection, %x SPC %y); } %center = "0 0 0"; // finding the center of the selected group for (%i = 0; %i < %numUnitsSelected; %i++) { %center = VectorAdd(%center, %client.selection.getObject(%i).getPosition()); } %center = VectorScale(%center, 1.0 / %numUnitsSelected); for(%i = 0; %i < %numUnitsSelected; %i++) { %obj = %client.selection.getObject(%i); %dest = %x SPC %y SPC %z; switch$(%formation) { case "FormDoubleLine": // calculate number of columns. %numColumns = mFloor(%numUnitsSelected / 2); // find out if this count is even or odd if ( %i % 2 > 0 ) %rowOffset = 0; else %rowOffset = 1; // set column spacing %ColSpacing = 5; %rowSpacing = 5; // right now we will treat the destination position as the top left corner of the formation // so rows are always to the positive. when we add in formation orientation, this will rotate // about the destination %offset = ( mFloor(%i / 2) * %ColSpacing ) SPC ( %RowOffset * %RowSpacing ) SPC 0; echo("Set FormDoubleLine Destination: Unit Count " @ %i @ " form offset " @ %offset); case "FormSquare": // calculate number of columns/rows. %numColumns = mFloor(mSqrt(%numUnitsSelected) ); if (%numColumns * %numColumns < %numUnitsSelected) %numColumns++; echo("FormSquare: numColumns is " @ %numColumns); // set column spacing %ColSpacing = 5; %RowSpacing = 5; // right now we will treat the destination position as the top left corner of the formation // so rows are always to the positive. when we add in formation orientation, this will rotate // about the destination %offset = ( %i % %numColumns) * %ColSpacing SPC ( mFloor( %i / %numColumns) * %RowSpacing ) SPC 0; echo("Set FormSquare Destination: Unit Count " @ %i @ " form offset " @ %offset); default : // default to maintain relative position %offset = VectorSub(%obj.getPosition(), %center); } %dest = VectorAdd(%dest, %offset); %obj.clearAim(); %obj.setMoveGoal(%dest); } }To change the formation type, open the console window and type:
$Player::CurSquadFormation = "FormSquare";
or
$Player::CurSquadFormation = "FormDoubleLine";
Then select your units and make 'em move!
Comments/suggestions/critiques are requested.