Camera2D slowing down towards min/max

Topics: User Forum
Oct 15, 2013 at 6:25 PM
Edited Oct 15, 2013 at 11:36 PM
Hi Guys,
I'm making use of the Camera2D class released in the samples together with MonoGame. However I am having trouble with the camera movement speed. Let me explain the situation.

I have made a level which is twice my resolution of 1920 in width. In this level I have a player which is the TrackingBody of the camera.

The camera has the following temporary test min/max positions which balance it exactly on the left and right borders:
            Camera.MinPosition = new Vector2(960, 0);
            Camera.MaxPosition = new Vector2(2880, 540);
The player can move left/right and perform a simple jump. The tracking works fine as long as I dont drag and flick it away :P. When the player approaches the min or max position of the camera the camera starts to slow down. At the slowest moving one pixel.

I know this is intended behavior, but is there a way I can keep the camera the same smooth speed as the player is moving by removing this "inertia".

PS. I tried messing with the inertia, but I dont quite understand the calculation and the camera started blinking weirdly.

EDIT: Camera2D from the Farseer samples version 3.5
EDIT 2: Perhaps someone has a good parallax background example that uses the Camera2D? I used Tutorial to get it to work.
Oct 16, 2013 at 5:45 PM
What code are you executing to update the camera?
Oct 16, 2013 at 6:10 PM
Camera2D has its own Update method to update. This is all original Camera2D code as I dont yet understand exactly what is happening here. Matrix calculations have been a while :P. Here it is:
        /// <summary>
        /// Moves the camera forward one timestep.
        /// </summary>
        public void Update(GameTime gameTime)
        {
            if (_trackingBody != null)
            {
                if (_positionTracking)
                {
                    _targetPosition = _trackingBody.Position;
                    if (_minPosition != _maxPosition)
                    {
                        Vector2.Clamp(ref _targetPosition, ref _minPosition, ref _maxPosition, out _targetPosition);
                    }
                }
                if (_rotationTracking)
                {
                    _targetRotation = -_trackingBody.Rotation % MathHelper.TwoPi;
                    if (_minRotation != _maxRotation)
                    {
                        _targetRotation = MathHelper.Clamp(_targetRotation, _minRotation, _maxRotation);
                    }
                }
            }
            Vector2 delta = _targetPosition - _currentPosition;
            float distance = delta.Length();
            if (distance > 0f)
            {
                delta /= distance;
            }
            float inertia;
            if (distance < 10f)
            {
                inertia = (float)Math.Pow(distance / 10.0, 2.0);
            }
            else
            {
                inertia = 1f;
            }

            float rotDelta = _targetRotation - _currentRotation;

            float rotInertia;
            if (Math.Abs(rotDelta) < 5f)
            {
                rotInertia = (float)Math.Pow(rotDelta / 5.0, 2.0);
            }
            else
            {
                rotInertia = 1f;
            }
            if (Math.Abs(rotDelta) > 0f)
            {
                rotDelta /= Math.Abs(rotDelta);
            }

            _currentPosition += 100f * delta * inertia * (float)gameTime.ElapsedGameTime.TotalSeconds;
            _currentRotation += 80f * rotDelta * rotInertia * (float)gameTime.ElapsedGameTime.TotalSeconds;

            SetView();
        }
Here is the SetView(); method:
private void SetView()
        {
            Matrix matRotation = Matrix.CreateRotationZ(_currentRotation);
            Matrix matZoom = Matrix.CreateScale(_currentZoom);
            Vector3 translateCenter = new Vector3(_translateCenter, 0f);
            Vector3 translateBody = new Vector3(-_currentPosition, 0f);

            SimView = Matrix.CreateTranslation(translateBody) * matRotation * matZoom * Matrix.CreateTranslation(translateCenter);

            translateCenter = ConvertUnits.ToDisplayUnits(translateCenter);
            translateBody = ConvertUnits.ToDisplayUnits(translateBody);

            View = Matrix.CreateTranslation(translateBody) * matRotation * matZoom * Matrix.CreateTranslation(translateCenter);
        }
Oct 22, 2013 at 12:11 AM
If you want to keep an object in the centre of the screen you can use Camera.SetCamera() rather than setting the position and using Camera.Update().

If you want a smooth step without the slow down you need to remove anything in the update statement that alters the distance over time. Basically the inertia value.

Off the top of my head here's a quickly (note quickly!) modified update statement. It probably won't solve your problem but it'll might point you in the right direction. All it does is use the normalised vector of the delta to move the camera. This should give it a slow but constant(ish) speed. There's probably a better way of doing it.
        public void Update(GameTime gameTime)
        {
            if (_trackingBody != null)
            {
                if (_positionTracking)
                {
                    _targetPosition = _trackingBody.Position;
                    if (_minPosition != _maxPosition)
                    {
                        Vector2.Clamp(ref _targetPosition, ref _minPosition, ref _maxPosition, out _targetPosition);
                    }
                }
                if (_rotationTracking)
                {
                    _targetRotation = -_trackingBody.Rotation % MathHelper.TwoPi;
                    if (_minRotation != _maxRotation)
                    {
                        _targetRotation = MathHelper.Clamp(_targetRotation, _minRotation, _maxRotation);
                    }
                }
            }

            if (TrackingIgnoreX)
                _targetPosition.X = _currentPosition.X;
 
            if (TrackingIgnoreY)
                _targetPosition.Y = _currentPosition.Y;

            Vector2 delta = _targetPosition - _currentPosition;

            float distance = delta.Length();

            if (distance < 0.1f)
            {
                _currentPosition = _targetPosition;
                delta = Vector2.Zero;

            }

            delta.Normalize();

            if (delta == Vector2.Zero)
                return;

            if (distance < 0.1f)
            {
                _currentPosition = _targetPosition;
                delta = Vector2.Zero;
            }

            float rotDelta = _targetRotation - _currentRotation;

            float rotInertia;
            if (Math.Abs(rotDelta) < 5f)
            {
                rotInertia = (float)Math.Pow(rotDelta / 5.0, 2.0);
            }
            else
            {
                rotInertia = 1f;
            }
            if (Math.Abs(rotDelta) > 0f)
            {
                rotDelta /= Math.Abs(rotDelta);
            }

            //            _currentPosition += 100f * delta * inertia * (float)gameTime.ElapsedGameTime.TotalSeconds;

            _currentPosition += delta * (float)gameTime.ElapsedGameTime.TotalSeconds;
            _currentRotation += 80f * rotDelta * rotInertia * (float)gameTime.ElapsedGameTime.TotalSeconds;

            SetView();
        }
Oct 23, 2013 at 1:05 AM
Thanks for the reply Hal. I have been busy building an inventory system and will look at this as soon as I can and let you know if it worked out.