This tutorial will show you how to create a simple camera class that allows for movement, rotation and zoom. It will also give an example input class and method to transform the mouse coordinates so that object picking can still work correctly.
Fields
protected float _zoom; //Camera Zoom Value
protected Matrix _transform; //Camera Transform Matrix
protected Matrix _inverseTransform; //Inverse of Transform Matrix
protected Vector2 _pos; //Camera Position
protected float _rotation; //Camera Rotation Value (Radians)
protected Viewport _viewport; //Cameras Viewport
protected MouseState _mState; //Mouse state
protected KeyboardState _keyState; //Keyboard state
protected Int32 _scroll; //Previous Mouse Scroll Wheel Value
Some general fields are needed for the camera, most are self explanatory and the last 3 are only needed if you wish to control the camera from within this class. The InverseTransform is needed if you use any kind of mouse interaction with objects that are being transformed by the camera.
Properties
public float Zoom
{
get { return _zoom; }
set { _zoom = value; }
}
/// <summary>
/// Camera View Matrix Property
/// </summary>
public Matrix Transform
{
get { return _transform; }
set { _transform = value; }
}
/// <summary>
/// Inverse of the view matrix, can be used to get objects screen coordinates
/// from its object coordinates
/// </summary>
public Matrix InverseTransform
{
get { return _inverseTransform; }
}
public Vector2 Pos
{
get { return _pos; }
set { _pos = value; }
}
public float Rotation
{
get { return _rotation; }
set { _rotation = value; }
}
The protected fields need public properties so that they can be accessed from outside of the class. I have kept the properties simple and not included any logic, all the logic is handled in the update method below.
Update Method
public void Update()
{
//Call Camera Input
Input();
//Clamp zoom value
MathHelper.Clamp(_zoom, 0.01f, 10.0f);
//Clamp rotation value
_rotation = ClampAngle(_rotation);
//Create view matrix
_transform = Matrix.CreateRotationZ(_rotation) *
Matrix.CreateScale(new Vector3(_zoom, _zoom, 1)) *
Matrix.CreateTranslation(_pos.X, _pos.Y, 0);
//Update inverse matrix
_inverseTransform = Matrix.Invert(_transform);
}
The update is fairly straight forward, but I will explain some things. The zoom is clamped because if a negative value is used to scale the scene then the scene will be flipped as well as scaled, this is not what we want. The ClampAngle function just ensures that the angle will be between pi and -pi, the source can be found in the class code at the bottom of the tutorial.
The main part of this update is calculating the transform matrix, I won't go into the maths too much but it basically creates a matrix by combining a rotation matrix, scale matrix and translation matrix. The translation is done last as this means that the scene will rotate around (0,0) in object coordinates.
Constructor
public Camera2D(Viewport viewport)
{
_zoom = 1.0f;
_scroll = 1;
_rotation = 0.0f;
_pos = Vector2.Zero;
_viewport = viewport;
}
The constructor is basic and just sets the camera values.
Using the Camera to Draw
XNA 4.0
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend, null, null, null, null, cam.Transform); //Draw Any Scene Stuff Here spriteBatch.End();
XNA 3.1
spriteBatch.Begin(SpriteBlendMode.AlphaBlend,
SpriteSortMode.Immediate,
SaveStateMode.SaveState,
cam.Transform);
//Draw Any Scene Stuff Here
spriteBatch.End();
Drawing is simple, anything that you want to be manipulated by the camera should go between these begin and end calls. If you want a GUI or anything that won't be affected by the camera view you can draw between standard Begin() and End() calls.
Transforming Mouse to Object Coordinates
mState = Mouse.GetState();
Vector2 mouse = new Vector2(mState.X, mState.Y);
mouse = Vector2.Transform(mouse, cam.InverseTransform);
To use the mouse to interact with objects that are shown using the camera, first the mousestate should be captured, the x and y coordinates must then be used to populate a Vector2. This Vector2 can then be transformed using the inverse of the camera transform matrix. Now the X and Y of the Vector2 are the X and Y coordinates of the Mouse in object space. You can use these coordinates in the same way you normally use the mouse X and Y coordinates.
That's pretty much all there is to it, this can be used in pretty much any 2D game that needs to scroll, zoom or rotate and as you can see it is fairly easy to implement. Here is the full class code including the ClampAngle method and an example Input method.
Full Class Code
#region Version History (1.0)
// 03.07.11 ~ Created
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace CameraTest
{
public class Camera2D
{
#region Fields
protected float _zoom;
protected Matrix _transform;
protected Matrix _inverseTransform;
protected Vector2 _pos;
protected float _rotation;
protected Viewport _viewport;
protected MouseState _mState;
protected KeyboardState _keyState;
protected Int32 _scroll;
#endregion
#region Properties
public float Zoom
{
get { return _zoom; }
set { _zoom = value; }
}
/// <summary>
/// Camera View Matrix Property
/// </summary>
public Matrix Transform
{
get { return _transform; }
set { _transform = value; }
}
/// <summary>
/// Inverse of the view matrix, can be used to get objects screen coordinates
/// from its object coordinates
/// </summary>
public Matrix InverseTransform
{
get { return _inverseTransform; }
}
public Vector2 Pos
{
get { return _pos; }
set { _pos = value; }
}
public float Rotation
{
get { return _rotation; }
set { _rotation = value; }
}
#endregion
#region Constructor
public Camera2D(Viewport viewport)
{
_zoom = 1.0f;
_scroll = 1;
_rotation = 0.0f;
_pos = Vector2.Zero;
_viewport = viewport;
}
#endregion
#region Methods
/// <summary>
/// Update the camera view
/// </summary>
public void Update()
{
//Call Camera Input
Input();
//Clamp zoom value
_zoom = MathHelper.Clamp(_zoom, 0.0f, 10.0f);
//Clamp rotation value
_rotation = ClampAngle(_rotation);
//Create view matrix
_transform = Matrix.CreateRotationZ(_rotation) *
Matrix.CreateScale(new Vector3(_zoom, _zoom, 1)) *
Matrix.CreateTranslation(_pos.X, _pos.Y, 0);
//Update inverse matrix
_inverseTransform = Matrix.Invert(_transform);
}
/// <summary>
/// Example Input Method, rotates using cursor keys and zooms using mouse wheel
/// </summary>
protected virtual void Input()
{
_mState = Mouse.GetState();
_keyState = Keyboard.GetState();
//Check zoom
if (_mState.ScrollWheelValue > _scroll)
{
_zoom += 0.1f;
_scroll = _mState.ScrollWheelValue;
}
else if (_mState.ScrollWheelValue < _scroll)
{
_zoom -= 0.1f;
_scroll = _mState.ScrollWheelValue;
}
//Check rotation
if (_keyState.IsKeyDown(Keys.Left))
{
_rotation -= 0.1f;
}
if (_keyState.IsKeyDown(Keys.Right))
{
_rotation += 0.1f;
}
//Check Move
if (_keyState.IsKeyDown(Keys.A))
{
_pos.X += 0.5f;
}
if (_keyState.IsKeyDown(Keys.D))
{
_pos.X -= 0.5f;
}
if (_keyState.IsKeyDown(Keys.W))
{
_pos.Y += 0.5f;
}
if (_keyState.IsKeyDown(Keys.S))
{
_pos.Y -= 0.5f;
}
}
/// <summary>
/// Clamps a radian value between -pi and pi
/// </summary>
/// <param name="radians">angle to be clamped</param>
/// <returns>clamped angle</returns>
protected float ClampAngle(float radians)
{
while (radians < -MathHelper.Pi)
{
radians += MathHelper.TwoPi;
}
while (radians > MathHelper.Pi)
{
radians -= MathHelper.TwoPi;
}
return radians;
}
#endregion
}
}





MultiQuote






|