Alrighty. Some may say a 2D camera is ‘useless’ and kind of overkill for a 2D game. I’d like to politely disagree :). There are certain instances where a 2D camera-like interface can lead to a better overall design of the game and reduce headaches later on when you’re trying to figure out why your sprite avatar isn’t on screen (while looking at that sloppy ‘moving’ code to emulate a camera). There’s probably more than a few 2D games that emulate using a 2D camera, but underneath it might not look like that at all. With XNA, C#, and an object-oriented mind set, why not just create the functionality of a 2D camera and be done with it?
This tutorial will cover a very basic camera for 2D. I’ve seen in several places people that are just starting out their 2D games in XNA and other languages asking ‘how do I move my view/camera/follow my character?’. While this tutorial will show how to do that, there is alot of room for improvement in terms of how to organize a more complex 2D scene. Although that is not the goal of this tutorial. I’m going to try to keep it short, minimalistic, and leave room for expansion. I’ve made a bit more sophisticated 2D camera system before that allowed for zooming, rotation, and following of characters, but that isn’t in this tutorial. If there are enough requests I may add additional series to this tutorial, extending the functionality of this camera.
Camera2D class
I first started off making an empty class called Camera2D. We’ll need a few members, properties, and method stubs to get us started off.
private SpriteBatch spriteRenderer;
private Vector2 cameraPosition;
public Vector2 Position
{
get { return cameraPosition; }
set { cameraPosition = value; }
}
Those are the members and properties we’ll need for now. Here is the constructor I created:
public Camera2D(SpriteBatch renderer)
{
spriteRenderer = renderer;
cameraPosition = new Vector2(0, 0);
}
Followed by the almighty draw method:
public void DrawNode(Scene2DNode node)
{
// get the screen position of the node
Vector2 drawPosition = ApplyTransformations(node.Position);
node.Draw(spriteRenderer, drawPosition);
}
It’s a pretty straightforward method. I call the one method ‘ApplyTransformations’ to give me the final screen coordinates of where to actually draw this node.
Sidenote: This is where you could optimize on larger levels and do a rectangle overlap check with the node’s final drawPosition and the camera’s screen position to see you need to draw this node at all.
Next we should define our ApplyTransformation function.
private Vector2 ApplyTransformations(Vector2 nodePosition)
{
// apply translation
Vector2 finalPosition = nodePosition – cameraPosition;
// you can apply scaling and rotation here also
//.....
/--------------------------------------
return finalPosition;
}
As you can see above, once you understand scaling,rotation,translation matrices you can do all sorts of cool stuff to the position of this node to create zoom and rotational effects with the camera class. Translation is the easiest to implement so that is all I’m going to show for now. Maybe more on this in a future tutorial.
We also want our camera to be able to move in the world! So let’s add a Translate method for our camera class so it will move around in world space.
public void Translate(Vector2 moveVector)
{
cameraPosition += moveVector;
}
Tada! And that’s it for the camera class. Short and sweet. Some things to note though:
- notice this class doesn’t reload the spriteBatch when the device is reset. Yea well…it should become standard practice when dealing with XNA so adding it here is trivial.
- If you want to start getting into more complex 2D scenes (with parallaxing and rotational sprites) you’ll be better suited to use this camera class in some sort of 2D scene manager class where it can handle all the sprites in one go rather than taking one node at a time.
Alright let’s move onto creating our basic Scene2DNode class…
Scene2DNode class
We’ll start off with the members and properties we need for this class:
private Texture2D texture;
private Vector2 worldPosition;
public Vector2 Position
{
get { return worldPosition; }
set { worldPosition = value; }
}
The above is just our standard texture and the position of this node in our world space. Make note that world space and screen space are different things (the more technical term would be coordinate spaces if you wish to look it up).
Now we make a basic public constructor.
public Scene2DNode(Texture2D texture, Vector2 position)
{
this.texture = texture;
this.worldPosition = position;
}
Nothing fancy here. We move along to our Draw method:
public void Draw(SpriteBatch renderer, Vector2 drawPosition)
{
renderer.Draw(texture, drawPosition, Color.White);
}
This method is called by our camera class. Our camera passes this node its final screen coordinates to draw itself with. Notice I’m not using worldPosition. Just want to emphasize that they are two different positions and should not be interchanged (alot of people seem to trip over this concept, especially when moving onto scaling and rotation).
Again some things to note:
- This class doesn’t reload it’s texture. I leave it up to the reader do this themselves. Even if you do not, the example that comes with this tutorial will work fine as long as you don’t fiddle with the graphics window.
- You should probably extend Scene2DNode so you can use it for more robust classes (like your character avatar perhaps?).
Some things you could add to this camera class:
- Zooming
- Rotation
- Object Tracking
- Smooth movement (with interia if you’re feeling adventurous)
Hopefully, this gave you an idea of one approach to emulating a camera in a 2D system and provided you with a framework to use in your own games if you so choose. Feedback and questions are welcome.
Here is the complete source and also a quick example application showing the camera class in action with Scene2DNode. Use WSAD to move the camera. Enjoy!
Download: XNA Camera 2D Tutorial.zip