Sign in to follow this  
Canvas

Help with implementing smooth mario movement

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 this post


Link to post
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 biggrin.png

 

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 this post


Link to post
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 this post


Link to post
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 biggrin.png

 

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 this post


Link to post
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 this post


Link to post
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 this post


Link to post
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 this post


Link to post
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 this post


Link to post
Share on other sites

Its ok I have it working now biggrin.png

 

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

Share this post


Link to post
Share on other sites
Instead of having `leftSpeed' and `rightSpeed', combine them into a single `horizontalSpeed' (probably set to be `leftSpeed - rightSpeed'), and use that in your logic.

Share this post


Link to post
Share on other sites
What alvaro said. Besides what happens when the user presses both a and d (left and right). With ur code, it'll move right. If u remove the else om the 2nd if, and use speed, the resulting speed will be 0.

Share this post


Link to post
Share on other sites

I generally handle the input as a vector.

Instead of direct cause to effect (movement occurs as you click), I use an additionnal layer:

When I click, I apply force in a direction.

I have a background process that tries to simulate inertia (progressively) by nullifying all vectors over time.

 

So, if I tap a direction, the move begins, and slows down into position.

 

You can see it in action here:

https://www.youtube.com/watch?v=dBukqr9GLbA

 

Notice how my 'direction' doesn't affect my vector and that it dies out of its own accord?

It is possible to apply something similar for your program.

Unfortunately, I am not familiar with the syntax you are using...

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this