Game Development Community

[Solved] Super Mario-esque 2D Camera

by Bryant Drew Jones · in Torque X 2D · 01/11/2010 (12:07 am) · 6 replies

Hello everyone :)

I just started working with TX2D to create what is essentially a 2D sidescrolling version of Pilotwings. I'm having trouble customizing the camera (I still haven't wrapped my head around all of the various coordinate systems going on...I'm sure that's part of the problem). I started by mounting the camera to the player; the player always remained in the centre of the screen making it look like the world was moving and not the player. To fix this, I added a small mount force to the camera, giving it a bit of lag. That lag behaviour is exactly what I'm looking for, but in reverse. Rather than having the camera lag behind the player, I would like it to look ahead of the player. (I tried setting the mount force to a negative value but that led to disaster :D )

If you look at the camera in any 2D Super Mario game, it moves as if there's an invisible bounding box in the centre of the screen. There's a dead zone in the middle where, if the player is moving within this space, the camera remains stationary. If the player starts travelling to the right, the camera will position the player at (X = ScreenWidth / 3), leaving 2/3 of the screen to show what obstacles are coming up.

I hope I'm making myself clear. I've searched the forums high and low, and while many people discuss this same problem at a conceptual level, I haven't come across any guidance on how to actually implement this system in TX2D.

Any thoughts? :

Thanks in advance!

-Bryant

#1
01/12/2010 (4:09 am)
you could try something like this

public void ProcessTick(Move move, float elapsed)
{
     if(move != null)
     {
          // set our test object's Velocity based on stick input
          _sceneObject.Physics.VelocityX = move.Sticks[0].X * 40.0f;

          // set the cameras' position based on the objects position
          // the Offset float value controls when the camera will start
          // scrolling somewhere around 30f, should keep 2/3 ahead of
          // the player visible at a 1280x720 resolution
          if (_sceneObject.Position.X >= _camera.Position.X - Offset)
                    _camera.Position = new Vector2(_sceneObject.Position.X + Offset, _camera.Position.Y);
     }
}
#2
01/12/2010 (4:37 pm)
Thanks for the quick response, Alex. I ended up creating a CameraFollowComponent that I attached to my player. Inside the component I created a blank scene object that I positioned at an offset determined by the player’s velocity. Here’s the code in case anyone’s interested:

private T2DSceneCamera camera;
        private T2DSceneObject cameraMount;
        private T2DStaticSprite player;

        private Vector2 targetPosition;

        /// <summary>
        /// Camera only moves if player.Physics.Velocity.LengthSquared() is
        /// greater than this value. Prevents jerkiness when the player is at rest.
        /// </summary>
        private const int minimumVelocitySquared = 300;

        /// <summary>
        /// Used to determine how quickly the camera moves to the target position. The
        /// faster the player is moving, the faster the camera will move to catch up.
        /// When the player's velocity squared is greater than or equal to this value,
        /// the camera's position maps one-to-one with the cameraMount.
        /// </summary>
        private const int maximumVelocitySquared = 490000; //(int)Math.Pow(800, 2);

        protected override bool _OnRegister(TorqueObject owner)
        {
            if (!base._OnRegister(owner) || !(owner is T2DSceneObject))
                return false;

            ProcessList.Instance.AddTickCallback(Owner, this);

            camera = TorqueObjectDatabase.Instance.FindObject<T2DSceneCamera>("Camera");
            player = TorqueObjectDatabase.Instance.FindObject<T2DStaticSprite>("player");

            cameraMount = new T2DSceneObject();
            cameraMount.CollisionsEnabled = false;
            cameraMount.Position = player.Position;
            TorqueObjectDatabase.Instance.Register(cameraMount);

            // Every level must contain a blank scene object sized to the bounds of the camera.
            // This scene object tells the camera where the edges of the world are.
            T2DSceneObject background = TorqueObjectDatabase.Instance.FindObject<T2DSceneObject>("background");

            // Bound the camera so it doesn't look outside the edges of the world
            camera.CameraWorldLimitMin = new Vector2(0, camera.Extent.Y - background.Size.Y);
            camera.CameraWorldLimitMax = new Vector2(background.Size.X - camera.Extent.X, 0);
            camera.UseCameraWorldLimits = true;

            camera.Mount(cameraMount, "", true);
            camera.TrackMountRotation = false;
            camera.UseMountForce = true;
            camera.MountForce = 3.0f;

            targetPosition = cameraMount.Position;

            return true;
        }

        public virtual void ProcessTick(Move move, float dt)
        {
            // If the player is moving faster than the threshold level, reposition the camera
            if (player.Physics.Velocity.LengthSquared() > 0 && player.Physics.Velocity.LengthSquared() > minimumVelocitySquared)
            {
                // Calculate the direction the player is moving in
                Vector2 normalizedVector = Vector2.Normalize(player.Physics.Velocity);

                // Position the camera mount at an offset from the player's origin
                Vector2 targetDirection = Vector2.Multiply(normalizedVector, camera.Extent.Y / 2);

                // Move the offseted position to the player's new location
                targetPosition = Vector2.Add(targetDirection, player.Position);

                // Interpolate that shit
                float interpolationSpeed = MathHelper.Lerp(0.1f, 1.0f,
                     MathHelper.Clamp(player.Physics.Velocity.LengthSquared() / maximumVelocitySquared, 0.0f, 1.0f));

                cameraMount.Position = Vector2.SmoothStep(cameraMount.Position, targetPosition, interpolationSpeed);
            }
        }
#3
03/09/2010 (12:59 pm)
Hello friends.

I have tried to use the code created by Bryant, but I have some errors, like:


player=TorqueObjectDatabase.Instance.FindObject<T2DStaticSprite>("player");
var player is null for this condition.

I tryed:

TorqueObjectDatabase.Instance.FindObject<T2DStaticSprite>("Player");
TorqueObjectDatabase.Instance.FindObject<T2DAnimatedSprite>("player");
TorqueObjectDatabase.Instance.FindObject<T2DAnimatedSprite>("Player");

but, for all options I get a null value.

What's wrong?

Tx in advance.

Best regards.
#4
03/09/2010 (1:13 pm)
These snippet code have the same problem:

T2DSceneObject background = TorqueObjectDatabase.Instance.FindObject<T2DSceneObject>("background");

var background is null too.

Regards.
#5
03/09/2010 (2:31 pm)
Is your player object named "Player"? Is your background object named "Background"?
#6
03/09/2010 (2:37 pm)
are those object names assigned in your scenes xml file?

if you open your level in teh builder and select your "player" and goto the edit tab and expand the scripting roll-down where it says name it should say "player" then the FindObject method should return a non-null variable.