0
\$\begingroup\$

I have a camera that works as long as its stationary. Now I'm trying to create a child class of that camera class that will look at its target.

The new addition to the class is a method called SetTarget(). The method takes in a Vector3 target. The camera wont move but I need it to rotate to look at the target. If I just set the target, and then call CreateLookAt() (which takes in position, target, and up), when the object gets far enough away and underneath the camera, it suddenly flips right side up. So I need to transform the up vector, which currently always stays at Vector3.Up. I feel like this has something to do with taking the angle between the old direction vector and the new one (which I know can be expressed by target - position).

I feel like this is all really vague, so here's the code for my base camera class:

public class BasicCamera : Microsoft.Xna.Framework.GameComponent
    {
        public Matrix view { get; protected set; }
        public Matrix projection { get; protected set; }

        public Vector3 position { get; protected set; }
        public Vector3 direction { get; protected set; }
        public Vector3 up { get; protected set; }
        public Vector3 side
        {
            get { return Vector3.Cross(up, direction); }
            protected set { }
        }

        public BasicCamera(Game game, Vector3 position, Vector3 target, Vector3 up)
            : base(game)
        {
            this.position = position;
            this.direction = target - position;
            this.up = up;

            CreateLookAt();

            projection = Matrix.CreatePerspectiveFieldOfView(
                MathHelper.PiOver4,
                (float)Game.Window.ClientBounds.Width /
                (float)Game.Window.ClientBounds.Height,
                1, 500);
        }

        public override void Update(GameTime gameTime)
        {
            // TODO: Add your update code here
            CreateLookAt();

            base.Update(gameTime);
        }
}

And this is the code for the class that extends the above class to look at its target.

class TargetedCamera : BasicCamera
    {
        public Vector3 target { get; protected set; }

        public TargetedCamera(Game game,
            Vector3 position,
            Vector3 target,
            Vector3 up)
            : base(game, position, target, up)
        {
            this.target = target;
        }

        public void SetTarget(Vector3 target)
        {
            direction = target - position;

        }

        protected override void CreateLookAt()
        {
            view = Matrix.CreateLookAt(position, target, up);
        }
    }
\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

Where do you want the camera's up to be when the object is below the camera? The roll of the camera can't be calculated when cross(dir, up) is zero and inaccuracies will result in mostly random roll values. You can cache the cameras up vector of the last frame and use it as up in CreateLookAt instead of your static up vector when cross(dir, world_up) gets to small, but it depends on how you want it to behave.

\$\endgroup\$
0
\$\begingroup\$

Whenever you call Matrix.CreateLookAt(pos, targ, Up), use this for you up vector arg:

Vector3 currentUp = Matix.Invert(lastFramesView).Up;

and your camera wont flip anymore. By setting it to Vector3.Up it will always keep it's Y component (pitch) a positive number. When you look down and start to go inverted (up side down), it flips to keep the Y component positive. But a good thing about using Vector3.Up is that it also keeps the X & Z where it needs to be to keep the camera from any roll.

By using last frame's world space up vector, you remove the +Y pitch aspect so it will remain inverted but be careful, you also eliminate the roll prevention that Vector3.Up gave you.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .