RTS Camera Snippet
by Matthew Genge · in Torque 3D Professional · 06/19/2012 (3:05 pm) · 4 replies
I've been playing with an RTS Camera and thought I would share, having found some idiosyncracies of Torque3D 1.2 that took me many hours to solve.
The camera in this example is based on the code from the RTS prototype but has some additional functionality. The camera will zoom in and out and change the pitch (lean) angle from a maximum to a minimium on zoom in. The camera can be moved using mousedrag and rotated around it's position using SHIFT+mousedrag.
The code is based on the RTS prototype. Camera limits (distance and angles) are defined by globals, mouse events are handled in the art playGUI script like in the RTS prototype and call serverside functions to change the camera position and angle which are in the server commands scripts. These functions update clientside variables so that each client records the camera turn (around the z axis, the yaw) and the camera pitch. The camera distance is also in a clientside variable like in the RTS prototype. Clientside variables need to be initialised somewhere.
In playGUI the code is:
function PlayGui::onMouseDown(%this, %modifier, %mousepos, %mouseclick)
{
//Save the cursor values
%mousepos = Canvas.getCursorPos();
LocalClientConnection.cursorX = getWord(%mousepos,0);
LocalClientConnection.cursorY = getWord(%mousepos,1);
LocalClientConnection.mouseisDown = 1;
if(LocalClientConnection.lshift){
%this.schedule(50, "mouseTurnCamera");
} else {
%this.schedule(50, "mouseDragged");
}
}
function PlayGui::mouseDragged(%this)
{
%curpos = Canvas.getCursorPos();
%curX = getWord(%curpos,0);
%curY = getWord(%curpos,1);
if(%curX != LocalClientConnection.cursorX || %curY != LocalClientConnection.cursorY){
%dx = %curX - LocalClientConnection.cursorX;
%dy = %curY - LocalClientConnection.cursorY;
commandToServer('DragCamera', %dX, %dY);
LocalClientConnection.cursorX = %curX;
LocalClientConnection.cursorY = %curY;
}
if(LocalClientConnection.mouseisDown){
%this.schedule(50, "mouseDragged");
}
}
function PlayGui::mouseTurnCamera(%this)
{
%curpos = Canvas.getCursorPos();
%curX = getWord(%curpos,0);
%curY = getWord(%curpos,1);
if(%curX != LocalClientConnection.cursorX){
%dx = %curX - LocalClientConnection.cursorX;
%scrnWidth = getWord(Canvas.getExtent(),0);
%incAng = 180.0*%dx/%scrnWidth;
commandToServer('TurnCamera', %incAng);
LocalClientConnection.cursorX = %curX;
LocalClientConnection.cursorY = %curY;
}
if(LocalClientConnection.mouseisDown){
%this.schedule(50, "mouseTurnCamera");
}
}
function PlayGui::onMouseUp(%this, %modifier, %mousepos, %mouseclick)
{
LocalClientConnection.mouseisDown = 0;
}Notice that mousedrag is handled not by the onMouseDragged callback, which seems not to work for me, but by setting schedule calls to either mouseTurnCamera() or mouseDragged() depending on whether the shift key is pressed. Due to problems with the %modifer the shift key is detected simply by binding the left shift key in the movemap to a function that sets a clientside variable (1 on, 0 off). Both functions calculate the movement or rotation of the camera by comparing the current mouse cursor position to one saved in clientside variables from the last call to the schedule.
Mousewheel zoom is controlled by binding to the server command function adjustCamera() which largely is the same as that in the RTS prototype.
The guts of the code is the function updateCamera() which calculates the camera position using the current
pitch, roll and camdist, and ensures the camera is above the ground. Note that the camera.setRotation() is used rather than camera.LookAt() since problems were encountered with this function.
function serverCmdoverheadCam(%client)
{
commandToClient(%client,'changeCameraFOV', $camangle);
%client.camera.camDist = 20.0;
updateCamera(%client);
%client.camera.controlMode = "Overhead";
}
function serverCmdtoggleCamMode(%client)
{
if(%client.camera.controlMode $= "Overhead")
{
commandToClient(%client,'changeCameraFOV', $camangle);
%client.camera.setOrbitObject(%client.player, mDegToRad(20) @ "0 0", 0, 5.5, 5.5);
%client.camera.controlMode = "OrbitObject";
}
else if(%client.camera.controlMode $= "OrbitObject")
{
commandToClient(%client,'changeCameraFOV', $camangle);
updateCamera(%client);
%client.camera.controlMode = "Overhead";
}
}
function serverCmdDragCamera(%client, %dragx, %dragy)
{
//Get coordinates of camera from angle
%screen = Canvas.getExtent();
%scrWidth = getWord(%screen,0);
%scrHeight = getWord(%screen,1);
%camangle = 10.0;
if (%client.camera.camDist>2.0*$minCamDist){
%camlean = $maxcamlean;
} else {
%camLean = ($maxcamlean-$mincamlean)*(%client.camera.camDist - $minCamDist)/(2.0*$minCamDist) +
$mincamlean;
}
%xscale = %client.camera.camDist*mTan(%camangle*mPi()/180.0)/%scrWidth;
%yscale = 2.0*%xscale/(mSin(%camlean*mPi()/180.0));
%client.posX = %client.posX + (%xscale*%dragx*mCos(%client.camera.camTurn*mPi()/180.0) - %yscale*
%dragy*mSin(%client.camera.camTurn*mPi()/180.0));
%client.posY = %client.posY - (%yscale*%dragy*mCos(%client.camera.camTurn*mPi()/180.0) + %xscale*
%dragx*mSin(%client.camera.camTurn*mPi()/180.0));
updateCamera(%client);
}
function serverCmdTurnCamera(%client, %incAng)
{
%client.camera.camTurn = %client.camera.camTurn + %incAng;
updateCamera(%client);
}
function updateCamera(%client)
{
if (%client.camera.camDist>2.0*$minCamDist){
%camlean = $maxcamlean;
} else {
%camLean = ($maxcamlean-$mincamlean)*(%client.camera.camDist - $minCamDist)/(2.0*$minCamDist) +
$mincamlean;
}
%camZ = %client.camera.camDist*mSin(%camlean*mPi()/180.0);
%camX = %client.camera.camDist*mCos(%camlean*mPi()/180.0)*mSin(%client.camera.camTurn*mPi()/180.0);
%camY = %client.camera.camDist*mCos(%camlean*mPi()/180.0)*mCos(%client.camera.camTurn*mPi()/180.0);
%trans = %camX SPC %camY SPC %camZ;
%curpos = %client.posX SPC %client.posY SPC getTerrainHeight(%client.posX, %client.posY)+0.5;
%camposition = VectorAdd(%curpos, %trans);
//check not below ground
%camg = getTerrainHeight(getWord(%camposition,0), getWord(%camposition,1));
if (%camg+0.5>getWord(%camposition,2)){
%temp = getWord(%camposition,0) SPC getWord(%camposition,1) SPC %camg+0.5;
%camposition = %temp;
}
%client.camera.position = %camposition;
%client.camera.setRotation(%camlean*mPi()/180.0 SPC 0.0 SPC (%client.camera.camTurn-180.0)*mPi()/180.0);
}
function wrapangle(%ang){
if(%ang>180.0){
%ang = %ang - 360.0;
}
else if(%ang<-180.0){
%ang = %ang + 360.0;
}
return %ang;
}
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")
{
if(%adjustment == 1)
%client.camera.camDist = %client.camera.camDist + %client.camera.camDist*0.05;
else
%client.camera.camDist = %client.camera.camDist - %client.camera.camDist*0.05;
if (%client.camera.camDist<$minCamDist)
%client.camera.camDist=$minCamDist;
if (%client.camera.camDist>$maxCamDist)
%client.camera.camDist=$maxCamDist;
updateCamera(%client);
}
}The code seems to perform well, but could do with some tweaking.
About the author
I am an Earth Scientist who likes writing games for fun.
#2
@Matthew, this looks like just the camera I need. Thanks for sharing. Hopefully I'll get to implementing it this before the end of the week so I'll post my results then.
08/08/2012 (12:42 am)
Did you delete your compiled scripts (.DSO) before firing up the editor? I'm only guessing that could be the case, as that's my #1 faux pas. @Matthew, this looks like just the camera I need. Thanks for sharing. Hopefully I'll get to implementing it this before the end of the week so I'll post my results then.
#3
08/08/2012 (6:27 am)
yep i tried that still no luck :( i have the camera in god view(the rts view) and i can zoom in and out(i implemented them from another resource) all i want to do know is to be able to move the camera around using my mouse when its near the edge of the window :( please help
#4
As far as moving by screen edge, just get the cursor position and check the x and y components. If %curX < 10 move camera left, if greater than screenWidth - 10, move camera right, and so on. You could modify one of the mouse functions in the playGUI section for this pretty easily.
Hope that points you in the right direction. I'm assuming you're looking for old Command and Conquer/Warcraft/Baldur's Gate camera movement....
08/09/2012 (7:09 am)
The onMouseDragged() callback is not exposed to the console, sadly - I think Jeff (AFX) must have exposed it for his work.As far as moving by screen edge, just get the cursor position and check the x and y components. If %curX < 10 move camera left, if greater than screenWidth - 10, move camera right, and so on. You could modify one of the mouse functions in the playGUI section for this pretty easily.
Hope that points you in the right direction. I'm assuming you're looking for old Command and Conquer/Warcraft/Baldur's Gate camera movement....
Sean Oloughlin
West Coast Institute of Training WCIT