Learn how to make an endless runner for the Sega Mega Drive using SGDK! I highly recommend working through the Megapong Tutorial first, as that explains a lot of the basics of MD coding.

Megarunner 5 - Jumping Math

Posted December 2, 2019

Now that we have obstacles, we need to enable the player to jump over them. However, in order to do that we need to use math. Don’t panic, SGDK makes it quite simple. Let’s go!

Fixed Point Math

To make the player jump we’ll have to change his vertical velocity to move him upwards, then apply gravity to pull him back down so that we get a jumping arc. We could do this using variables of type int, but this would lead to very hectic and jerky movement, because there’s just not much precision. So we’ll just use floats, right? That way we can get smoother movement.

Well…yes and no. The thing is, using variables of type float is very expensive on retro hardware such as the Mega Drive. This might seem weird, but if you recall that irrational numbers exist, it makes a bit more sense. So using floats is not the way to go here.

Luckily there is a way to “fake” floats, and even more luckily, SGDK can do it for us! Introducing: Fixed-point arithmetic! Don’t worry: You don’t have to work through the theory and understand how they work. Using them in SGDK is straightforward and we’ll learn as we go. So let’s finally dive in!

Setting Up

First we’ll need to define some variables that we will use. Add the following variable near the top of main.c which will store the vertical velocity of the player:

fix16 player_vel_y = FIX16(0);

And here we’re already getting to some new stuff. As you can see, the variable is of the type fix16, which means it’s a 16bit fixed number variable. Since the player won’t be moving vertically at the beginning, we want to assign 0 to it…but we can’t exactly do that, because 0 would be a regular int. In order to turn it into a fix16 we have to use the helper function FIX16(X), which will turn the value X into a fix16. So even if we only assign a constant (like 0, 6 or -14) we have to convert them to a fix16 first. Keep that in mind!

And while we’re up here we’ll change the player_y variable as well, because otherwise we’d add a fix16 velocity value to an int position value:

fix16 player_y = FIX16(112);

And finally, we’ll add these variables that’ll help us with jumping:

int player_height = 16;
const int floor_height = 128;
fix16 gravity = FIX16(0.2);
bool jumping = FALSE;

The variable player_height stores the height of the player. The constant floor_height will specify how high the floor is. gravity on the other hand needs to be a fix16 as a gravity value of 1 would be too strong, leading to wonky jumping arcs. jumping will keep track of whether the player is currently in the air or not. We’ll be using all of these in a second.

Jumping

Now to implement the jumping mechanic! First, we’ll check if the player has pressed the jump button. Add the following statement to your myJoyHandler callback function:

if (state & BUTTON_C)
{
    if(jumping == FALSE){
        jumping = TRUE;
        player_vel_y = FIX16(-4);
        SPR_setAnim(player,ANIM_JUMP);
    }
}

This is what we’re doing: If C is pressed while the player is on the ground (meaning “not jumping”, meaning jumping == FALSE), we set jumping to TRUE. Then we set the player’s vertical velocity to -4, which will launch him upward. Remember to use FIX16(-4) as player_vel_y is of type fix16! Finally we set the player’s animation to ANIM_JUMP which we’ve defined in the previous step of this tutorial.

Now let’s head to our main game loop and put some code inside the if(game_on == TRUE){...} statement. We need to actually apply the player’s velocity to its position so that the sprite will move. Add this between the scrolling code and the obstacle code:

//Apply velocity
player_y = fix16Add(player_y, player_vel_y);

Here is the next new thing: If you want to add two variables of type fix16, you will have to use the function fix16Add(a,b) which will return the sum of a and b. Just doing player_y + player_vel_y wouldn’t cut it in this case! As you can see, working with fix16 values isn’t exactly harder, you just have more stuff to remember.

Okay, now let’s head to the end of the game loop and tell SGDK where to put our sprite. Put the following line before (or after) SPR_setPosition(obstacle,obstacle_x,120);:

SPR_setPosition(player,player_x,fix16ToInt(player_y));

We’re using yet another fix16 helper function here: fix16ToInt(X). As the name implies, this converts a fix16 value to a regular int. We have to do this here, because SPR_setPosition expects an int value as the Mega Drive obviously can’t render anything smaller than 1 pixel.

But anyway, if you try the game now, the player should jump when you press C…and bing, ZOOM, will go straight to the Moon. Obviously we’ll need some gravity!

Gravity

Let’s add some gravity after we apply the jumping velocity to the player:

//Apply gravity
if(jumping == TRUE) player_vel_y = fix16Add(player_vel_y, gravity);

We only have to worry about gravity when the player is in the air, so we’ll only add it to the player’s vertical velocity while jumping == TRUE. This line will drag the player back down by a value of gravity per frame (which in our case is 0.2).

But of course the player will now jump majestically into the air, only to drop through the floor on his way down and fall eternally. Clearly we need some collision code to make sure the player stops when he hits the ground! So let’s add this big ol’ chunk of code right after the previous lines:

//Check if player is on floor
if(jumping == TRUE && fix16ToInt(player_y)+player_height >= (floor_height)){
    jumping = FALSE;
    player_vel_y = FIX16(0);
    player_y = intToFix16(floor_height-player_height);
    SPR_setAnim(player,ANIM_RUN);
}

Let’s break down the conditional. First we check if the player is currently jumping and thus in the air. Then we check whether the bottom edge of the player sprite (which would be the y-position + its height) is lower than the floor_height we defined previously. We have to use fix16ToInt(X) again here, because player_height is of type int, and you can’t simply add a fix16 and an int without converting one of them first.

Okay, so if the player is currently in the air and has reached the floor, we:

  1. Set jumping to FALSE as the player is no longer jumping
  2. We set player_vel_y to 0 so the player stops falling
  3. We position the player on top of the floor (in case the sprite slipped into it)
  4. We change the animation of the player back to ANIM_RUN.

Phew! That was a lot of stuff, but compile the game now and we should have a working jump. If you want, try playing with the values to make the player jump higher, increase gravity, etc. to see the effects.

images/jumping.gif

Oh and you might notice that the player isn’t positioned correctly at the beginning of the game…his vertical position is off. Maybe I forgot something…? hint hint

I hope this part wasn’t too complex or dry, despite all the math shenanigans. As you can see, using fixed-point math in SGDK isn’t really difficult, just a bit more cumbersome. Next time we’ll actually turn the obstacles into hazards by adding collision and a points system, so that players are rewarded for jumping over them. Thank you for reading and until next time!

If you have any questions, comments or criticism, post them in the comments below or reach out to me on Twitter @ohsat_games! Special thanks to Stephane Dallongeville for creating SGDK and everyone in the SGDK Discord for their help and keeping the dream alive!

Download the source code

All patrons on Patreon get the complete source code for this tutorial, as well as other perks such as early access! Become a Patron!
Just Want to Buy Me a Coffee?

Check out the rest of this tutorial series!

  • Megarunner 1 - The Framework
  • Megarunner 2 - Tiles
  • Megarunner 3 - Scrolling
  • Megarunner 4 - Player and Obstacles
  • Megarunner 5 - Jumping Math
  • Megarunner 6 - Collision and Score
  • Megarunner BONUS - Tile Scrolling
  • Get Words in Your Inbox!

    Be oldschool and sign up for my newsletter to get updates! Just enter your email address, prove you're not part of Skynet and you're good to go!



    Powered by CleverReach. I will not send you spam or sell/give your email address to someone else.  You can of course unsubscribe at any time. By clicking the subscribe button above, you confirm that you have read and agreed to our privacy policy.

    By using the Disqus service you confirm that you have read and agreed to the privacy policy.

    comments powered by Disqus

    Related Posts

    Streets of Rage 2 Design Docs

    A few years ago, Yuzo Koshiro posted a pile of old game design documents for Bare Knuckle 2 aka Streets of Rage 2 on the Ancient blog to commemorate the release of Streets of Rage 2 3D on the Nintendo 3DS. These documents gave a deep insight into the game’s inner workings, technical aspects, designs and even some cut content. They were an awesome resource for one of the most awesome games ever created.

    Read More

    Make a Space Shooter for the Mega Drive!

    It’s time for another SGDK tutorial series! After doing a single player Pong game and an endless runner, it’s time to reach for the stars… and make a space shooter! Apart from things like scrolling and animating sprites, this new series will show you how to deal with multiple entities and their collisions, how to randomly generate backgrounds and more! This project builds upon the previous tutorials, so if you’re new to SGDK programming and have not done those yet, I highly recommend starting with Megapong.
    Read More

    Patreon Revamps

    Hey there, what’s up? Things are continuing to evolve, as I’ve now updated my Patreon to give patrons more perks! Apart from early access to new tutorials and posts, the biggest one is probably the ability to peek behind the scenes…and there will be a lot to peek at in the coming months! This year I’m writing and submitting my MA thesis, meaning that I will have to do more stuff to make ends meet.
    Read More