Torque 2D/GenreTutorials/PlatformerCamera

From TDN

(Redirected from T2DScript/PlatformerWorld)

Contents

Introduction


A camera systems features are usually specific to the game for which it is being created. For example: The camera in Super Mario Brothers is very different from the camera in Sonic the Hedgehog. Because of this, we will be designing a system in this tutorial that can do anything you might want it to. So, you can just grab the parts out of it that you want, or, implement them all right along with me and just use what you need.

Here is a quick overview of the features I am planning to implement in this system. All of this will be fully dynamic, so you can change the behavior of the camera anytime you want.

  • Track any object (this will most likely be the player).
  • Offset the camera from the track object (so the object is not necessarily in the center of the screen).
  • Limit the camera to a certain area.
  • Trap the object within the camera’s view area.
  • Track the object perfectly, or lag behind it.
  • Track the x and y position of the object, or just the x position.
  • Automatically scroll the camera.





Implementation


Most of the code in this tutorial is going to be created in a new file called camera.cs. So create this file now and add its exec statement to InitializeT2D.cs:

   exec("./camera.cs");

While still in game.cs, add this to the setupT2DScene function between the calls to createPlayer and createLevel:

   createCamera();

We are going to create the camera as a script object, which is basically a class in TorqueScript. This will allow us to define methods for our camera that will make handling it simpler and cleaner. Here is the createCamera function definition that you should put in camera.cs:

function createCamera()
{
   $camera = new ScriptObject()
   {
      class = Camera;
   };
}

Pretty compact, but powerful. We are creating a new ScriptObject of class camera and saving it as $camera. The class attribute allows us to define functions like this:

function Camera::functionName(%this)
{
}

That can be called like this:

$camera.functionName();

So, let’s define some of these functions:

function Camera::onAdd(%this)
{
   %this.camera = new t2dSceneObject()
   {
      scenegraph = t2dscene;
   };
   
   %this.setTrackHeight(true);
   %this.setTrackingForce(0);
   %this.setFacePlayer(false);
   %this.setOffset("20 -15");
   %this.setTarget($player);
}

The Camera::onAdd function will be called by the engine anytime a new object of class Camera is created. In this case, it will be called right after our createCamera function. The first parameter of the function (%this) and of all functions that are methods of a ScriptObject, is a reference to the object that the function was called on. In other words, it is the same as $camera.

The first line of the function creates a t2dSceneObject and places it in our scenegraph. The purpose of this is to provide a much simpler interface to all of the camera’s mounting functions. We can mount our scene window rigidly to this object, and let it handle tracking the player. We could accomplish the same thing without this object by mounting the scene window directly to the player, but it wouldn’t be as easy. The next five lines just call some functions that we will define now in order to set default properties for the camera. Add the definitions after the Camera::onAdd function:

function Camera::setTrackHeight(%this, %trackHeight)
{
   %this.trackHeight = %trackHeight;
}

function Camera::setTrackingForce(%this, %force)
{
   %this.trackingForce = %force;
   sceneWindow2D.mount(%this.camera, "0 0", %force, true);
}

function Camera::setFacePlayer(%this, %face)
{
   %this.facePlayer = %face;
}

function Camera::setTarget(%this, %target)
{
   %this.target = %target;
}

function Camera::setOffset(%this, %offset)
{
   %this.xOffset = getWord(%offset, 0);
   %this.yOffset = getWord(%offset, 1);
}

function Camera::setTrapObject(%this, %trap)
{
   %this.trapObject = %trap;
   if (%trap)
      %this.target.setWorldLimit(CLAMP, %this.viewArea);
   else
      %this.target.setWorldLimit(OFF);
}

function Camera::setLimits(%this, %area)
{
   %this.viewArea = %area;
   sceneWindow2D.setViewLimitOn(%area);
}

function Camera::setAutoScroll(%this, %dir)
{
   %this.autoScroll = true;
   %this.scrollDirection = t2dVectorScale(%dir, 0.001);
}

function Camera::stopAutoScroll(%this, %dir)
{
   %this.autoScroll = false;
}

Really all these functions do is set parameters that are used in the update camera function, with the exception of the calls to setWorldLimit and mount. The call to mount in setTrackingForce tells the scene window to follow our camera around. The setWorldLimit calls bind the target object to the area defined in Camera::setLimits. Here is a list of the functions and their purpose:

trackHeight
The trackHeight function takes one parameter. This is a boolean (true or false) value that sets the track height status of the camera. True will cause the camera to follow the target object in both the x and y direction, false will cause the camera to only follow in the x direction.

trackingForce
The tracking force of the camera controls how the camera will follow the target object. If you pass in 0 as the parameter to the function, the camera will follow it perfectly, otherwise, higher values mean less lag.

facePlayer
This function also takes a boolean value for a parameter. If it is true, the camera will offset itself in the x direction according to the direction the player is facing. This is useful if the player is offset from the center of the screen and you want it to be able to see farther ahead in the direction it is facing.

target
This function sets the target object for the camera to track. It’s only parameter must be an object that is currently in the t2dscene scenegraph.

offset
The offset is the distance the target object should be from the center of the screen in both the x and y direction.

trapObject
If you call Camera::setTrapObject and pass true as the parameter, the target object will never be allowed outside the view of the camera.

limits
The parameter to this function should be 4 space separated numbers. The first two should be the position of the upper left corner you want to bind the camera to, and the next two should be the width and height of the area.

autoScroll
With this function, you can control the movement of the camera regardless of the object that is being tracked. The camera will scroll at the velocity you pass in as the first parameter. To stop scrolling the camera, just call Camera::stopAutoScroll.

Similarly to the onAdd function defined earlier, the onRemove function of a ScriptObject is called when the object is deleted. Basically, you want to use this function to clean up any objects that your script object owns. In our case, that is the camera mount object that we created in the onAdd function. So, here is the onRemove function:

function Camera::onRemove(%this)
{
   if (isObject(%this.camera))
      %this.camera.delete();
}

Now we need to use all of our new parameters to actually move the camera around. In game.cs, in the updateScene function, add this after the call to updatePlayer:

   $camera.update();

And back in camera.cs, add this function definition:

function Camera::update(%this)
{
   if (%this.autoScroll)
   {
      %pos = t2dVectorAdd(%this.camera.getPosition(), %this.scrollDirection);
      %this.camera.setPosition(%pos);
      
      if (%this.trapObject)
      {
         %area = sceneWindow2D.getcurrentCameraArea();
         %x = getWord(%area, 0);
         %y = getWord(%area, 1);
         %width = getWord(%area, 2);
         %height = getWord(%area, 3);
         %this.target.setWorldLimit(CLAMP, %x SPC %y SPC (%x + %width) SPC (%y + %height));
      }
   }
   else
   {
      %this.camera.setPositionX(%this.target.getPositionX() + %this.xOffset);
      
      if (%this.trackHeight)
         %this.camera.setPositionY(%this.target.getPositionY() + %this.yOffset);

      if (%this.facePlayer)
      {
         %move = $moveRight - $moveLeft;
         if ((%move * %this.xOffset) < 0)
            %this.xOffset = -%this.xOffset;
      }
   }
}

This function will be called every frame to update the position of the camera. The first if statement determines if the camera is auto scrolling or not, as set by Camera::setAutoScroll. If it is scrolling, the position of the camera is moved by the amount that we stored in scrollDirection. The next section updates the world limits of the object that is being tracked if trapObject is true so the object can’t leave the bounds of the camera area.

If the camera is not set to auto scroll, then we first update the x position based on the x position of the target object, and the x offset of the camera. Then, if we are tracking height, we update the y position similarly. And finally, if we want the camera to always be in front of the player, we force the x offset to be in the direction the player is facing.

The last thing we need to do is set the camera and player's limit areas. For the camera, we can just use our setLimits function. Add this line right before the call to resetPlayer in the createLevel function in gameData.cs:

   $camera.setLimits(%layer.getArea());

This is telling the camera to never leave the area covered by the tile layer. Since we want the player to be confined to the same area as the camera, we can just call this in the resetPlayer function in player.cs:

   $camera.setTrapObject(true);

Now the player will be trapped in the camera's view area.






Usage


Anytime during the game, you can change any of the camera’s parameters and it will update accordingly. You do this by calling $camera.functionName. So, if you wanted to set the offset of the camera, you would call $camera.setOffset("10 10"); Here are a couple of examples for when you might want to change something on the fly.

  • If you get to a level boss, you may want to change the area that the camera and player are allowed to move.

  • In Mario 3, there are some areas that change from a free moving camera to a scrolling camera.
  • If you want to cut to a cinema scene, you could reset the target object to something in the scene other than the player.



Try messing with some of these by running T2D.exe, opening the console (by pressing '~'), and typing in something like:

$camera.setOffset("-10 25");

or:

$camera.setTrackingForce(1);




Expansion


This system should be easily expandable. Some things you might want to add: Zooming in and out, path following, or edge scrolling (like Mario 2). All you would need to do is add a method to the Camera class, and update the Camera::update function to use the new stuff.

If you have added some functionality to this camera system that you think would be useful to others, feel free to add the code and a simple description of what it does to the additional topics tutorial in this series. For the rest of us, animations.