0
votes

tld;dr: Character movement glitches easily, doesn't always seem to register key releases, and is overall poor; any suggestions for improvements (or bug fixes)?

Hello,
I'm (still) working on making a relatively simple platformer game in SFML with C++, and I'm struggling with making smooth movement. My 'map' is composed of a grid of (x,y) pairs, which define the tile that is to be displayed at that point (the x and y represent the co-ordinates of the tile sprite in my level spritesheet).

The problem that I'm having is that even though the movement seems to work, it sometimes behaves in an unintended way, and I don't know how to fix it, or even why it might be happening.

For example, spamming (rapidly pressing) the arrow keys, you can sometimes get the character to slide across the map (and lose control of him) until he hits something. Other times, (usually while coming in at an angle) you can get stuck in the floor, and be unable to move unless you first jump to get unstuck.

As of right now, I'm doing collision detection through the use of 4 points which represent the rectangle that is the character's hitbox, so that might be part of the issue.

(Side note: my key presses also only detect the arrow keys and not WASD ... even though they are in the same if statement)

(main.cpp)(relevant code)

if (event.type == sf::Event::KeyPressed)
            {
                if (event.key.code == sf::Keyboard::D || event.key.code == sf::Keyboard::Right)
                    thePlayer.goingRight = true;
                if (event.key.code == sf::Keyboard::A || event.key.code == sf::Keyboard::Left)
                    thePlayer.goingLeft = true;
                if (event.key.code == sf::Keyboard::W || event.key.code == sf::Keyboard::Up)
                {
                    thePlayer.isJumping = true;
                    thePlayer.goingUp = true;
                }
                if (event.key.code == sf::Keyboard::S || event.key.code == sf::Keyboard::Down)
                    thePlayer.goingDown = true;
            }
            if (event.type ==  sf::Event::KeyReleased)
            {
                if (event.key.code == sf::Keyboard::D || event.key.code == sf::Keyboard::Right)
                    thePlayer.goingRight = false;
                if (event.key.code == sf::Keyboard::A || event.key.code == sf::Keyboard::Left)
                    thePlayer.goingLeft = false;
                if (event.key.code == sf::Keyboard::W || event.key.code == sf::Keyboard::Up)
                    thePlayer.goingUp = false;
                if (event.key.code == sf::Keyboard::Space)
                {
                    if (bulletsfired != true)
                    {
                        bulletsfired = true;
                        if (thePlayer.directionFacing == 0)
                        {
                            bulletsList.push_back(myPlayerNamespace::projectile (bulletTexture, thePlayer.mSprite.getPosition().x, (thePlayer.mSprite.getPosition().y + 20.f), thePlayer.directionFacing, bulletCount));
                        }
                        else
                        {
                            bulletsList.push_back(myPlayerNamespace::projectile (bulletTexture, thePlayer.mSprite.getPosition().x + 25.f, (thePlayer.mSprite.getPosition().y + 20.f), thePlayer.directionFacing, bulletCount));
                        }
                        bulletCount++;
                    }
                }
            }

            thePlayer.updatePlayer();

            for (int i = 0; i < bulletsList.size(); i++)
            {
                bulletsList[i].update();
            }


(player.cpp)(relevant code)

void player::updatePlayer()
{
    if (player::isJumping == true)
        player::goingDown = true;

    playerPosX = player::mSprite.getPosition().x;
    playerPosY = player::mSprite.getPosition().y;
    //Gravity
    if (((map[(playerPosY+48)/32][(playerPosX+5)/32].x) != 0 || (map[(playerPosY+48)/32][(playerPosX+5)/32].y) != 0 ) || ((map[(playerPosY+48)/32][(playerPosX+28)/32].x) != 0 || (map[(playerPosY+48)/32][(playerPosX+28)/32].y) != 0 ))
    {
        player::isJumping = false;
        player::goingDown = false;
    }
    else
    {
        player::isJumping = true;
        player::goingDown = true;
    }

    //Collision top
    if (((map[(playerPosY+6)/32][(playerPosX+5)/32].x) != 0 || (map[(playerPosY+6)/32][(playerPosX+5)/32].y) != 0 ) || ((map[(playerPosY+6)/32][(playerPosX+28)/32].x) != 0 || (map[(playerPosY+6)/32][(playerPosX+28)/32].y) != 0 ))
    {
        player::goingUp = false;
    }

    //Collision right
    if (((map[(playerPosY+46)/32][(playerPosX+28)/32].x) != 0 || (map[(playerPosY+46)/32][(playerPosX+28)/32].y) != 0 ) || ((map[(playerPosY+6)/32][(playerPosX+28)/32].x) != 0 || (map[(playerPosY+6)/32][(playerPosX+28)/32].y) != 0 ))
    {
        if (((map[(playerPosY+46)/32][(playerPosX+28)/32].x) == 4 && (map[(playerPosY+46)/32][(playerPosX+28)/32].y) == 5 ) && ((map[(playerPosY+6)/32][(playerPosX+28)/32].x) == 4 && (map[(playerPosY+6)/32][(playerPosX+28)/32].y) == 5 ))
        {
            std::cout << "Ladder right!";
            player::goingDown = false;
            player::isJumping = false;
        }
        else
        {
            player::goingRight = false;
        }
    }

    //Collision left
    if (((map[(playerPosY+6)/32][(playerPosX+5)/32].x) != 0 || (map[(playerPosY+6)/32][(playerPosX+5)/32].y) != 0 ) || ((map[(playerPosY+46)/32][(playerPosX+5)/32].x) != 0 || (map[(playerPosY+46)/32][(playerPosX+5)/32].y) != 0 ))
    {
        if (((map[(playerPosY+6)/32][(playerPosX+5)/32].x) == 4 && (map[(playerPosY+6)/32][(playerPosX+5)/32].y) == 5 ) && ((map[(playerPosY+46)/32][(playerPosX+5)/32].x) == 4 && (map[(playerPosY+46)/32][(playerPosX+5)/32].y) == 5 ))
        {
            std::cout << "Ladder left!";
            player::goingDown = false;
            player::isJumping = false;
        }
        else
        {
            player::goingLeft = false;
        }
    }



    if (player::goingRight == true)
    {
        player::moveRight(player::playerSpeed);
    }
    if (player::goingLeft == true)
    {
        player::moveLeft(player::playerSpeed);
    }
    if (player::goingDown == true)
    {
        player::moveDown(player::playerFallSpeed);
    }
    if (player::goingUp == true)
    {
        player::moveUp(player::playerJumpSpeed);
    }
    if (player::goingRight == true && player::goingUp == true)
    {
        player::mSprite.move(player::playerSpeed * 0.5 , -player::playerJumpSpeed * 0.5);
    }
    if (player::goingLeft == true && player::goingUp == true)
    {
        player::mSprite.move(-player::playerSpeed * 0.5 , -player::playerJumpSpeed * 0.5);
    }
    if (player::goingRight == true && player::goingDown == true)
    {
        player::mSprite.move(player::playerSpeed * 0.5 , player::playerFallSpeed * 0.5);
    }
    if (player::goingLeft == true && player::goingDown == true)
    {
        player::mSprite.move(-player::playerSpeed * 0.5 , player::playerFallSpeed * 0.5);
    }
}


The tile defined by map[i][j].x = 4, map[i][j].y = 5 is a 'ladder' tile, so I want the player to be able to move through it (and also why it outputs "Ladder /direction/!" to the console).

If it is at all relevant; playerSpeed = 4.f; playerFallSpeed = 5.f; playerJumpSpeed = 10.f;

Finally, thanks for reading through this wall of text! I really appreciate any and all help, since I've been struggling with this for about a week now.

1
The way you handle the movement input seems strange to me. Why are you using that many booleans (goingRight, goingLeft, goingDown...) and what the hell is the difference between isJumping and goingUp? You will never have the situation when both, goingLeft and goingRight will be used, will you? Why don't you have one vector representing movement? It could perfectly (and clearly) handle the situations when player is jumping and going left at the same time! Have a look heresjaustirni
I've just tried implementing vector-based movement, and it works for the most part. However, now my collision detection is completely wrong, and I can't figure out how to fix it. What is happening now is that the character gets 'stuck' to objects - once the collision returns true, I can't figure out how to be able to get it to become false again, essentially, since I can't move the character anymore. Any ideas? Should I post code? Thanks.user4678593
Yes, you should definitely post the code, however you should do it in a separate question.sjaustirni

1 Answers

0
votes

After trying to implement the suggestion from Dundee, I finally got the code to (more or less) work. The key changes were to set the directional speed to 0 in a collision (for example, going right is x positive, so if I collide with a wall on the right, and my player velocity is greater than 0, set it to 0). The other big change was to counteract the fact that sometimes, the player 'fell' through the floor. I believe this occurs since I only check for collisions 60 times a second, so sometimes, the player is able to fall a few pixels lower than it should, so I check if this happened, and if it does, move the player up two pixels. (Granted, the player can fall anywhere between 2 and about 5 pixels more than is intended, but this seems to fix the problem, more or less). The only downside to this solution is there is a visible 'bounce-back'. I will use this to my advantage with animations to hopefully make the game look a little more realistic.

Here is the new code:

(main.cpp)

if (event.type == sf::Event::KeyReleased)
            {
                if (event.key.code == sf::Keyboard::D || event.key.code == sf::Keyboard::Right)
                {
                    thePlayer.playerVelocity.x = 0.f;
                }
                if (event.key.code == sf::Keyboard::A || event.key.code == sf::Keyboard::Left)
                {
                    thePlayer.playerVelocity.x = 0.f;
                }
                if (event.key.code == sf::Keyboard::W || event.key.code == sf::Keyboard::Up)
                {   
                    //Nothing
                }
                if (event.key.code == sf::Keyboard::Space)
                {
                    if (bulletsfired != true)
                    {
                        bulletsfired = true;
                        if (thePlayer.directionFacing == 0)
                        {
                            bulletsList.push_back(myPlayerNamespace::projectile (bulletTexture, thePlayer.mSprite.getPosition().x, (thePlayer.mSprite.getPosition().y + 20.f), thePlayer.directionFacing, bulletCount));
                        }
                        else
                        {
                            bulletsList.push_back(myPlayerNamespace::projectile (bulletTexture, thePlayer.mSprite.getPosition().x + 25.f, (thePlayer.mSprite.getPosition().y + 20.f), thePlayer.directionFacing, bulletCount));
                        }
                        bulletCount++;
                    }
                    thePlayer.playerVelocity.x = 0;
                }
            }

            //Direct input for movement
            if (sf::Keyboard::isKeyPressed(sf::Keyboard::W) || sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
            {
                if (thePlayer.playerVelocity.y >= -thePlayer.playerMaxVelocity.y && thePlayer.isJumping == false)
                {
                    thePlayer.playerVelocity.y -= thePlayer.playerJumpSpeed;
                }   
            }
            if (sf::Keyboard::isKeyPressed(sf::Keyboard::A) || sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
            {
                thePlayer.mSprite.setTextureRect(sf::IntRect(0,0,32,48));
                thePlayer.directionFacing = 0;
                if (thePlayer.playerVelocity.x >= -thePlayer.playerMaxVelocity.x)
                {
                    thePlayer.playerVelocity.x -= thePlayer.playerSpeed;
                }
            }
            if (sf::Keyboard::isKeyPressed(sf::Keyboard::S) || sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
            {
                //Nothing
                //thePlayer.playerVelocity.y += gravity.y;
            }
            if (sf::Keyboard::isKeyPressed(sf::Keyboard::D) || sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
            {
                thePlayer.mSprite.setTextureRect(sf::IntRect(32,0,32,48));
                thePlayer.directionFacing = 1;
                if (thePlayer.playerVelocity.x <= thePlayer.playerMaxVelocity.x)
                {
                    thePlayer.playerVelocity.x += thePlayer.playerSpeed;
                }
            }

            thePlayer.updatePlayer();



(player.cpp)

void player::updatePlayer()
{
    playerPosX = player::mSprite.getPosition().x;
    playerPosY = player::mSprite.getPosition().y;
    //Gravity + Collision bottom
    if ((((map[(playerPosY+48)/32][(playerPosX+5)/32].x) != 0 || (map[(playerPosY+48)/32][(playerPosX+5)/32].y) != 0 ) || ((map[(playerPosY+48)/32][(playerPosX+28)/32].x) != 0 || (map[(playerPosY+48)/32][(playerPosX+28)/32].y) != 0 )) && isJumping==true)
    {
        {
            playerVelocity.y = 0.f;
            isJumping = false;
            if ((((map[(playerPosY+46)/32][(playerPosX+10)/32].x) != 0 || (map[(playerPosY+46)/32][(playerPosX+10)/32].y) != 0 ) || ((map[(playerPosY+46)/32][(playerPosX+20)/32].x) != 0 || (map[(playerPosY+46)/32][(playerPosX+20)/32].y) != 0 )))
            {
                playerPosY -= 2;
                mSprite.setPosition(playerPosX, playerPosY);
                outline.setPosition(playerPosX+8, playerPosY+6);
            }
        }
    }
    else
    {
        if (playerVelocity.y <= playerMaxVelocity.y && isJumping == true)
        {
            playerVelocity.y += gravity.y;
        }
        isJumping = true;
    }

    //Collision top
    if (((map[(playerPosY+6)/32][(playerPosX+5)/32].x) != 0 || (map[(playerPosY+6)/32][(playerPosX+5)/32].y) != 0 ) || ((map[(playerPosY+6)/32][(playerPosX+28)/32].x) != 0 || (map[(playerPosY+6)/32][(playerPosX+28)/32].y) != 0 ))
    {
        if (playerVelocity.y < 0)
        {
            playerVelocity.y = 0;
        }
    }

    //Collision right
    if (((map[(playerPosY+45)/32][(playerPosX+28)/32].x) != 0 || (map[(playerPosY+45)/32][(playerPosX+28)/32].y) != 0 ) || ((map[(playerPosY+6)/32][(playerPosX+28)/32].x) != 0 || (map[(playerPosY+6)/32][(playerPosX+28)/32].y) != 0 ))
    {
        if (((map[(playerPosY+45)/32][(playerPosX+28)/32].x) == 4 && (map[(playerPosY+45)/32][(playerPosX+28)/32].y) == 5 ) && ((map[(playerPosY+6)/32][(playerPosX+28)/32].x) == 4 && (map[(playerPosY+6)/32][(playerPosX+28)/32].y) == 5 ))
        {
            //std::cout << "Ladder right!";
        }
        else
        {
            if (playerVelocity.x > 0)
            {
                playerVelocity.x = 0;
            }
        }
    }

    //Collision left
    if (((map[(playerPosY+6)/32][(playerPosX+5)/32].x) != 0 || (map[(playerPosY+6)/32][(playerPosX+5)/32].y) != 0 ) || ((map[(playerPosY+45)/32][(playerPosX+5)/32].x) != 0 || (map[(playerPosY+45)/32][(playerPosX+5)/32].y) != 0 ))
    {
        if (((map[(playerPosY+6)/32][(playerPosX+5)/32].x) == 4 && (map[(playerPosY+6)/32][(playerPosX+5)/32].y) == 5 ) && ((map[(playerPosY+45)/32][(playerPosX+5)/32].x) == 4 && (map[(playerPosY+46)/32][(playerPosX+5)/32].y) == 5 ))
        {
            //std::cout << "Ladder left!";
        }
        else
        {
            if (playerVelocity.x < 0)
            {
                playerVelocity.x = 0;
            }
        }
    }
    mSprite.move(playerVelocity.x, playerVelocity.y);
    outline.move(playerVelocity.x, playerVelocity.y);
}



Special thanks to Dundee, for the great idea to use vectors for movement. If I can give you internet points for it somehow, let me know, and I'd be happy to do it! :D