Tutorials

Creating a simple XNA Camera class

NivÄ : 100

Libraries such as XNA, DirectX or OpenGL provides a set of facilities for rendering different types such as polygons and objects, but they normally do not include things you are used to in a 3D game engine. The facilities missing is normally something you put together yourself, often using built math facilities in the target library and platform.

This tutorial is meant to be very simplistic and will only show the basics of how you can build a reusable camera class for your own projects.

A camera is defined as a point in 3D space the camera is located and another point in which the camera is pointing towards, in addition the camera has a vector indicating what is up in the world. With these 3 variables we can manipulate the camera as we want to. By using these variables (and a couple of others) we can calculate the matrices needed to translate the 3D world onto the screen. We need 2 matrices; a view matrix that will rotate the world according to the camera in 3D and then we need a projection matrix that will translate the 3D data to the 2D screen.

In XNA we have a lot of math methods to help us achieve this very easy :

First we need to calculate the aspect ratio of the 2D screen, we will need this to create our projection matrix.

this._aspectRatio = ((float)viewport.Width) / ((float)viewport.Height);

We take the viewports width and divide it by the height of the viewport, e.g. 640 / 480 = 1.33333...
Then we can calculate the projection matrix by using a static method on the Matrix class :

this._projectionMatrix = Matrix.CreatePerspectiveFieldOfView(
                            MathHelper.ToRadians(40.0f),
                            this._aspectRatio,
                            1.0f,
                            10000.0f);

 

The first argument is the field of view in radians. By using the MathHelper.ToRadians() method we can convert from angles to radians. In our example we create a field of view of 40 degrees. Then we pass in the aspect ratio we calculated. The last two arguments represents the near clipping distance and the far clipping distance. These arguments are the distance from the camera when objects/polygons are clipped. The projection matrix will most likely not change, unless you rescale the viewport or want to change the field of view or the clipping distances, so it is sufficient to do this during construction of the camera.

We now have the projection matrix, we only need the view matrix in order to complete our camera.

this._viewMatrix = 
    Matrix.CreateLookAt(this._position, this._lookAt, Vector3.Up);

The Matrix class provides a static method called CreateLookAt(), this method takes in the position of the camera the location where the camera is looking at and a up vector which we get from the Vector3 class since we are pretty much going for default stuff. This matrix creation should be called every time you render your scene, in case the camera has changed.

All the code above assumes you have a set of properties and fields setup in your class, the entire class will look something like this :

public class Camera
{
    private Vector3 _position;
    private Vector3 _lookAt;
    private Matrix _viewMatrix;
    private Matrix _projectionMatrix;
    private float _aspectRatio;

    public Camera(Viewport viewport)
    {
        this._aspectRatio = ((float)viewport.Width) / ((float)viewport.Height);
        this._projectionMatrix = Matrix.CreatePerspectiveFieldOfView(
                                    MathHelper.ToRadians(40.0f),
                                    this._aspectRatio,
                                    1.0f,
                                    10000.0f);
    }

    public Vector3 Position
    {
        get { return this._position; }
        set { this._position = value; }
    }
    public Vector3 LookAt
    {
        get { return this._lookAt; }
        set { this._lookAt = value; }
    }
    public Matrix ViewMatrix
    {
        get { return this._viewMatrix; }
    }
    public Matrix ProjectionMatrix
    {
        get { return this._projectionMatrix; }
    }
    public void Update()
    {
        this._viewMatrix = 
            Matrix.CreateLookAt(this._position, this._lookAt, Vector3.Up);
    }
}
 
 
During construction of your game you initialize your camera : 
this._camera = new Camera(graphics.GraphicsDevice.Viewport);
this._camera.LookAt = new Vector3(0.0f, 0.0f, -1.0f);

 

In your Draw method you do something like this :

this._camera.Update();

...

...

When drawing your object(s), walk through the "effects" in the mesh and set the following :

effect.View = this._camera.ViewMatrix;
effect.Projection = this._camera.ProjectionMatrix;

 

 

Then you are good to go with a camera class that is easier to manipulate and have a more better feel to it than using the Matrix, MathHelper and Vector classes directly to achieve the same result. In addition you get an abstraction on top of XNA that will enable you in some ways to be able to create games that aren't aware of the platform they are running on.

Comments

 

XNA 2D Camera | venomation.com said:

Pingback from  XNA 2D Camera | venomation.com

mars 25, 2011 5:52
 

Xna 2D Camera | BoxHacker said:

Pingback from  Xna 2D Camera | BoxHacker

mai 23, 2011 9:07

About Einar Ingebrigtsen

I've been doing software development since forever.. :) Started off with a Commodore 64 attached to my fingers for several years, went on to Amiga, Atari and later the PC. I was involved in the demoscene on all of the above mentioned platforms under the nickname Adept in several different groups; Legion Design, Scoop, Melon Dezign to mention but a few. Started doing professional software development in 1994 for FunCom with the prime responsibility of doing the 3D rendering engie for a PC/DOS game called Deadly Skies. Went on to doing the game engine for Windows/DirectX and Playstation for Dragonheart. Did some SNES coding in between. In 1996 a bunch of us quit FunCom and started our own company called Dimaga Studios. We went on making games for Windows/DirectX, Playstation and SEGA Saturn. The company fusioned in 1997 with Innerloop and the new name was Innerloop Studios. I quit the company later that year and started doing interactive TV solutions for a company called Playbox. Carried on doing interactive TV games and services till 2002 for Playbox and later Hermes Interactive. In 2002 I went on doing something completely different; enterprise level business applications. After working with Visual Studio .net BETA in 2001, and looking at the .net framework and C# we started the process in 2002 on moving the code over to this platform. 6 years later I'm still in the same company (still moving the code to C# :) ). Every now and then I found myself missing doing games fulltime. Early 2007 I got an email from Rune Grothaug at Microsoft Norway (DPE Team - MSDN) with a link to DevelopMentals Canadian Game Camp site. 24 hours later the GameCamp.no domain was purchased and CommunityServer installed on my server. Now I get to feed my game development nerve every now and then; through working the community by creating tutorials, newsposts, forum discussions and holding Game Camp events.