1
\$\begingroup\$

For developing a side-scrolling platform 2D game I want to implement a moving camera class, the reason of using the class instead of moving the whole map is that I'll have to use too many objects at once witch will cause a lag. I cannot let that happen.

There's a nice algorithm for handling the camera, when player is moving further than the width of the screen then camera moves on players direction until he is once again in the middle of the screen, I've been working several days for making this algorithm work however there's been no success.

// Main
public class Camera
{
    protected float _zoom;
    protected Matrix _transform;
    protected Matrix _inverseTransform;

    //The zoom scalar (1.0f = 100% zoom level)
    public float Zoom
    {
        get { return _zoom; }
        set { _zoom = value; }
    }

    // Camera View Matrix Property
    public Matrix Transform
    {
        get { return _transform; }
        set { _transform = value; }
    }

    // Inverse of the view matrix, 
    // can be used to get 
    // objects screen coordinates
    // from its object coordinates
    public Matrix InverseTransform
    {
        get { return _inverseTransform; }
    }

    public Vector2 Pos;

    // Constructor
    public Camera()
    {
        _zoom = 2.4f;
        Pos = new Vector2(0, 0);
    }
    // Update
    public void Update(GameTime gameTime)
    {

        //Clamp zoom value
        _zoom = MathHelper.Clamp(_zoom, 0.0f, 10.0f);
        //Create view matrix
        _transform =    Matrix.CreateScale(new Vector3(_zoom, _zoom, 1)) *
                        Matrix.CreateTranslation(Pos.X, Pos.Y, 0);
        //Update inverse matrix
        _inverseTransform = Matrix.Invert(_transform);

    }
}

This is the camera class I made for handling the screen, it's main purpose is to resize the screen, more precisely to zoom in and out whenever I want to change my screen, (Title screen, Playing screen, Game over, and like that.) Moving the camera is quite simple with keys, like this.

                    if (keyState.IsKeyDown(Keys.D))
                        Cam.Pos.X -= 20;
                    if (keyState.IsKeyDown(Keys.A))
                        Cam.Pos.X += 20;
                    if (keyState.IsKeyDown(Keys.S))
                        Cam.Pos.Y -= 20;
                    if (keyState.IsKeyDown(Keys.W))
                        Cam.Pos.Y += 20;

And ofc. the drawing method witch apply the camera.

                        spriteBatch.Begin(SpriteSortMode.Texture, BlendState.AlphaBlend, null, null, null, null, Cam.Transform);

Here comes the part when I stop, so what I want to do is make something like 2 2D rooms. By Room I mean the place where I usually place objects. like this "Vector2(74, 63)" So I want to create a place where I could draw items that would stick to the screen and wouldn't move, and make the screen bounds that would make my algorithm to work, witch will be always on screen and as an addition it will check if one of the borders of the screen "room" reaches the certain coordinates of the map "room". I think that the reason for that would be obvious because I don't want player to move camera outside the map when he reaches the wall, otherwise the player would already see a part of the next map where he will be transformed. The reason of drawing both maps next to each other is again to reduce the loading time so player wouldn't have to wait for playing the next map.

So to make it more clear and have an idea how things work around I'll add the player class:

// Main
public class Player
{
    public Texture2D AureliusTexture;
    public Vector2 position;
    public Vector2 velocity;
    protected Vector2 dimensions;
    protected CollisionPath attachedPath;
    const float GRAVITY = 18.0f;
    const float WALK_VELOCITY = 120f;
    const float JUMP_VELOCITY = -425.0f;

    // Constructor
    public Player()
    {
        dimensions = new Vector2(23, 46);
        position = new Vector2(50, 770);
    }

    public void Update(float deltaSeconds, List<CollisionPath> collisionPaths)
    {

        #region Input handling
        KeyboardState keyState = Keyboard.GetState();

        if (keyState.IsKeyDown(Keys.Left))
        {
            velocity.X = -WALK_VELOCITY;
        }
        else if (keyState.IsKeyDown(Keys.Right))
        {
            velocity.X = WALK_VELOCITY;
        }
        else
        {
            velocity.X = 0;
        }



        if (attachedPath != null && keyState.IsKeyDown(Keys.Space))
        {
            velocity.Y = JUMP_VELOCITY;
            attachedPath = null;
        }

        velocity.Y += GRAVITY;

        #endregion

        #region Collision checking
        if (velocity.Y >= 0)
        {
            if (attachedPath != null)
            {
                position.X += velocity.X * deltaSeconds;
                position.Y = attachedPath.InterpolateY(position.X) - dimensions.Y / 2;
                velocity.Y = 0;

                if (position.X < attachedPath.MinimumX || position.X > attachedPath.MaximumX)
                {
                    attachedPath = null;
                }
            }
            else
            {
                Vector2 footPosition = position + new Vector2(0, dimensions.Y / 2);
                Vector2 expectedFootPosition = footPosition + velocity * deltaSeconds;

                CollisionPath landablePath = null;
                float landablePosition = float.MaxValue;


                foreach (CollisionPath path in collisionPaths)
                {
                    if (expectedFootPosition.X >= path.MinimumX && expectedFootPosition.X <= path.MaximumX)
                    {
                        float pathOldY = path.InterpolateY(footPosition.X);
                        float pathNewY = path.InterpolateY(expectedFootPosition.X);

                        if (footPosition.Y <= pathOldY && expectedFootPosition.Y >= pathNewY && pathNewY < landablePosition)
                        {
                            landablePath = path;
                            landablePosition = pathNewY;
                        }
                    }
                }

                if (landablePath != null)
                {
                    velocity.Y = 0;
                    footPosition.Y = landablePosition;
                    attachedPath = landablePath;

                    position.X += velocity.X * deltaSeconds;
                    position.Y = footPosition.Y - dimensions.Y / 2;
                }
                else
                {
                    position = position + velocity * deltaSeconds;
                }
            }
        }
        else
        {
            position += velocity * deltaSeconds;
            attachedPath = null;
        }
        #endregion

    }
}

So I state it clear that I asked my friend to do most of it because I wanted to handle the gravity and the slopes so we made it work similar like in Unity. And he happened to know how to do that. And so I'll add the Update method that handles the camera from the Main Class.

                        MM.Update(gameTime); // Map Managher update function for map handling

                    Cam.Update(gameTime); // Camera update
                    Cam.Zoom = 2.4f; // Sets the zoom level for the title screen
                    // Takes the start position for camera in map and then turns off the update 
                    // so the camera position can be changed. Else it would just keep an infinite
                    // loop and we couldn't change the camera.
                    if (StartInNewRoom)
                    {
                        Cam.Pos = MM.CameraPosition; // Applys the camera position value from the map manager class
                        StartInNewRoom = false;
                    }

I am unsure how to handle the camera, like I used your method and the result often ended up that camera moves by itself or it doesn't move at all.

\$\endgroup\$

Your Answer

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

Browse other questions tagged or ask your own question.