1
\$\begingroup\$

I have a simple camera class which works for following my character around, and has an adjustable zoom. However, I cannot figure out how to keep the camera within the bounds of the world. As far as I know, I can't just pull the X and Y coordinates of the Transform, so I don't know how to check if it is out of bounds. Help greatly appreciated.

using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Game10
{
class Camera
{
    protected float _zoom;
    public Matrix Transform { get; private set; }

    public Camera()
    {
        _zoom = 1f;
    }

    public void DecreaseZoom()
    {
        if (Input.IsZoomDecrease())
           {
                _zoom -= 0.25f;
                if (_zoom < 0.25f
                {
                    _zoom = 0.25f;
                }
           }
    }


    public void IncreaseZoom()
    {
        if (Input.IsZoomIncrease())
           {
                _zoom += 0.25f;
           }
                if (_zoom > 4f
                {
                    _zoom = 4f;
                }
    }

    public void Follow(Player target)
    {
        var position = Matrix.CreateTranslation(
            -target.Position.X - (target.Bounds.Width / 2),
            -target.Position.Y - (target.Bounds.Height / 2),
            0);

        var offset = Matrix.CreateTranslation(
            Platformer.ScreenWidth / _zoom / 2,
            Platformer.ScreenHeight / _zoom / 2,
            0);

        var zoom = Matrix.CreateScale(_zoom, _zoom, 1);

        Transform = position * offset * zoom;


    }

}
}
\$\endgroup\$
  • 1
    \$\begingroup\$ Sorry, why can't you pull x and y values out of the transform? The rendering pipeline has to use this information to position everything correctly, so it's not clear to me what would prevent you from accessing it elsewhere. In fact, it looks like you compute new x and y values every time you run Follow() so why not put your bounds clamping right there? \$\endgroup\$ – DMGregory Mar 15 '18 at 10:41
  • \$\begingroup\$ jgallant.com/… \$\endgroup\$ – jgallant Mar 15 '18 at 11:49
  • \$\begingroup\$ I suppose I just don't know the proper method to pull the x and you coordinates out of a matrix. What should I call? \$\endgroup\$ – LankyJon Mar 15 '18 at 17:10
  • \$\begingroup\$ Oh, just saw the link you posted. I'll read through that when I get the chance. \$\endgroup\$ – LankyJon Mar 15 '18 at 17:13
  • \$\begingroup\$ A good idea of how to make a camera in XNA or Monogame: dreamincode.net/forums/topic/237979-2d-camera-in-xna \$\endgroup\$ – Saliken Mar 16 '18 at 20:21
1
\$\begingroup\$

I think your camera class needs some tuning up. If you follow along with this link you'll have a nice implementation to work with.

Now on to the matter of restricting your camera to the world boundaries.

The algorithm looks like this:

  1. Take the camera boundaries (position + viewport) and transform them into world coordinates.
  2. Restrict those coordinates so that they are inside the world boundaries.
  3. Transform the restricted coordinates back into camera coordinates.

Note: I'm assuming the camera position is supposed to align with the center of the viewpoint.

In order to find the world space coordinates you will need an inverse transform which you can get from the transform like this: var inverseTransform = Matrix.Invert(transform);

//Requirements
Vector2 cameraPosition;
Viewport viewport;
Matrix transform;
Matrix inverseTransform;

//Get the top left and bottom right position of the camera in screen space.
var cameraTopLeft = cameraPosition - new Vector2(viewport.Width / 2f, viewport.Height / 2f);
var cameraBottomRight = cameraPosition + new Vector2(viewport.Width / 2f, viewport.Height / 2f);

//Transform the screenspace coordinates into world space coordinates.
var cameraTopLeftWorld = Vector2.Transform(cameraTopLeft, inverseTransform);
var cameraBottomRightWorld = Vector2.Transform(cameraBottomRight, inverseTransform);

//Create the world space boundary rectangle of the camera
var width = cameraBottomRightWorld.X - cameraTopLeftWorld.X;
var height = cameraBottomRightWorld.Y - cameraTopLeftWorld.Y;
var bounds = new Rectangle(cameraTopLeftWorld.X, cameraTopLeftWorld.Y, width, height);

//Use the bounds rectangle to restrict the camera position.
if(bounds.X < worldBounds.X) bounds.X = worldBounds.X; //Off the left side
if(bounds.Y < worldBounds.Y) bounds.Y = worldBounds.Y; //Off the top side
if(bounds.Right > worldBounds.Right) bounds.X = worldBounds.Right - bounds.Width; //Off the right side
if(bounds.Bottom > worldBounds.Bottom) bounds.Y = worldBounds.Bottom - bounds.Height; //Off the bottom side

//Now take the restricted boundaries and transform them back into a position for the camera
var boundsCenter = new Vector2(bounds.X + bounds.Width / 2f, bounds.Y + bounds.Height / 2f);
var cameraCenterPosition = Vector2.Transform(boundsCenter, transform);
cameraPosition = cameraCenterPosition;

This algorithm is quite basic but works well. However, if you include rotation into your camera then this will fail.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.