• 10
• 10
• 12
• 12
• 14

# Help with implementing smooth mario movement

This topic is 1400 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hey there people,

I'm creating a very simple Mario clone for myself and i'm trying to create a smooth movement for Mario, I'm using C++ and SFML for this project,

This is the code I have at the moment

void State::KeyboardEvent(sf::Event event)
{
if (state == GameStates::GS_Level1)
{
if (sf::Keyboard::isKeyPressed(sf::Keyboard::D))
{
//Set move right to true
Characters.at(0).UpdateMovement(2, 0);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::A))
{
//Set move left to true
Characters.at(0).UpdateMovement(-2, 0);
}
}
}


This works but not smoothly at all, when the D or A key is pressed down, Mario will move once, then he will continue after about a second delay, it's like holding key down on your keyboard, it will type a letter, but if you keep it held down it will repeat that character, I'm thinking the best way is to maybe set a state, so if A key is pressed set mario state to moving left, and when a is released change the state to stopmoving and once speed is 0 set state to not moving, but i can't really find a good site explaining this and was hoping for some help from your guys,

Any idea on how I should implement this?

##### Share on other sites

Well at the moment BeetNutts I have this has my keyboard event handler

Same as above

void State::KeyboardEvent(sf::Event event)
{
if (state == GameStates::GS_Level1)
{
if (sf::Keyboard::isKeyPressed(sf::Keyboard::D))
{
//Set move right to true
Characters.at(0).moveRight = true;
}
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::A))
{
//Set move left to true
Characters.at(0).moveLeft = true;
}

if (!sf::Keyboard::isKeyPressed(sf::Keyboard::D))
{
Characters.at(0).moveRight = false;
}

if (!sf::Keyboard::isKeyPressed(sf::Keyboard::A))
{
Characters.at(0).moveLeft = false;
}
}
}


Feel free to show code on how to make mine better

and here is my character update method

void CharacterObject::UpdateMovement()
{

if (moveRight == true || moveLeft == true)
{
if (xSpeed < xSpeedMax)
{
xSpeed += xIncrease;
}
}

if (moveRight == true)
{
xPos += xSpeed;
}

if (moveLeft == true)
{
xPos -= xSpeed;
}

if (moveLeft == false && moveRight == false)
{
if (xSpeed > 0)
{
std::cout << "X speed : " << xSpeed << std::endl;
if ((xSpeed - 0.1) > 0)
{
xSpeed -= 0.1;
}
else
{
xSpeed = 0;
}
}
else
{
xSpeed = 0;
}
}
}


At the moment the out come is pretty good, but the problem I am facing now is if I move right and hit my maxspeed, when I then press left and stop pressing right, I need to slow Mario down and then turn him around, but at the moment Mario just turns around and get's full maxspeed, trying to work out how I can deal with this. But also if i dont have right or left down my character stops moving, but the xSpeed isn't 0 yet

Edited by Canvas

##### Share on other sites

I'm not familiar with SMFL, but, according to some googling, SMFL has a function IsKeyDown. Try using that instead of IsKeyPressed. Depending on your loop speed, you may have to reduce the amount you move the character - it should be proportional to the time between render cycles if that's not a constant delta-time.

Edited by Buckeye

##### Share on other sites

Well at the moment BeetNutts I have this has my keyboard event handler

Same as above

void State::KeyboardEvent(sf::Event event)
{
if (state == GameStates::GS_Level1)
{
if (sf::Keyboard::isKeyPressed(sf::Keyboard::D))
{
//Set move right to true
Characters.at(0).moveRight = true;
}
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::A))
{
//Set move left to true
Characters.at(0).moveLeft = true;
}

if (!sf::Keyboard::isKeyPressed(sf::Keyboard::D))
{
Characters.at(0).moveRight = false;
}

if (!sf::Keyboard::isKeyPressed(sf::Keyboard::A))
{
Characters.at(0).moveLeft = false;
}
}
}


Feel free to show code on how to make mine better

and here is my character update method

void CharacterObject::UpdateMovement()
{

if (moveRight == true || moveLeft == true)
{
if (xSpeed < xSpeedMax)
{
xSpeed += xIncrease;
}
}

if (moveRight == true)
{
xPos += xSpeed;
}

if (moveLeft == true)
{
xPos -= xSpeed;
}

if (moveLeft == false && moveRight == false)
{
if (xSpeed > 0)
{
std::cout << "X speed : " << xSpeed << std::endl;
if ((xSpeed - 0.1) > 0)
{
xSpeed -= 0.1;
}
else
{
xSpeed = 0;
}
}
else
{
xSpeed = 0;
}
}
}


At the moment the out come is pretty good, but the problem I am facing now is if I move right and hit my maxspeed, when I then press left and stop pressing right, I need to slow Mario down and then turn him around, but at the moment Mario just turns around and get's full maxspeed, trying to work out how I can deal with this. But also if i dont have right or left down my character stops moving, but the xSpeed isn't 0 yet

instead of having maxspeed go from 0 - max, let it go from -max to max and "reduce" speed if the left key is down, always add xSpeed to your xPos (if xSpeed is negative it will move left, if it is positive it will move right and if it is 0 it won't move)

something like.

[source]

if (moveLeft) {

xSpeed -= xIncrease;

}

if (moveRight) {

xSpeed += xIncrease;

}

if (!(moveLeft || moveRight)) {

if (abs(xSpeed) > 0.1) {

xSpeed -= xSpeed > 0 ? 0.1 : -0.1;

} else {

xSpeed = 0;

}

}

xPos += xSpeed;

[/source]

Edited by SimonForsman

##### Share on other sites

Well at the moment BeetNutts I have this has my keyboard event handler

Same as above

void State::KeyboardEvent(sf::Event event)
{
if (state == GameStates::GS_Level1)
{
if (sf::Keyboard::isKeyPressed(sf::Keyboard::D))
{
//Set move right to true
Characters.at(0).moveRight = true;
}
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::A))
{
//Set move left to true
Characters.at(0).moveLeft = true;
}

if (!sf::Keyboard::isKeyPressed(sf::Keyboard::D))
{
Characters.at(0).moveRight = false;
}

if (!sf::Keyboard::isKeyPressed(sf::Keyboard::A))
{
Characters.at(0).moveLeft = false;
}
}
}


Why does that function get an sf::Event as a parameter? You never use it, which is a sign that there is something wrong. Chances are you are only executing this code in response to a keyboard event, but you should run it in every frame, whether there is a keyboard event or not.

As SimonForsman indicated, you should make your input affect the speed, and the speed affect the position. So the input is basically an acceleration, not a velocity.

I am going to try to write a program using C++ and SFML that allows you to experiment with many parameters to control Mario-style movement. The parameters would be things like:
* acceleration given by horizontal arrow keys while on the ground
* acceleration given by horizontal arrow keys while on the air going upwards
* acceleration given by horizontal arrow keys while on the air going downwards
* maximum horizontal speed when on the ground
* maximum horizontal speed when on the air going upwards
* maximum horizontal speed when on the air going downwards
* horizontal deceleration when no input is given
* gravity
* vertical jump speed (or perhaps jump height would be a more natural parameter to set)
* maximum downward speed

I think it would be a great little tool to play around with when starting to develop a 2d platform game. I have a long plane ride next week, and that might be a good time for me to write this. I'll post the result in this forum when I am done.

##### Share on other sites

Here is a quote from the documentation:

Sometimes, people try to react to KeyPressed events directly to implement smooth movement. Doing so will not produce the expected effect, because when you hold a key you only get a few events (remember, the repeat delay). To achieve smooth movement with events, you must use a boolean that you set on KeyPressed and clear on KeyReleased; you can then move (independently of events) as long as the boolean is set.
The other (easier) solution to produce smooth movement is to use real-time keyboard input with sf::Keyboard (see the dedicated tutorial).

I suggest reading the tutorial first.

http://www.sfml-dev.org/tutorials/2.1/

Edit:: Anyway, in the quote the "Dedicated tutorial is not the one you want, this is what you want to read"

http://www.sfml-dev.org/tutorials/2.1/window-events.php

Edited by BaneTrapper

##### Share on other sites

Thank you for the all the posts guys, I have looked at the my KeyboardEvent method and I have notice I wasn't using the event that is my bad, I have updated my mario to move smoothly now, but the one part I can't get my head around is the new switch statement I'm attempting to implement for my keyboard event

Here is my switch statement so far

		switch (event.type)
{
case sf::Event::KeyPressed:
case sf::Keyboard::D:Characters.at(0).moveRight = true; break;
case sf::Keyboard::A:Characters.at(0).moveLeft = true; break;
break;
case sf::Event::KeyReleased:
case sf::Keyboard::D:Characters.at(0).moveRight = false; break;
case sf::Keyboard::A:Characters.at(0).moveLeft = false; break;
break;
default:
break;
}


I'm getting an error at the second sf::Keyboard::D and sf::Keyboard::A, the error states case label value has already appeared in this switch, how do I get around this? I want to change the a variable if a key is released.

##### Share on other sites
event.type' encodes whether a key has been pressed or whether it has been released (or a bunch of other possible things), but it doesn't encode what key was pressed or released: For that you have to look at event.key'. See http://sfml-dev.org/documentation/2.1/classsf_1_1Event.php#af41fa9ed45c02449030699f671331d4aac3c7abfaa98c73bfe6be0b57df09c71b

##### Share on other sites

Its ok I have it working now

Here is my switch statement

switch (event.type)
{
case sf::Event::KeyPressed:
if (event.key.code == sf::Keyboard::D)
{
Characters.at(0).moveRight = true;
}
else if (event.key.code == sf::Keyboard::A)
{
Characters.at(0).moveLeft = true;
}
break;
case sf::Event::KeyReleased:
if (event.key.code == sf::Keyboard::D)
{
Characters.at(0).moveRight = false;
}
else if (event.key.code == sf::Keyboard::A)
{
Characters.at(0).moveLeft = false;
}
break;
default:
break;
}


Now at the moment i'm trying to swap the sprite around when Mario is moving left or moving right, this is what I have at the moment

if (leftSpeed > 0)
{
sprite.setScale(-1.0, 1.0);
}

if (rightSpeed > 0)
{
sprite.setScale(1.0, 1.0);
}


it works fine when moving right, but if I am moving left and then turn right the sprite will turn straight around to the right side, I have tried to change the if statement to be if else if and then it works the other way around, where left is more dominant then right. How do I fix this?

Edited by Canvas