Simplest way to simulate Mario Jumping?

Started by
6 comments, last by kauna 12 years, 6 months ago
Hello Guys,

I've been trying to find a way to simulate the mario jump, such that I could still slightly control the sprite even while in mid-air due to a jump.
I'm not really sure how to do this.
Particularly, I use C++ and SFML.

I went to the SFML forums to ask this; however, I still can't seem to get it.
One told me to use Vector2f(which i don't really understand how it works)
and others told me to enable user input while in mid-air

I can move the sprite at least, I can even make the sprite jump; however, The computer cant receive any additional input until the jumping of my character is done, disabling me from allowing the user to move the sprite in midair.


A friend told me to simulate gravity and use collision detection to ensure that the sprite doesn't fall off the world and sticks to the floor, but perhaps due to real life stress and frustration on my part, I don't have a clear
train of thought in executing it.


Could anyone help me? I posted my topic here since i thought that the help i could get here theoretically applies to general graphics programming, it's just that i need to know how to maneuver C++ and SFML correctly to execute this.


For the benefit of being on the same page as you guys, I'll put my code below:


#define SFML_STATIC

#include<iostream>
#include<SFML\System.hpp>
#include<SFML\Graphics.hpp>
#include<SFML\Window\Input.hpp>

using namespace std;
using namespace sf;
using namespace sf::Key;

int main()
{

Image Image;


if (!Image.LoadFromFile("ToadR.png"))
return 1;

VideoMode Vmode(800,600,32);
RenderWindow Window(Vmode, "I am Awesome.");

Sprite Sprite;
Sprite.SetImage(Image);
float x = 400.0;
float y = 300.0;
Sprite.SetPosition(x, y);
bool mariojumping = false;
Event Event;


Window.Draw(Sprite);
Window.Display();

while(Window.IsOpened())
{



while(Window.GetEvent(Event))
{


if(Window.GetInput().IsKeyDown(sf::Key::Left))
{

Sprite.Move(-1, 0);
Window.Clear();
Window.Draw(Sprite);
Window.Display();


}
else if(Window.GetInput().IsKeyDown(sf::Key::Right))
{

Sprite.Move(1,0);
Window.Clear();
Window.Draw(Sprite);
Window.Display();

}
else if(Window.GetInput().IsKeyDown(sf::Key::Down))
{

Sprite.Move(0,1);
Window.Clear();
Window.Draw(Sprite);
Window.Display();

}

else if(Window.GetInput().IsKeyDown(Key::Up))
{
Sprite.Move(0,-1);
Window.Clear();
Window.Draw(Sprite);
Window.Display();

for(float y = 0; y <101; y++)
{
Sprite.Move(0,-y);
Window.Clear();
Window.Draw(Sprite);
Window.Display();
}

for(float y = 0; y < 101; y++)
{Sprite.Move(0,y);
Window.Clear();
Window.Draw(Sprite);
Window.Display();
}

}
else if((Window.GetInput().IsKeyDown(sf::Key::Up)) && (Window.GetInput().IsKeyDown(sf::Key::Left)))
{
Sprite.Move(-1.0,1.0);
Window.Clear();
Window.Draw(Sprite);
Window.Display();
}



}


}



return 0;
}






Cheers.

PS: Is it also possible to make my jump sequence slower, such as the sprite stays in mid-air longer? Thanks
Advertisement
As you may have noticed, your logic for moving your sprite is flawed. You are mixing several things (moving, clearing the screen and drawing) under the input functions which is the problem.

Consider all those things separate. ie.

- read and handle input
- move sprites
- draw screen

You'll need to store a position, velocity and acceleration for each sprite. When handling the input, just change the acceleration according to the desired movement direction. This allows your to take gravity in the consideration too. In the moving phase you'll update the sprite velocities according to the acceleration and then move the sprites according to their updated velocities. Since the gravity keeps your sprites falling all the time, you'll need to implement a simple collision detection with scene geometry so that your character doesn't fall through the blocks.

Practically you don't need "mariojumping" variable. The question is to give the character a certain impulse upwards when he wants to jump AND is touching ground.

Finally you may draw all the sprites.

I hope this helps you to understand a simple game loop a bit better.

Cheers!

As you may have noticed, your logic for moving your sprite is flawed. You are mixing several things (moving, clearing the screen and drawing) under the input functions which is the problem.

Consider all those things separate. ie.

- read and handle input
- move sprites
- draw screen

You'll need to store a position, velocity and acceleration for each sprite. When handling the input, just change the acceleration according to the desired movement direction. This allows your to take gravity in the consideration too. In the moving phase you'll update the sprite velocities according to the acceleration and then move the sprites according to their updated velocities. Since the gravity keeps your sprites falling all the time, you'll need to implement a simple collision detection with scene geometry so that your character doesn't fall through the blocks.

Practically you don't need "mariojumping" variable. The question is to give the character a certain impulse upwards when he wants to jump AND is touching ground.

Finally you may draw all the sprites.

I hope this helps you to understand a simple game loop a bit better.

Cheers!


Okay, I revamped my code, here is a segment of it:

Sprite Sprite;
Sprite.SetImage(Image);
float x = 400.0;
float y = 300.0;
Sprite.SetPosition(x, y);
float gravity = -4;
float velocity = 10.0;
float acceleration = 10.0;
Event Event;
bool jumping = false;


Window.Draw(Sprite);
Window.Display();

while(Window.IsOpened())
{
Window.Clear();
Window.Draw(Sprite);
Window.Display();

while(Window.GetEvent(Event))
{


while(jumping == true)
{
Sprite.Move(0, -(velocity + acceleration));
acceleration -= gravity;
velocity += acceleration;
}



if(velocity <= -5)
{
velocity = 10.0;
acceleration = 10.0;
jumping = false;
}








if(Window.GetInput().IsKeyDown(sf::Key::Left))
{

Sprite.Move(-1, 0);



}
else if(Window.GetInput().IsKeyDown(sf::Key::Right))
{

Sprite.Move(1,0);


}
else if(Window.GetInput().IsKeyDown(sf::Key::Down))
{

Sprite.Move(0,1);


}

else if(Window.GetInput().IsKeyDown(Key::Up))
{
Sprite.Move(0,-1);
jumping = true;



}
else if((Window.GetInput().IsKeyDown(sf::Key::Up)) && (Window.GetInput().IsKeyDown(sf::Key::Left)))
{
Sprite.Move(-1.0,1.0);

}



}





}

I still added some mariojumping variable just for my sake. However, now the game seems to freeze when I jump. Also, how do i manage to adjust the gamespeed and the smoothness of the animation?
It's getting cryptic for me here. Cheers! biggrin.gif


Edit: I still use mariojumping since I don't have a floor yet. I just want mario to jump, even if he goes a few pixels lower biggrin.gif
[color="#1C2837"][color="#000000"] [color="#000088"]else [color="#000088"]if[color="#666600"]([color="#660066"]Window[color="#666600"].[color="#660066"]GetInput[color="#666600"]().[color="#660066"]IsKeyDown[color="#666600"]([color="#000000"]sf[color="#666600"]::[color="#660066"]Key[color="#666600"]::[color="#660066"]Right[color="#666600"])) [color="#666600"]{ [color="#660066"]Sprite[color="#666600"].[color="#660066"]Move[color="#666600"]([color="#006666"]1[color="#666600"],[color="#006666"]0[color="#666600"]); [color="#666600"]}


Don't move your sprite while handling input. Just change the acceleration.

Position, velocity and acceleration has to have 2 components (x and y), use a structure to store them.

Split the program in several functions as I mentioned earlier : HandleInput(), MoveSprites(), DrawScreen()




[color="#1C2837"] [color="#000088"]while[color="#666600"]([color="#000000"]jumping [color="#666600"]== [color="#000088"]true[color="#666600"]) [color="#666600"]{ [color="#660066"]Sprite[color="#666600"].[color="#660066"]Move[color="#666600"]([color="#006666"]0[color="#666600"], [color="#666600"]-([color="#000000"]velocity [color="#666600"]+[color="#000000"] acceleration[color="#666600"]));[color="#000000"]
acceleration [color="#666600"]-=[color="#000000"] gravity[color="#666600"];[color="#000000"]
velocity [color="#666600"]+=[color="#000000"] acceleration[color="#666600"]; [color="#666600"]}

Your program freezes because jumping never changes back to false. Plus, you may not use a while loop for the jumping since it will freeze the program from running as long as you are jumping.






use a structure to store the required coordinates



Some pseudocode which may not be physically accurate:



[source]

struct Vector2d
{
float x,y;
};

Vector2d position,velocity, acceleration;

void MoveSprites(float deltatime) //where deltatime is the time between updates
{
//Velocity = acceleration * time


velocity.x += acceleration.x * deltatime;
velocity.y += acceleration.y * deltatime;

position.x += velocity.x * deltatime;
position.y += velocity.y * deltatime;
}

void DrawScreen()
{

Sprite.SetPosition(position.x,position.y);
Window.Clear();
Window.Draw(Sprite);
Window.Display()

}


[/source]

[color="#1C2837"][color="#000000"] [color="#000088"]else [color="#000088"]if[color="#666600"]([color="#660066"]Window[color="#666600"].[color="#660066"]GetInput[color="#666600"]().[color="#660066"]IsKeyDown[color="#666600"]([color="#000000"]sf[color="#666600"]::[color="#660066"]Key[color="#666600"]::[color="#660066"]Right[color="#666600"])) [color="#666600"]{ [color="#660066"]Sprite[color="#666600"].[color="#660066"]Move[color="#666600"]([color="#006666"]1[color="#666600"],[color="#006666"]0[color="#666600"]); [color="#666600"]}


Don't move your sprite while handling input. Just change the acceleration.

Position, velocity and acceleration has to have 2 components (x and y), use a structure to store them.

Split the program in several functions as I mentioned earlier : HandleInput(), MoveSprites(), DrawScreen()




[color="#1C2837"] [color="#000088"]while[color="#666600"]([color="#000000"]jumping [color="#666600"]== [color="#000088"]true[color="#666600"]) [color="#666600"]{ [color="#660066"]Sprite[color="#666600"].[color="#660066"]Move[color="#666600"]([color="#006666"]0[color="#666600"], [color="#666600"]-([color="#000000"]velocity [color="#666600"]+[color="#000000"] acceleration[color="#666600"]));[color="#000000"]
acceleration [color="#666600"]-=[color="#000000"] gravity[color="#666600"];[color="#000000"]
velocity [color="#666600"]+=[color="#000000"] acceleration[color="#666600"]; [color="#666600"]}

Your program freezes because jumping never changes back to false. Plus, you may not use a while loop for the jumping since it will freeze the program from running as long as you are jumping.






use a structure to store the required coordinates



Some pseudocode which may not be physically accurate:



[source]

struct Vector2d
{
float x,y;
};

Vector2d position,velocity, acceleration;

void MoveSprites(float deltatime) //where deltatime is the time between updates
{
//Velocity = acceleration * time


velocity.x += acceleration.x * deltatime;
velocity.y += acceleration.y * deltatime;

position.x += velocity.x * deltatime;
position.y += velocity.y * deltatime;
}

void DrawScreen()
{

Sprite.SetPosition(position.x,position.y);
Window.Clear();
Window.Draw(Sprite);
Window.Display()

}


[/source]


revamped my code again, i defined vectors (in spite of not using a class) and used SetPosition() instead of Move.

Perhaps the final thing to do is to slowdown the animation sequence since when I make my character jump, it just stays in place.

How do I produce a slower animation sequence, or adjust the game speed itself?


Edit: Regarding the gamespeed, I heard that the animation speed of a game upon debug on different computers vary. I think my computer's quite fast in the first place. Is there anyway to have some smooth gamespeed?
You'll need to measure time between frames. You may use QueryPerformanceCounter to calculate the time between frames. Google "QueryPerformanceCounter".

The measured delta time may be used for moving the sprites and advancing the animation in fashion that the program will run more and less smoothly on different computers.

I suggest using classes / structures for storing data. Makes your life much easier.

Cheers!

You'll need to measure time between frames. You may use QueryPerformanceCounter to calculate the time between frames. Google "QueryPerformanceCounter".

The measured delta time may be used for moving the sprites and advancing the animation in fashion that the program will run more and less smoothly on different computers.

I suggest using classes / structures for storing data. Makes your life much easier.

Cheers!


Working on the QueryPerformanceCounter, but would it it be easier to use sf::Clock Clock and then execute the game if the ClockElapsedTime exceeds a certain constant (ie: 0.2 secs) and then reset the clock?

Working also on the class, I just have to make sure everything works biggrin.gif

Cheers!


Edit: Got the jump to work using the Window.SetFrameRate() function. And now it jumps and it can even change its x-position in midair!!!

Only the jump makes the character jump down instead of up xD.

How does SFML get its coordinates? It seems different from the classic Cartesian Plane coordinates
I am not an SFML expert but it seems that

[font=sans-serif][size=2] Clock.GetElapsedTime(); Clock.Reset();[/font]

Should do the trick for calculating the frametime. You may use use

[source]
if(Clock.GetElapsedTime() > 1.0f / EngineFPS){ExecuteLogic();Clock.Reset();};
[/source]

To fix the frame rate. However, that code doesn't take it in account if the elapsed time takes actually several frames and the logic should be executed several times.

The following code should do the trick.


[source]

int Frames = Clock.GetElapsedTime() / (1.0f / EngineFPS);

if(Frames > 0)
{
for(int i=0;i<Frames;++i)ExecuteLogic();
Clock.Reset();
}

[/source]


Traditionally (0,0) is the top left corner of the screen and increasing "y" moves down.

Cheers!

This topic is closed to new replies.

Advertisement