Simple 2D Camera

Hello, through my Monogame adventures I have pieced together a camera for 2D games. It has code in it from all over. Not all of it is my own but I’ve lost track of the snippets that I have found to make it. As a beginner it was one of the hardest things for me to get right. And find for that matter. It handles movement,zoom and has a VisibleArea Rectangle useful for culling. Thought I would post it on here for any newcomers that are just looking for a copy paste simple camera. Hope it helps someone! And if you have any suggestions to improve it let me know!

To use you have to supply your GraphicsDevice.Viewport in the constructor, call the UpdateCamera method in your Update loop and also supply the GraphicsDevice.Viewport there aswell.

public class Camera
{
    public float Zoom { get; set; }
    public Vector2 Position { get; set; }
    public Rectangle Bounds { get; protected set; }
    public Rectangle VisibleArea { get; protected set; }
    public Matrix Transform { get; protected set; }

    private float currentMouseWheelValue, previousMouseWheelValue, zoom, previousZoom;

    public Camera(Viewport viewport)
    {
        Bounds = viewport.Bounds;
        Zoom = 1f;
        Position = Vector2.Zero;
    }


    private void UpdateVisibleArea()
    {
        var inverseViewMatrix = Matrix.Invert(Transform);

        var tl = Vector2.Transform(Vector2.Zero, inverseViewMatrix);
        var tr = Vector2.Transform(new Vector2(Bounds.X, 0), inverseViewMatrix);
        var bl = Vector2.Transform(new Vector2(0, Bounds.Y), inverseViewMatrix);
        var br = Vector2.Transform(new Vector2(Bounds.Width, Bounds.Height), inverseViewMatrix);

        var min = new Vector2(
            MathHelper.Min(tl.X, MathHelper.Min(tr.X, MathHelper.Min(bl.X, br.X))),
            MathHelper.Min(tl.Y, MathHelper.Min(tr.Y, MathHelper.Min(bl.Y, br.Y))));
        var max = new Vector2(
            MathHelper.Max(tl.X, MathHelper.Max(tr.X, MathHelper.Max(bl.X, br.X))),
            MathHelper.Max(tl.Y, MathHelper.Max(tr.Y, MathHelper.Max(bl.Y, br.Y))));
        VisibleArea = new Rectangle((int)min.X, (int)min.Y, (int)(max.X - min.X), (int)(max.Y - min.Y));
    }

    private void UpdateMatrix()
    {
        Transform = Matrix.CreateTranslation(new Vector3(-Position.X, -Position.Y, 0)) *
                Matrix.CreateScale(Zoom) *
                Matrix.CreateTranslation(new Vector3(Bounds.Width * 0.5f, Bounds.Height * 0.5f, 0));
        UpdateVisibleArea();
    }

    public void MoveCamera(Vector2 movePosition)
    {
        Vector2 newPosition = Position + movePosition;
        Position = newPosition;
    }

    public void AdjustZoom(float zoomAmount)
    {
        Zoom += zoomAmount;
        if (Zoom < .35f)
        {
            Zoom = .35f;
        }
        if (Zoom > 2f)
        {
            Zoom = 2f;
        }
    }

    public void UpdateCamera(Viewport bounds)
    {
        Bounds = bounds.Bounds;
        UpdateMatrix();

        Vector2 cameraMovement = Vector2.Zero;
        int moveSpeed;

        if (Zoom > .8f)
        {
            moveSpeed = 15;
        }
        else if (Zoom < .8f && Zoom >= .6f)
        {
            moveSpeed = 20;
        }
        else if (Zoom < .6f && Zoom > .35f)
        {
            moveSpeed = 25;
        }
        else if (Zoom <= .35f)
        {
            moveSpeed = 30;
        }
        else
        {
            moveSpeed = 10;
        }


        if (Keyboard.GetState().IsKeyDown(KeyBinds.CameraMoveUp))
        {
            cameraMovement.Y = -moveSpeed;
        }

        if (Keyboard.GetState().IsKeyDown(KeyBinds.CameraMoveDown))
        {
            cameraMovement.Y = moveSpeed;
        }

        if (Keyboard.GetState().IsKeyDown(KeyBinds.CameraMoveLeft))
        {
            cameraMovement.X = -moveSpeed;
        }

        if (Keyboard.GetState().IsKeyDown(KeyBinds.CameraMoveRight))
        {
            cameraMovement.X = moveSpeed;
        }

        previousMouseWheelValue = currentMouseWheelValue;
        currentMouseWheelValue = Mouse.GetState().ScrollWheelValue;

        if (currentMouseWheelValue > previousMouseWheelValue)
        {
            AdjustZoom(.05f);
            Console.WriteLine(moveSpeed);
        }

        if (currentMouseWheelValue < previousMouseWheelValue)
        {
            AdjustZoom(-.05f);
            Console.WriteLine(moveSpeed);
        }

        previousZoom = zoom;
        zoom = Zoom;
        if (previousZoom != zoom)
        {
            Console.WriteLine(zoom);

        }

        MoveCamera(cameraMovement);
    }
}
9 Likes

Thanks for sharing, that’s helping me to create my first camera :slight_smile:

1 Like

Someone bumped this 3 days ago, so I thought I would add my two cents.

One thing I would add is a Screen to world method for absolute pointing devices: Mouse and Touchscreen.

Make the inverseViewMatrix a class level property:

public Matrix InverseViewMatrix { get; protected set; } //capitalize all uses

Change the line var inverseViewMatrix = Matrix.Invert(Transform); in the first line of UpdateVisibleArea() to:
InverseViewMatrix = Matrix.Invert(Transform); to remove the local variable.

Add methods:

public Vector2 DeprojectScreenPosition(Vector2 position)
{
   return Vector2.Transform(position, InverseViewMatrix);
}
public Vector2 DeprojectScreenPosition(Point position) // for MouseState.Position
{
   return Vector2.Transform(new Vector2(position.X, position.Y), InverseViewMatrix);
}
1 Like

Hey! I’m pretty new to Monogame and this is my first camera, I’ve done everything you said in your post, but i still don’t get how the actual screen changes position, because not matter how much i tried it doesn’t seem to do anything to the screen, i can show code if needed if you will even see this reply :slight_smile: