Game Development Community

dev|Pro Game Development Curriculum

Advanced Camera

by Thomas \"Man of Ice\" Lund · 04/03/2008 (11:57 am) · 480 comments

Download Code File

Description
Over time a lot of people have released camera resources, but some do not work anymore, others are hard to implement etc.

I've tried to assemble a single class to add to the engine that implements them all in one go using the same basic architecture. This enables minimum code bloat and makes it way easier to keep the code up to date.

Change log
March 3rd, 2005
Manoel made some changes to the orbit camera. Works in multiplayer now and is much nicer by using console variables

February 3rd, 2005
Fixed small big in interpolation.

February 2nd, 2005
Major changes in this one with various contributors.
* Static camera mode
* Smooth interpolation and transition between modes
* Smooth orbit camera!!!
* Vertical freedom mode when in 3rd person
* Better collision check with terrain and interiors
* Mouse control of orbit camera
* Totally reworked codebase and lots of cleanups. Much more readable now

Manoel Neto contributed the new orbit camera and the interpolation
Zik Saleeba contributed the vertical freedom mode and better collision check

Thanks a lot!!!!

I have marked changes with a New in the text below for those who upgrade

January 23rd, 2005
Minor changes. Larger update soon with new functionality
* Now takes GameBase objects as target + player
* Removed debug message in orbit camera
* Added getters for player and target object

June 25th, 2004
Updated the bindings for orbit camera. Switched left+right.

June 23rd, 2004
A big thanks to Stephen Zepp for contributing with an orbit camera mode. Its added to the resource, and is perfect for RTS games and action adventures. It allows for a camera to rotate around a user as if placed on a sphere. The user can zoom in/out, rotate and tilt the camera.

June 9th, 2004
Added getter/setter for the 3 offset values accessible from script
Added a "follow terrain" mode for the third person camera, so the camera follows the terrain slope - doesnt work perfectly

April 5th, 2004
All camera modes now use a raycast to not get hidden behind terrain or interiors

April 3rd, 2004
This release is the first release, and might not be as "advanced" as the author would like, but its time to release it and get some feedback to further enhance it down the road.

Camera Modes Implemented
The resource currently implements the following camera systems:
* Track Mode
This is the same as the tracking camera resource posted by Cory Osborn. A stationary camera tracks the player and keeps him in focus.

* New Static Mode
Camera stays in its position and rotation. Useful for e.g. scene based adventures where the camera doesnt move with the players

* Third Person Mode
The camera is placed at an offset behind the player and rotates with the player. The camera itself is not controllable

* Third Person Track Mode
The camera is placed in third person mode but rotated so it always looks at a specified object, but with the player in full view

* God View
Camera is placed at an offset from the player and does not rotate with the player. This is your typical "Diablo" kind of camera.

* New Orbit Mode
Camera is placed on a sphere looking at the player, and can rotate/tilt and zoom controlled by the player. There are bindings to the mouse when in single player game (server and client on same machine)

Movie
Here is a small movie displaying the different camera modes (3 MB)
www.codejar.com/advancedcamerademo.wmv

How to Add
First off all you need to take the attached advancedCamera.cc/h files and add to engine\game and add them to the project.

Then you need to expose the camera object in GameConnection as described in Cory's resource www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=4720

I took the liberty to paste the relevant parts in here too

Exposing mCameraObject
The first thing you want to do is add console method's to access the GameConnection setCameraObject/getCameraObject methods. I added these to GameConnection.cc right after the console method for getControlObject:

ConsoleMethod( GameConnection, setCameraObject, bool, 3, 3, "(ShapeBase object)")
{
   ShapeBase *gb;
   if(!Sim::findObject(argv[2], gb))
      return false;

   object->setCameraObject(gb);
   return true;
}

ConsoleMethod( GameConnection, getCameraObject, S32, 2, 2, "")
{
   argv;
   SimObject* cp = object->getCameraObject();
   return cp ? cp->getId(): 0;
}

ConsoleMethod( GameConnection, clearCameraObject, void, 2, 2, "")
{
   object->setCameraObject(NULL);
}

After playing around with it, I also found you need some adjustments to the setCameraObject and setControlObject methods - otherwise the client connection can screw things up if you bounce the same object from your connection's control to camera or vice versa. Here is my GameConnection::setControlObject:

void GameConnection::setControlObject(ShapeBase *obj)
{
   if(mControlObject == obj)
      return;

   if(mControlObject && mControlObject != mCameraObject) 
      mControlObject->setControllingClient(0);
   
   if(obj)
   {
      // Nothing else is permitted to control this object.
      if (ShapeBase* coo = obj->getControllingObject())
         coo->setControlObject(0);
      if (GameConnection *con = obj->getControllingClient()) 
      {
         if (this != con) 
         {
            // was it controlled via camera or control?
            if (con->getControlObject() == obj)
               con->setControlObject(0);
            else
               con->setCameraObject(0);
         }
      }

      // We are now the controlling client of this object.
      obj->setControllingClient(this);
   }

   // Okay, set our control object.
   mControlObject = obj;
   if (mCameraObject.isNull())
      setScopeObject(mControlObject);
}


and here is my GameConnection::setCameraObject:

void GameConnection::setCameraObject(ShapeBase *obj)
{
   if(mCameraObject == obj)
      return;

   if(mCameraObject && mCameraObject != mControlObject) 
      mCameraObject->setControllingClient(0);
   
   if (obj) {
      // Nothing else is permitted to control this object.
      if (ShapeBase* coo = obj->getControllingObject())
         coo->setControlObject(0);
      if (GameConnection *con = obj->getControllingClient()) 
      {
         if (this != con) 
         {
            // was it controlled via camera or control?
            if (con->getControlObject() == obj)
               con->setControlObject(0);
            else
               con->setCameraObject(0);
         }
      }

      // We are now the controlling client of this object.
      obj->setControllingClient(this);
   }

   // Okay, set our camera object.

   mCameraObject = obj;

   if (mCameraObject.isNull()) {
      setScopeObject(mControlObject);
   } else {
      setScopeObject(mCameraObject);
      // if this is a client then set the fov and active image
      if(isServerConnection())
      {
         F32 fov = mCameraObject->getDefaultCameraFov();
         GameSetCameraFov(fov);
      }
   }
}

Camera read/write packets
None of this will work unless the client copy of the camera object gets packets updated. Here we're going to modify GameConnection::readPacket and GameConnection::writePacket.

In GameConnection::readPacket, find this block of code:

if (bstream->readFlag())
      {
         S32 gIndex = bstream->readInt(10);
         ShapeBase* obj = static_cast<ShapeBase*>(resolveGhost(gIndex));
         setCameraObject(obj);
      }
      else
         setCameraObject(0);

and change it to

if (bstream->readFlag()) 
      {
         S32 gIndex = bstream->readInt(NetConnection::GhostIdBitSize);
         ShapeBase* obj = static_cast<ShapeBase*>(resolveGhost(gIndex));
         setCameraObject(obj);
         obj->readPacketData(this, bstream);
      }
      else
         setCameraObject(0);


In GameConnection::writePacket, find this block of code:

if (!mCameraObject.isNull() && mCameraObject != mControlObject)
      {
         gIndex = getGhostIndex(mCameraObject);
         if (bstream->writeFlag(gIndex != -1))
            bstream->writeInt(gIndex, 10);
      }
      else
         bstream->writeFlag( false );

and change it to:

if (!mCameraObject.isNull() && mCameraObject != mControlObject) 
      {
         gIndex = getGhostIndex(mCameraObject);
         if (bstream->writeFlag(gIndex != -1)) {
            bstream->writeInt(gIndex, NetConnection::GhostIdBitSize);
            mCameraObject->writePacketData(this, bstream);
         }
      }
      else
         bstream->writeFlag( false );

Recompile it all and the engine is ready to go.

Script

To use the camera you need to follow some of Corys resource, but with some modifications. Datablock and naming has changed

First, add a datablock for the tracking camera to /fps/server/scripts/camera.cs:

...
datablock AdvancedCameraData(AdvCameraData)
{
   lookAtOffset = "0 0 2";
   thirdPersonOffset = "0 -3 3";
   godViewOffset = "0 -20 20";
   maxTerrainDiff = 2;
   orbitMinMaxZoom = "5 100";
   orbitMinMaxDeclination = "10 80";
   damping = 0.25;

};
...

Next, add it to the connection just like is currently done with the base camera class. Add this inside GameConnection::onClientEnterGame (/fps/server/scripts/game.cs), right after %this.camera is set up:

...
   // create advanced camera
      %this.advCamera = new AdvancedCamera() {
      dataBlock = AdvCameraData;
   };
   MissionCleanup.add(%this.advCamera);
   %this.advCamera.scopeToClient(%this);
...


We'll need to clean it up after the client leaves the game, so add this to GameConnection::onClientLeaveGame

...
   if (isObject(%this.advCamera))

      %this.advCamera.delete();
...


We need to tell it what to do when added and assign the connection's camera object, so add this to the end of GameConnection::createPlayer:

...
  // We set the camera system to run in 3rd person mode around the %player
  %this.advCamera.setPlayerObject(%player);
  %this.advCamera.setThirdPersonMode();
  %this.advCamera.setFollowTerrainMode(false);
  %this.advCamera.setVerticalFreedomMode(false);
  %this.setCameraObject(%this.advCamera);
...


And we'll want to unhook it when the player dies. Insert this at the beginning of GameConnection::onDeath:

...
   // clear connections camera
   %this.advCamera.clearPlayerObject();
   %this.advCamera.clearTargetObject();
   %this.clearCameraObject();
...

The last thing to know, is if you're in first-person mode, GameConnection automatically uses the control object to render the engine rather than the camera object. So if you don't see this working when you first enter a mission, toggle out of first-person.

Orbit mode
To use the orbit camera, one needs to add some key binds to manipulating the camera.

All you need to do, is add the following to your client\config.cs or better to your client\scripts\default.bind.cs

//------------------------------------------------------------------------------
// Advanced Camera Movement
//------------------------------------------------------------------------------

$cameraYawSpeed = -100.0;
$cameraPitchSpeed = -50.0;
$cameraZoomSpeed = -5.0;

function rotateCameraHorizontal(%val)
{
     $advCamera::Yaw = getMouseAdjustAmount(%val)*$cameraYawSpeed ;
}

function rotateCameraVertical(%val)
{
     $advCamera::Pitch = getMouseAdjustAmount(%val)*$cameraPitchSpeed;
}

function zoomCamera(%val)
{
     $advCamera::Zoom = getMouseAdjustAmount(%val)*$cameraZoomSpeed;
}

moveMap.bind( mouse, xaxis, rotateCameraHorizontal);
moveMap.bind( mouse, yaxis, rotateCameraVertical );
moveMap.bind( mouse, zaxis, zoomCamera );

Remember to comment out the mouse commands for the player movement, as these are overwritten by the orbit camera controls
//moveMap.bind( mouse, xaxis, yaw );
//moveMap.bind( mouse, yaxis, pitch );

Script API
To use the different camera modes you can use the following API
Selecting the camera mode is done with e.g.:
%this.advCamera.setTrackMode();
  %this.advCamera.setThirdPersonMode();
  %this.advCamera.setThirdPersonTargetMode();
  %this.advCamera.setGodViewMode();
  %this.advCamera.setOrbitMode();
  %this.advCamera.setStaticMode();

Prior to calling the above modes you have to set the PlayerObject using
setPlayerObject();

To use the 3rd person target mode you also need to set a TargetObject using
setTargetObject();

To use the static or tracking camera you need to set the position the camera should be placed suing
setCameraPosition(Point3F pos);

To use the follow terrain mode in 3rd person you give a bool to
setFollowTerrainMode(true/false);

To use the vertical freedom mode in 3rd person you give a bool to
setVerticalFreedomMode(true/false);
This only works if the player object is a Player, because it uses the head movement.

The offset values in the datablock can be changed for the camera object via script using
get/setLookAtOffset();
get/setThirdPersonOffset();
get/setGodViewOffset();
The setters take a Point3F as argument.

The orbit camera can be manipulated from script using
get/setOrbitMinMaxZoom()
get/setOrbitMinMaxDeclination()
The min/max take a Point2F

You can also manipulate the orbit camera directly by assigning values to the following variables
[code]
$advCamera::Yaw
$advCamera::Pitch
$advCamera::Zoom
$advCamera::azimuth
$advCamera::declination
$advCamera::zoomDistance
[code]

All camera modes coexist, so you can set the position, player object and target object once and then switch camera modes around as you see fit. Switching mode will not clear the old objects/positions.
#201
11/09/2005 (9:50 am)
You use the

get/setThirdPersonOffset();

methods

E.g.

setThirdPersonOffset("0 -5 5");

is 0 units to the left/right, 5 units behind the player and 5 up

You then use the lookatoffset (also has a get/set method in script), so you can control where the camera is looking at - offset from the target object.

E.g. usually you dont want the camera to look at the 0,0,0 of your player - typically the feet or stumach. By letting the lookatoffset being something like "0 5 0" the camera looks at a point 5 units in front of the target

Hope that helps
#202
11/10/2005 (5:59 am)
I'm experiencing a strange thing with the Advanced Camera. It is strange because this doesn't happening with the normal camera. In my application I have a city with roads, skyscrapers and people who walk from building to building. I need your camera to follow a certain avatar while it is walking from a building to another one. In the same time the aspect of the buildings are changing by changing their shape file via script. The avatars are implemented as AIPlayers, the buildings as StaticShape and the application is single user. The strange thing is that if the application is switched to Advanced Camera then when it changes the shape file of the buildings they are disappearing. I tried to debug it and I realized that when Advanced Camera is on then the client side building objects are missing. If I switch to normal camera then all the buildings are coming back. Is the client-server connection influenced in any way by the Advanced Camera? If yes then what can be done to have it working as it does with the normal camera?
I tracked down the client object with the following code:
%client = ClientGroup.getObject(0);
%temp = %client.GetGhostIndex( %destinationBuilding );
%temp = ServerConnection.ResolveGhost( %temp );
It returns 0 in Advanced Camera mode and a valid ID in normal camera mode.
Thanks.
#203
11/11/2005 (11:00 pm)
@Robert Savage
There is a post way up (about halfway through the comments) that handles this problem. It's a post by Manoel Neto, dated Mar 07, 2005.
#204
11/14/2005 (3:43 am)
While customizing the new version of the camera, I noticed that when you switch to the orbit mode, the camera instantly flashes to the last orbit offset. That offset looks to be controled by the current orbit settings(zoom, azimuth, and declination). I'm wondering if anyone knows a way to turn a current offset into it's respective zoom, azimuth, and declination? I'm trying to have the camera seamlessly enter/exit the orbit mode. At the moment, I have it exiting perfectly(at least to the 3rd person mode).


Edit: Nevermind, I got it.
#205
11/22/2005 (12:42 am)
okay... I can't read this thread properly in Firefox. There is a huage grey box in the middle of the comments so I can't read them, and the code boxes down the bottom are blank.
#206
11/22/2005 (12:45 am)
Just reload (Ctrl+F5)... Mine works fine :-/
#207
11/25/2005 (10:14 am)
I have this working on 1.4 for those interested. Follow the resource the same way and it should work just fine. You'll notice that gameconnection.cc already is prepared to accept a second camera. so you may not need to to anything besides add in the advancedcamera.cc file and do the scripting work.

However, you should add any fixes from march 3rd 2005 and up that are on this page. i expericened issues that were fixed by adding in the extra fixed.

I too get the grey boxes with no text. reloading does nothing. i have to use IE to see it.
#208
11/26/2005 (2:43 am)
Works fine in my firefox (1.0.7 with Google toolbar build in)
#209
11/26/2005 (11:05 am)
I was writing something, but sama got my answer... thx man!, and yet i cant see the boxes with firefox as well :)
#210
11/26/2005 (11:24 am)
weird, i can see the boxes today. but not yesterday.....
#211
11/29/2005 (2:25 am)
i also tried this change my code and compiled but i am not able to move my player using mouse he walk staright and i can't turn my player and also i have impelmentd AIGuard but i can't see them but if i come in there rage they start shooting like i am having some invisible enemy.
any idea any one plz help me out.
#212
12/06/2005 (5:52 pm)
ive been having a problem with this.. im currently runnin torque 1.4 and i am getting this error

mygame/server/scripts/game.cs (218): Unable to instantiate non-conobject class AdvancedCamera.
Set::add: Object "0" doesn't exist
mygame/server/scripts/game.cs (220): Unable to find object: '0' attempting to call function 'scopeToClient'
mygame/server/scripts/game.cs (341): Unable to find object: '0' attempting to call function 'setPlayerObject'
mygame/server/scripts/game.cs (342): Unable to find object: '0' attempting to call function 'setThirdPersonMode'
mygame/server/scripts/game.cs (343): Unable to find object: '0' attempting to call function 'setFollowTerrainMode'
mygame/server/scripts/game.cs (344): Unable to find object: '0' attempting to call function 'setVerticalFreedomMode'

i thought that it was a problem with compiling. but i checked the code and it seems right.. at least i think its right.. its not really clear what exactly needs to be changed in 1.4 but im pretty sure.. i also checked and made sure i added the files to the project. it seems to be compiling fine.. i just get those errors...

EDIT::
i looked over previous posts.. and relized that i have to run torque debug.. but it crashes and i get the following errors

Fatal: (../engine/audio/audioBuffer.cc @ 307) AudioBuffer::readWAV: remaining chunk data should never be less than zero.
Cannot display alerts in full screen mode. Dumping message to console...
Fatal: (../engine/audio/audioBuffer.cc @ 307), AudioBuffer::readWAV: remaining chunk data should never be less than zero.
Error, a DecalManager (5c32d00) isn't properly out of the bins!

any suggestions
i really need this camera to work so if anyone has any suggestions, it would be greatly appreiciated
#213
12/08/2005 (4:17 pm)
ok so i have checked to code 1000 times. here is what i did.. i added the two new files advancedcamera.cc and advanced camera.h to the project and left the other files as it (because i am running 1.4, they changes have already been made) i then recompiled and ran the torque demo_debug.app i manage to get into my game but i still get the errors
mygame/server/scripts/game.cs (218): Unable to instantiate non-conobject class AdvancedCamera.
Set::add: Object "0" doesn't exist
mygame/server/scripts/game.cs (220): Unable to find object: '0' attempting to call function 'scopeToClient'
mygame/server/scripts/game.cs (341): Unable to find object: '0' attempting to call function 'setPlayerObject'
mygame/server/scripts/game.cs (342): Unable to find object: '0' attempting to call function 'setThirdPersonMode'
mygame/server/scripts/game.cs (343): Unable to find object: '0' attempting to call function 'setFollowTerrainMode'
mygame/server/scripts/game.cs (344): Unable to find object: '0' attempting to call function 'setVerticalFreedomMode'
i dont know what to do, any help would be great. i did however manage to get rid of the audio problems by fixing a corrupt audio file i had.
thanks again
#214
12/08/2005 (4:18 pm)
ignore all that stuff I had posted here...

Works great :D
just need to play with all the settings now.

@Jonathan Lawitts
it looks like it might not be including the .cc and .h files. Don't forget to add them to the project as well as put them in the directory SDK/engine/game

if you are running VS7 you can add them to the project by right cliking on the solution for Torque Demo/game and add->add existing item then selecting the 2 files from the directory.

the first thing about the non-conobject is stuff about it not being able to find the object in the C++ code that you are trying to create with %this.advCamera = new AdvancedCamera();
All the other errors are because that didn't succeed and it's trying to call nonexistant functions of an object with an ID of 0.
#215
12/14/2005 (9:23 am)
I was wondering if there was a plan for smooth camera position changeing from one mode to the next. Perhaps it should be a toggle option?
#216
12/19/2005 (12:58 pm)
Does anyone have a working version of this resource for 1.4 with smooth movement and all objects render correctly and its all optomized. if so could you please post it or email me because i am having some serious problems getting this to work correctly. thanks
#217
01/04/2006 (3:24 am)
can any one plz help me out i have AIGuard resources but after adding Advance camera resources to my SDK i can't see some of gaurd . i can see them if player get killed. if player get inside there range they attack and if they killed me i can see them.

Damm....... my problem is solved i didn't read the comment Thanks for such a great resource
#218
01/17/2006 (4:39 pm)
I have yet to implement this code but before I do, I have a simple question. I currently like the 3rd person camera in 1.4. All I want the camera to do is rotate around the player when no directional keys are pressed but as soon as I press a key, it moves back to the default view. Is this possible with this resource?
#219
01/21/2006 (5:44 pm)
think this thread could use a bit of a cleanup...
#220
01/22/2006 (4:53 am)
I had the same problem as Gary and Christian with the player object sliding off vehicles (and the camera staying focused on them) -- well, they probably solved it already, but just in case...

I'm not sure why, but toggling the world editor, then switching to first person and back was restoring the proper focus; also, the player object wasn't really sliding off the vehicle -- it just looked that way to the client. The health bar also became inaccurate... toggling to or staying in first person mode did nothing to prevent the glitches, but calling %client.clearCameraObject() from a server command would. Reattaching the advanced camera would re-add the strange errors, but if you delete the adv. camera then create a new one, it's attached properly.

Thank you so much for this great camera -- it's absolutely beautiful.