What is the best way to create a parallax effect in an XNA game? I would like the camera to follow my sprite as it moves across the world, that way I can build in effects like zoom, panning, shake, and other effects. Anybody have a solid example of how this is done, preferably in a GameComponent?
Asked
Active
Viewed 5.0k times
4 Answers
45
So I figured it out using a combination of the tutorials above and have created the class below. It tweens towards your target and follows it around. Try it out.
public interface IFocusable
{
Vector2 Position { get; }
}
public interface ICamera2D
{
/// <summary>
/// Gets or sets the position of the camera
/// </summary>
/// <value>The position.</value>
Vector2 Position { get; set; }
/// <summary>
/// Gets or sets the move speed of the camera.
/// The camera will tween to its destination.
/// </summary>
/// <value>The move speed.</value>
float MoveSpeed { get; set; }
/// <summary>
/// Gets or sets the rotation of the camera.
/// </summary>
/// <value>The rotation.</value>
float Rotation { get; set; }
/// <summary>
/// Gets the origin of the viewport (accounts for Scale)
/// </summary>
/// <value>The origin.</value>
Vector2 Origin { get; }
/// <summary>
/// Gets or sets the scale of the Camera
/// </summary>
/// <value>The scale.</value>
float Scale { get; set; }
/// <summary>
/// Gets the screen center (does not account for Scale)
/// </summary>
/// <value>The screen center.</value>
Vector2 ScreenCenter { get; }
/// <summary>
/// Gets the transform that can be applied to
/// the SpriteBatch Class.
/// </summary>
/// <see cref="SpriteBatch"/>
/// <value>The transform.</value>
Matrix Transform { get; }
/// <summary>
/// Gets or sets the focus of the Camera.
/// </summary>
/// <seealso cref="IFocusable"/>
/// <value>The focus.</value>
IFocusable Focus { get; set; }
/// <summary>
/// Determines whether the target is in view given the specified position.
/// This can be used to increase performance by not drawing objects
/// directly in the viewport
/// </summary>
/// <param name="position">The position.</param>
/// <param name="texture">The texture.</param>
/// <returns>
/// <c>true</c> if the target is in view at the specified position; otherwise, <c>false</c>.
/// </returns>
bool IsInView(Vector2 position, Texture2D texture);
}
public class Camera2D : GameComponent, ICamera2D
{
private Vector2 _position;
protected float _viewportHeight;
protected float _viewportWidth;
public Camera2D(Game game)
: base(game)
{}
#region Properties
public Vector2 Position
{
get { return _position; }
set { _position = value; }
}
public float Rotation { get; set; }
public Vector2 Origin { get; set; }
public float Scale { get; set; }
public Vector2 ScreenCenter { get; protected set; }
public Matrix Transform { get; set; }
public IFocusable Focus { get; set; }
public float MoveSpeed { get; set; }
#endregion
/// <summary>
/// Called when the GameComponent needs to be initialized.
/// </summary>
public override void Initialize()
{
_viewportWidth = Game.GraphicsDevice.Viewport.Width;
_viewportHeight = Game.GraphicsDevice.Viewport.Height;
ScreenCenter = new Vector2(_viewportWidth/2, _viewportHeight/2);
Scale = 1;
MoveSpeed = 1.25f;
base.Initialize();
}
public override void Update(GameTime gameTime)
{
// Create the Transform used by any
// spritebatch process
Transform = Matrix.Identity*
Matrix.CreateTranslation(-Position.X, -Position.Y, 0)*
Matrix.CreateRotationZ(Rotation)*
Matrix.CreateTranslation(Origin.X, Origin.Y, 0)*
Matrix.CreateScale(new Vector3(Scale, Scale, Scale));
Origin = ScreenCenter / Scale;
// Move the Camera to the position that it needs to go
var delta = (float) gameTime.ElapsedGameTime.TotalSeconds;
_position.X += (Focus.Position.X - Position.X) * MoveSpeed * delta;
_position.Y += (Focus.Position.Y - Position.Y) * MoveSpeed * delta;
base.Update(gameTime);
}
/// <summary>
/// Determines whether the target is in view given the specified position.
/// This can be used to increase performance by not drawing objects
/// directly in the viewport
/// </summary>
/// <param name="position">The position.</param>
/// <param name="texture">The texture.</param>
/// <returns>
/// <c>true</c> if [is in view] [the specified position]; otherwise, <c>false</c>.
/// </returns>
public bool IsInView(Vector2 position, Texture2D texture)
{
// If the object is not within the horizontal bounds of the screen
if ( (position.X + texture.Width) < (Position.X - Origin.X) || (position.X) > (Position.X + Origin.X) )
return false;
// If the object is not within the vertical bounds of the screen
if ((position.Y + texture.Height) < (Position.Y - Origin.Y) || (position.Y) > (Position.Y + Origin.Y))
return false;
// In View
return true;
}
}
And Here is how you would use it with SpriteBatch
:
spriteBatch.Begin(SpriteBlendMode.AlphaBlend,
SpriteSortMode.FrontToBack,
SaveStateMode.SaveState,
Camera.Transform);
spriteBatch.Draw(_heliTexture,
_heliPosition,
heliSourceRectangle,
Color.White,
0.0f,
new Vector2(0,0),
0.5f,
SpriteEffects.FlipHorizontally,
0.0f);
spriteBatch.End();
Let Me know if this helps you out, and thanks to StackOverflow and the community. W00t!

Leri
- 12,367
- 7
- 43
- 60

Khalid Abuhakmeh
- 10,709
- 10
- 52
- 75
-
I was trying this code, but when I initialized it `Camera2D camera= new Camera2D`, I noticed I needed to pass in a Game variable. What variable would that be? – Dominic K Apr 03 '10 at 00:19
-
5If the `IFocusable` Focus property should never be empty, it would be better if the ctor was: `Camera2D(Game game, IFocusable focused)` – Andreas Grech Feb 16 '11 at 17:32
-
4In the Update method, you're probably going to want to calculate the Origin property before calculating the transformation matrix. Otherwise there will be a temporary disparity between the origin used the the transformation and the latest calculated origin based on scale. Also, you can just do Matrix.CreateScale(Scale) without explicitly creating a new Vector3. – Factor Mystic Mar 06 '11 at 05:26
-
I know this is an older thread, but it's a terrific solution and I'd love to implement it to understand it better. The only issue is that I can't seem to get it working. I have all the parts of the class wired up, it's initialized, it's updating, no errors are thrown in code. However, at runtime, a NullReferenceException is thrown at this point in the class Update override: _position.X += (Focus.Position.X - Position.X) * MoveSpeed * delta; _position.Y += (Focus.Position.Y - Position.Y) * MoveSpeed * delta; the debugger says that camera.Focus is null...I've been trying everything. Any ideas?? – Dec 27 '09 at 05:01
-
Wow, it's been a while. But if I am reading my code correctly then you need a sprite to implement a IFocusable interface. Then you need to pass that sprite to the camera. Hope this helps. – Khalid Abuhakmeh Dec 28 '09 at 00:12
-
You're right! So elegant, thanks! I was trying to brute force it by just slapping the focus into a Vector2 and circumventing the whole point of using an interface, but now I see the light! Thanks so much for the response :) – Dec 29 '09 at 06:33
-
I've had some trouble implementing this at first. For those who are having the null camera problem `Components.Add(camera);` inside the `Initialize()`. Awesome camera btw :) – Bogdan Rybak Dec 18 '13 at 09:37
19
Here are some tutorials on implementing a 2D camera in XNA:

Martin.
- 10,494
- 3
- 42
- 68

David Brown
- 35,411
- 11
- 83
- 132
-
1cool, thanks. I have to look at them and see if they will work first. – Khalid Abuhakmeh Apr 03 '09 at 12:43
4
Here's another one
http://www.david-amador.com/2009/10/xna-camera-2d-with-zoom-and-rotation/

DJLink
- 41
- 1
2
I know it's an old question but i had the same one and i have found this great Monogame camera library so i thought i should share...
https://github.com/aloisdeniel/Comora
It is really easy to install and to follow sprite you only need to add
this.camera.Position = position of your sprite;

Gojko Ubiparip
- 45
- 2
- 6