Camera Setup
First we're going to set up our camera mode framework. In scripts/server/commands.cs we're going to add some functions to set and toggle our camera modes. The following code can sit at the end of the script file:
Code: Select all
// ----------------------------------------------------------------------------
// Camera commands
// ----------------------------------------------------------------------------
function serverCmdorbitCam(%client)
{
%client.camera.setOrbitObject(%client.player, mDegToRad(20) @ "0 0", 0, 5.5, 5.5);
%client.camera.camDist = 5.5;
%client.camera.controlMode = "OrbitObject";
}
function serverCmdoverheadCam(%client)
{
%client.camera.position = VectorAdd(%client.player.position, "0 0 30");
%client.camera.lookAt(%client.player.position);
%client.camera.controlMode = "Overhead";
%client.camera.position = Cam1.position;
%client.camera.rotation = Cam1.rotation;
}
function serverCmdtoggleCamMode(%client)
{
if(%client.camera.controlMode $= "Overhead")
{
%client.camera.setOrbitObject(%client.player, mDegToRad(20) @ "0 0", 0, 5.5, 5.5);
%client.camera.camDist = 5.5;
%client.camera.controlMode = "OrbitObject";
}
else if(%client.camera.controlMode $= "OrbitObject")
{
%client.camera.controlMode = "Overhead";
%client.camera.position = VectorAdd(%client.player.position, "0 0 30");
%client.camera.lookAt(%client.player.position);
}
}
function serverCmdadjustCamera(%client, %adjustment)
{
if(%client.camera.controlMode $= "OrbitObject")
{
if(%adjustment == 1)
%n = %client.camera.camDist + 0.5;
else
%n = %client.camera.camDist - 0.5;
if(%n < 0.5)
%n = 0.5;
if(%n > 15)
%n = 15.0;
%client.camera.setOrbitObject(%client.player, %client.camera.getRotation(),
0, %n, %n);
%client.camera.camDist = %n;
}
if(%client.camera.controlMode $= "Overhead")
{
%client.camera.position = VectorAdd(%client.camera.position, "0 0 " @ %adjustment);
}
}
Code: Select all
function toggleCameraMode(%val)
{
if (%val)
commandToServer('toggleCamMode');
}
moveMap.bind( keyboard, "ctrl m", toggleCameraMode);
function mouseZoom(%val)
{
if(%val > 0)
{
commandToServer('adjustCamera', -1);
}
else
{
commandToServer('adjustCamera', 1);
}
}
moveMap.bind(mouse, "zaxis", mouseZoom);
Mouse Setup
The following code will change the way mouse input affects movement and click interaction.
Mouse Cursor Toggling
Normally, the camera is controlled by an actor in FPS (aim) mode. To focus on just mouse and camera work, we need to change how the default camera is controlled. Open game/scripts/server/gameCore.cs. In function GameCore::preparePlayer(%game, %client), locate the following line:
Code: Select all
%game.spawnPlayer(%client, %playerSpawnPoint);
Code: Select all
%game.spawnPlayer(%client, %playerSpawnPoint, false);
Immediately below the %game.spawnPlayer() function, add the following code:
Code: Select all
// Set camera to Overhead mode
commandToServer('overheadCam');
Code: Select all
// Turn mouse cursor on or off
// If %val is true, the button was pressed in
// If %val is false, the button was released
function toggleMouseLook(%val)
{
// Check to see if button is pressed
if(%val)
{
// If the cursor is on, turn it off.
// Else, turn it on
if(Canvas.isCursorOn())
hideCursor();
else
showCursor();
}
}
// Bind the function toggleMouseLook to the keyboard 'm' key
moveMap.bind(keyboard, "m", "toggleMouseLook");
If you start the game now, it will still default to a free flying (mouse look) camera. By hitting the 'm' key you will be able to toggle "mouse look" mode. If mouse look is on, you can control your view direction by moving the mouse. If it is off, you can move your cursor around on the screen. You can switch back to an actor controlled camera by pressing Alt + C.
We will go ahead and force the cursor to be on as soon as the level loads. Open game/art/gui/playGui.gui. You can edit .gui files just like any other script file. Look for the noCursor field. Make the following change to this field:
Code: Select all
noCursor = "0";
Placing Structures Using The GUI
First, open art/gui/PlayGui.gui and find new GuiControl(DamageHUD) toward the end of the file and remove the entire block of code (ensure that you do not delete the main block's closing brace - it's the one with the semi-colon after it). It occupies the center of the screen when you are in play mode and it will block mouse clicks to that area.
Optionally, you can remove this control element using the Gui Editor.
Next, open the GUI Editor by pressing F10 or by clicking the GUI Editor button from the World Editor

Open the Library tab, pull down the Buttons rollout, then click and drag the GuiBitmapButtonCtrl onto the GUI to create a new button.

Select your new button and change the settings as follows:

Browse to your project's art/gui folder and select the orcburrow.png ,or any image that you have, file for the button image.

If you close the GUI Editor ( F10 ) you should now see your button in the game UI.

Next, open scripts/gui/playGui.cs and add the following code at the end:
Code: Select all
Next, open scripts/gui/playGui.cs and add the following code at the end:
// onMouseDown is called when the left mouse
// button is clicked in the scene
// %pos is the screen (pixel) coordinates of the mouse click
// %start is the world coordinates of the camera
// %ray is a vector through the viewing
// frustum corresponding to the clicked pixel
function PlayGui::onMouseDown(%this, %pos, %start, %ray)
{
// If we're in building placement mode ask the server to create a building for
// us at the point that we clicked.
if (%this.placingBuilding)
{
// Clear the building placement flag first.
%this.placingBuilding = false;
// Request a building at the clicked coordinates from the server.
commandToServer('createBuilding', %pos, %start, %ray);
}
else
{
// Ask the server to let us attack a target at the clicked position.
commandToServer('checkTarget', %pos, %start, %ray);
}
}
// This function is the callback that handles our new button. When you click it
// the button tells the PlayGui that we're now in building placement mode.
function orcBurrowButton::onClick(%this)
{
PlayGui.placingBuilding = true;
}
Code: Select all
function serverCmdcreateBuilding(%client, %pos, %start, %ray)
{
// find end of search vector
%ray = VectorScale(%ray, 2000);
%end = VectorAdd(%start, %ray);
// set up to look for the terrain
%searchMasks = $TypeMasks::TerrainObjectType;
// search!
%scanTarg = ContainerRayCast( %start, %end, %searchMasks);
// If the terrain object was found in the scan
if( %scanTarg )
{
// get the world position of the click
%pos = getWords(%scanTarg, 1, 3);
// Note: getWord(%scanTarg, 0) will get the SimObject id of the object
// that the button click intersected with. This is useful if you don't
// want to place buildings on certain other objects. For instance, you
// could include TSStatic objects in your search masks and check to see
// what you clicked on - then don't place if it's another building.
// spawn a new object at the intersection point
%obj = new TSStatic()
{
position = %pos;
shapeName = "art/shapes/building/orcburrow.dts";
collisionType = "Visible Mesh";
scale = "0.5 0.5 0.5";
};
// Add the new object to the MissionCleanup group
MissionCleanup.add(%obj);
}
}

Optionally, you can remove the DamageHUD element from the PlayGui - it is not useful in a strategy game and it happens to block mouse clicks.

In the level editor select SpawnSphere and change :

Move your camera to the position you want your first marker to be and get your angle dialed in. Once your camera is in position, open the Library tab of the Scene Tree, browse to the Level tab's Level folder and double click the Player Spawn Sphere item to create a new Spawn Sphere.

Name your new Spawn Sphere Cam1.

Pull down the Object menu and select Drop Location > at Camera w/Rotation.

Now you can drop your Spawn Sphere so that it will be in the correct place and face the correct direction by selecting Object > Drop Selection with Cam1 selected. You can optionally use the shortcut CTRL-D.

You may see your screen turn green. This is not a bug, you've just pulled the Spawn Sphere over your head and you're seeing the world through emerald-tinted glasses. The Spawn Sphere should be placed and facing exactly the way the camera currently is, so this is a good sign.
Now, to create the panning camera:
Go in to Gui Editor-> Library and create one GuiButtonBaseCtrl.
Click on the GUI tab and follow the picture :

Now this Gui button will be set on the upper side of the screen. If you select it you should see some thing like this :

If you click and hold on it you should see a cross. Now drag it to the middle of the screen and squar should appear :

Resize the square to fit the width of the screen :

Now move it to the upper side of the screen untile you get some thing like this :

The are that is market red will be the are that is sensitive to the cursor.
Now that we made the Up part is time to make the Down part.
Repeat the steps on making the GuiButtonBaseCtrl. But now use the following settings:

And move it on the lower part of the screen.
Let’s do the Left and Right.
For Left you have :

Don’t forget to resize it and move it to the left side

For right you have :

Remember to move it to the right.
Now for the last part:
Open: scripts\gui\PlayGui.cs . At the end of the file paste the following :
Code: Select all
function panUp::onMouseEnter(%this)
{
echo("- movement UP enter " @ %this.Name);
//commandToServer('adjustCamera', 1);
setSpeed(100);
moveforward(1);
}
function panUp::onMouseLeave(%this)
{
echo("- movement UP exit " @ %this.Name);
setSpeed(100);
moveforward(0);
}
function panDown::onMouseEnter(%this)
{
echo("- movement DOWN enter " @ %this.Name);
//commandToServer('adjustCamera', -1);
setSpeed(100);
movebackward(1);
}
function panDown::onMouseLeave(%this)
{
echo("- movement DOWN exit " @ %this.Name);
setSpeed(100);
movebackward(0);
}
function panLeft::onMouseEnter(%this)
{
echo("- movement LEFT enter " @ %this.Name);
setSpeed(100);
moveleft(1);
//yaw(10);
}
function panLeft::onMouseLeave(%this)
{
echo("- movement LEFT exit " @ %this.Name);
//yaw(0);
moveleft(0);
}
function panRight::onMouseEnter(%this)
{
echo("- movement RIGHT enter " @ %this.Name);
setSpeed(100);
moveright(1);
}
function panRight::onMouseLeave(%this)
{
echo("- movement RIGHT exit " @ %this.Name);
setSpeed(100);
moveright(0);
}
Code: Select all
function PlayGui::onWake(%this)
Code: Select all
if ( isObject( MainChatHud ) )
{
Canvas.pushDialog( MainChatHud );
chatHud.attach(HudMessageVector);
}
Remember the old function “serverCmdcreateBuilding”. We need to change that to add a new turret.
Code: Select all
function serverCmdcreateBuilding(%client, %pos, %start, %ray)
{
// find end of search vector
%ray = VectorScale(%ray, 2000);
%end = VectorAdd(%start, %ray);
// set up to look for the terrain
%searchMasks = $TypeMasks::TerrainObjectType;
// search!
%scanTarg = ContainerRayCast( %start, %end, %searchMasks);
// If the terrain object was found in the scan
if( %scanTarg )
{
// get the world position of the click
%pos = getWords(%scanTarg, 1, 3);
// Note: getWord(%scanTarg, 0) will get the SimObject id of the object
// that the button click intersected with. This is useful if you don't
// want to place buildings on certain other objects. For instance, you
// could include TSStatic objects in your search masks and check to see
// what you clicked on - then don't place if it's another building.
// spawn a new object at the intersection point
%obj = new AITurretShape()
{
datablock = AITurret;//%client;
//rotation = "0 0 1 " @ getWord(%rot, 2);
count = 1;
position = %pos;
//sourceObject = %user;
//client = %user.client;
isAiControlled = true;
};
MissionGroup.add(%obj);
// Add the new object to the MissionCleanup group
//MissionCleanup.add(%obj);
}
}
First you will need to add a GuiTextCtrl so the user can see the money.
To create a GuiTextCtrl press F10 or go to the gui editor -> click on the Library->expand Text and click on the GuiTextCtrl.
Click on GUI on the tree you should see

Click it.
On the “name” field add a name, in my case “TotalMoney”.
On the “text” field you could add a default text, if you want to.
Now is time for scrypt, modification.
Go back in the file commands.cs (this is temporary) .
A bit of info: to make global variables in Torque scrypt you must use “$” at the begining of every variable. For the local variables use “%” at the begining of the variable.
With that knowledge we shall define a variable $money

And add some functions for later:
Code: Select all
$money = 9000;
Function PrintMoney()
{
echo("Money = " @ $money);
}
function SetMoney(%val)
{
$money = %val;
}
function SetMoney(%val)
{
$money = %val;
MoneyGuiPrinter($money);
}
function AddMoney(%val)
{
$money = $money + %val;
}
function SubtractMoney(%val)
{
if($money >= %val)
{
$money = $money - %val;
}
}
function MoneyGuiPrinter(%val)
{//Used to set the text value of the gui text element
TotalMoney.setText("Money : " @ %val );
}
Go in the function “serverCmdcreateBuilding” after “MissionGroup.add(%obj);” add
“ MoneyGuiPrinter($money); “
Run the game, add a turret. You should see:

Let’s create a system for the cost of the turrets.
The idea: Every turret has a cost, the user will have a bank account called “$money” in scrypt(you could change its name).
If the cost of the turret is greater then the amount of money in the user account the game will show a warning message. For now we will just change the text from ower text box.
Code: Select all
If the cost of the turret is greater then the amount of money in the user account the game will show a warning message. For now we will just change the text from ower text box.
function serverCmdcreateBuilding(%client, %pos, %start, %ray)
{
// find end of search vector
%ray = VectorScale(%ray, 2000);
%end = VectorAdd(%start, %ray);
%cost = 200;
// set up to look for the terrain
%searchMasks = $TypeMasks::TerrainObjectType;
// search!
%scanTarg = ContainerRayCast( %start, %end, %searchMasks);
// If the terrain object was found in the scan
if( ( %scanTarg ) && (%cost <= $money) )
{
// get the world position of the click
%pos = getWords(%scanTarg, 1, 3);
// Note: getWord(%scanTarg, 0) will get the SimObject id of the object
// that the button click intersected with. This is useful if you don't
// want to place buildings on certain other objects. For instance, you
// could include TSStatic objects in your search masks and check to see
// what you clicked on - then don't place if it's another building.
// spawn a new object at the intersection point
%obj = new AITurretShape()
{
datablock = AITurret;//%client;
//rotation = "0 0 1 " @ getWord(%rot, 2);
count = 1;
position = %pos;
//sourceObject = %user;
//client = %user.client;
isAiControlled = true;
};
SubtractMoney(%cost);
MoneyGuiPrinter($money);
// Add the new object to the MissionCleanup group
MissionCleanup.add(%obj);
}
else if(%cost > $money)
{
MoneyGuiPrinter("Insufficient funds");
}
}
Run the game. Add some turrets until you have no money. If the cost of the turret is greater then amount of money in the user account the textbox will change the text to : “Insufficient funds”.
Go to game\scrypt\server\gameCore.cs find “function GameCore::onClientEnterGame(%game, %client)” add %client.team = 1; in there.
Find datablock AITurretShapeData(AITurret) and add team = 0;
Code: Select all
function serverCmdcreateBuilding(%client, %pos, %start, %ray)
// spawn a new object at the intersection point
%obj = new AITurretShape()
{
datablock = AITurret;//%client;
//rotation = "0 0 1 " @ getWord(%rot, 2);
count = 1;
position = %pos;
//sourceObject = %user;
//client = %user.client;
isAiControlled = true;
team = %client.team; // this is added to make the turret be on the same team as the player.
};
Delete F32 mEnergy and replace it with:
F32 mTeamId:
Code: Select all
public:
F32 mTeamId;
protected:
F32 mEnergy; ///< Current enery level.
Look for “ mFadeDelay( 0.0f ), “ and under it add “ mTeamId(0), “
In the same file look for “ addField( "isAiControlled", TypeBool, Offset(mIsAiControlled, ShapeBase), “
Under it add :
addField( "team", TypeF32, Offset(mTeamId, ShapeBase),
Now go in to aiTurretShape.cpp
Look for “ AITurretShape* turret = (AITurretShape*)data; “
And in the If statement add:
Code: Select all
if ((shape->mTeamId == 0)||(shape->mTeamId == turret->mTeamId)) return;
Code: Select all
if (shape && shape->getDamageState() == ShapeBase::Enabled)
{
if ((shape->mTeamId == 0) || (shape->mTeamId == turret->mTeamId)) return;
Point3F targetPos = shape->getBoxCenter();
Go in to game/script/server and open aiPlayer.cs . Look for “function AIPlayer::spawnAtLocation(%name, %spawnPoint) “
Code: Select all
function AIPlayer::spawnAtLocation(%name, %spawnPoint)
{
// Create the demo player object
%player = new AiPlayer()
{
dataBlock = DemoPlayer;
path = "";
team = 2;
};
MissionCleanup.add(%player);
%player.setShapeName(%name);
%player.setTransform(%spawnPoint);
return %player;
}
Go in to game/script/server and open aiPlayer.cs . Look for “function AIPlayer"
This is to make the ai enemy be on the a different team. In this way the turrets will kill them.
Now that we have a working killing force we need somethink to kill.
For this demo we are going to make a simple spown Ai player (enemy) at one location in the scene and make them move to some point.
Create a new simgroup (library -> level -> system -> simGroup) and call it "aiSpawnGroup" (the name can be changed in the Inspector after left clicking on the simgroup in the Name field.
We want to add two WayPoints(StartPoint,EndPoint). WayPoint can be found with SpawnSphere in (library -> scripted -> misc). Once you have a load in, if they're not inside "aiSpawnGroup" you want to select them all and drag them in.
The simgroup is if you want to have more then one spown point for the enemies.
Now to create a function that will spawn some enemies.
Go to game\scripts\server and open aiPlayer.cs and add this function :
Code: Select all
function spawnAi()
{
%index = 0; //In case you have more then one spawnpoint you can use a random function to select the next spawn point
%count = aiSpawnGroup.getCount();
%spawnPoint = aiSpawnGroup.getObject(%index).getTransform();
%player = new AiPlayer()
{
dataBlock = DemoPlayer;
path = "";
team = 2;
position = %spawnPoint;
};
%player.setMoveSpeed(0.5);
if(%count > 1)
{
%player.setMoveDestination(aiSpawnGroup.getObject( (%count - 1) ).getTransform());
}
else
{
echo("ERROR: you need more elements in aiSpawnGroup");
}
MissionCleanup.add(%player);
}
This function will create the ai enemies and order them to go to the EndPoint.
Now we need one more thing. After killing the something the player should receive some money.
For this just copy the following function after the one from above:
Code: Select all
function DemoPlayer::onDamage(%this,%obj, %delta)
{
%val = 200;
if(%obj.getstate() $="Dead")
{
//when ai enemy is dead the player will receive some money
AddMoney(%val);
}
}
Now open the game, press the “ ` “ (tilda) and write spawnAi() then press “Enter” from keyboard. Should see AI soldiers runnig across the map to the EndPoint.
Add some turrets, have some fun.
If you remember we changed the code to make the turrets be friendly to the other turrets.
The following example will use the build in ignoreList of the Ai turret.
Remember the “serverCmdcreateBuilding” function from commands.cs .
We will change it a bit

First we will create a global list of friendly units like this.
You must do this out side of a function. ” $mySSet = new SimSet(); “
Example:

Now in the function “serverCmdcreateBuilding” below
Code: Select all
%obj = new AITurretShape()
{
datablock = AITurret;//%client;
//rotation = "0 0 1 " @ getWord(%rot, 2);
//count = 1;
position = %pos;
//sourceObject = %user;
//client = %user.client;
isAiControlled = true;
team = %client.team; // <- TEAM
};
Code: Select all
//This code is meant to use the build in ignore list of the turret. But we need to make some List management. By management i want to say the we need to delete the dead units from the list. Soo Ai Enemy on dead :D
echo("XXX Turret Count : ",$mySSet.getCount());
for (%i=0; %i<$mySSet.getCount(); %i++)
{
%TeamMate = $mySSet.getObject(%i);
//All the units will ignore this unit
%TeamMate.addToIgnoreList(%obj);
//Read all the objects that are friends and add them to the ignore list of the turret. In this way the turret will ignore the team mates.
%obj.addToIgnoreList(%TeamMate);
}
$mySSet.add(%obj);
For debug purpuoes enter in the GUI editor and add "GuiButtonCtrl" from Library->Buttons.
Place it somewhere in the scene, give it a name, and in the Control tab you should find the "command" line, in the right column write (or call ) spawnAi();
With this function we can call enemies more easily.
Now is time to make a more advance path for the AI. For this we use the path system from torque 3d.
Go in to the Library - > Level.
Double click on the Path item. This action will place an Path object in the Scene Tree. Go back in the Scene Tree select the new path and rename it to MyPath( you can name it as you want).
This step is important: right mouse click on the Path and selet "Add New Objects Here", done select anything after.
Navigate back to the Library->Level and double click on the Path Node. It should be in the Path tree from the Scene Tree.

Remember to deselect isLooping.
Now you can select every marker one by one and make a path with them.
I made a triangle :

Now some script modification:
Open aiPlayer.cs and add a new function
Code: Select all
function spawnAiAtPath()
{
%path = "MissionGroup/MyPath";
%name = "AiEnemy";
%player = new AiPlayer()
{
dataBlock = DemoPlayer;
path = "";
team = 2;
};
if (!isObject(%path))
{
echo("ERROR: you need to add a path");
return 0;
}
%node = %path.getObject(0);
//Spawn the player at the first node of the path
%player = AIPlayer::spawnAtLocation(%name, %node.getTransform());
%player.setMoveSpeed(0.5);
//Sets the target node index of the path to be "-1". We wil take care of this later
%player.followPath("MissionGroup/MyPath",-1);
MissionCleanup.add(%player);
}
Code: Select all
if(%obj.currentNode >= (%obj.path.getCount() - 1))
{
//TODO: should kill the AiEnemy and remove some lives from player
%obj.damage(0, 0, 100000, 0);
}
Let's test it :
Go in the GUI editor and change the command that is called by the SpawnAi button to "spawnAiAtPath();"
You should see that the Ai player followed the path and at the end died. This gives us a problem, the money indicator went up and it should not do that.
As we increment the money value in the onDamage we need to check that the id of the AIAgent that triggered the function call (onDamage) is not killed by us when they complete the path.
To do this we create a variable to store the index of the object
Code: Select all
$var = 0;
function DemoPlayer::onReachDestination(%this,%obj)
{
//echo( %obj @ " onReachDestination" );
// Moves to the next node on the path.
// Override for all player. Normally we'd override this for only
// a specific player datablock or class of players.
if(%obj.currentNode >= (%obj.path.getCount() - 1))
{
$var = %obj.getId();
//TODO: should kill the AiEnemy and remove some lives from player
%obj.damage(0, 0, 100000, 0);
$var = 0;
}
if (%obj.path !$= "")
{
if (%obj.currentNode == %obj.targetNode)
{
%this.onEndOfPath(%obj,%obj.path);
}
else
{
%obj.moveToNextNode();
}
}
}
Code: Select all
function DemoPlayer::onDamage(%this,%obj, %delta)
{
%val = 200;
if(%obj.getstate() $="Dead")
{
//when ai enemy is dead the player will receive some money
if($var != %obj.getId() )
{
AddMoney(%val);
}
}
}