Help with implementing smooth mario movement

Started by
11 comments, last by Orymus3 9 years, 10 months ago

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?

Advertisement

I'm guessing you're only getting the "KeyboardEvent" when the repeat key is triggered, which mimics the behavior you've outlined.

Instead, you might just want to check isKeyPressed every update loop. Or, if you must use events, then just set a state with the keyboard pressed event, and then clear that state when the keyboard released event happens.

I'd suggest you just poll every frame, much simpiler, and I guarantee you won't see any decrease in performance.

My Gamedev Journal: 2D Game Making, the Easy Way

---(Old Blog, still has good info): 2dGameMaking
-----
"No one ever posts on that message board; it's too crowded." - Yoga Berra (sorta)

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

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.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

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]

[size="1"]I don't suffer from insanity, I'm enjoying every minute of it.
The voices in my head may not be real, but they have some good ideas!

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.

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

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.

`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

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?

This topic is closed to new replies.

Advertisement