Jump to content

  • Log In with Google      Sign In   
  • Create Account

Help with implementing smooth mario movement


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
12 replies to this topic

#1 Canvas   Members   -  Reputation: 243

Like
0Likes
Like

Posted 24 June 2014 - 01:26 PM

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?



Sponsor:

#2 BeerNutts   Crossbones+   -  Reputation: 3018

Like
5Likes
Like

Posted 24 June 2014 - 02:41 PM

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)

#3 Canvas   Members   -  Reputation: 243

Like
0Likes
Like

Posted 24 June 2014 - 03:33 PM

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, 24 June 2014 - 03:38 PM.


#4 Buckeye   Crossbones+   -  Reputation: 6407

Like
0Likes
Like

Posted 24 June 2014 - 06:09 PM

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, 24 June 2014 - 06:11 PM.

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


#5 SimonForsman   Crossbones+   -  Reputation: 6325

Like
0Likes
Like

Posted 24 June 2014 - 11:24 PM

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.

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;
 


Edited by SimonForsman, 24 June 2014 - 11:35 PM.

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!

#6 Álvaro   Crossbones+   -  Reputation: 13936

Like
2Likes
Like

Posted 25 June 2014 - 08:24 AM

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.

#7 BaneTrapper   Members   -  Reputation: 1246

Like
3Likes
Like

Posted 25 June 2014 - 11:27 AM

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, 25 June 2014 - 11:38 AM.

Current projects:
The Wanderer, 2d turn based rpg style game

www.gamedev.net/topic/641117-check-up-the-wanderer/


#8 Canvas   Members   -  Reputation: 243

Like
0Likes
Like

Posted 25 June 2014 - 01:34 PM

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.



#9 Álvaro   Crossbones+   -  Reputation: 13936

Like
0Likes
Like

Posted 25 June 2014 - 02:00 PM

`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

#10 Canvas   Members   -  Reputation: 243

Like
0Likes
Like

Posted 25 June 2014 - 02:23 PM

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, 25 June 2014 - 02:33 PM.


#11 Álvaro   Crossbones+   -  Reputation: 13936

Like
2Likes
Like

Posted 25 June 2014 - 02:33 PM

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

#12 BeerNutts   Crossbones+   -  Reputation: 3018

Like
0Likes
Like

Posted 25 June 2014 - 05:00 PM

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.
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)

#13 Orymus3   Crossbones+   -  Reputation: 10706

Like
0Likes
Like

Posted 26 June 2014 - 09:32 PM

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:

 

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...






Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS