简体   繁体   中英

(SFML) Character movement is unnatural and has strange quirks

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM