Megarunner 6 - Collision and Score

Posted December 9, 2019

NOTE:
This tutorial is most likely not compatible with versions of SGDK above 1.70. Unfortunately, I simply do not have the capacity or ability to update them right now. Read more about it here. Sorry.

Hold up!

If you like 90s platformers, check out my new game Kid Bubblegum!

We have obstacles and the player can jump over them, but it doesn’t really matter…you don’t get rewarded when you do and you don’t get punished when you don’t. So let’s add a scoring system and collision to the obstacles so that there’s actual motivation to do things!

Collision

First we’ll take care of the collision detection. Go down into your main game loop and add the following if-statement after the code that moves the obstacle across the screen:

if(player_x < obstacle_x + 8 && player_x + 8 > obstacle_x){
    //Do things
}

This checks whether the player is touching the obstacle. As you can see we’re only checking for the horizontal position. Why? Well, the player sprite will stay on the ground for most of the game and will thus be at the same height as the obstacle by default. Checking the vertical position would be useful when the player is currently in the air, but we actually don’t have to be that precise. The obstacle sprites are small, so the player sprite will clear them very quickly once it jumps. Comparing the y-positions of the player and obstacle would be a bit overkill, as there are only one or two frames where the player sprite is still low enough during the jump arc to actually hit the obstacle. And it’s nice to give players a bit of leeway, so instead of checking the vertical position, we’ll just check whether the player is in the air or not:

if(player_x < obstacle_x + 8 && player_x + 8 > obstacle_x){
    if(jumping == FALSE){
        endGame();
    }
}

To sum up: If the player sprite intersects with the obstacle and the player is currently not in the air, we end the game by calling endGame() which we’ve defined at the beginning of this series.

And…what if the player is in the air? That’s where scoring comes in!

Score

You know the drill by now: We’ll have to define a few variables to help us out. Here they are, define them near the top of main.c:

int score = 0;
char label_score[6] = "SCORE\0";
char str_score[3] = "0";
bool score_added = FALSE;

The setup is similar to the one we used in Megapong. You have completed that tutorial by now, right? Anyway, score will store our actual score value. label_score will be our score label on the screen and str_score will store our score value as a string (that is, a char[]) so that it can be displayed on screen. score_added will come into play in a bit, I’ll explain what it does when we get to it.

Okay, now we add a quick helper function to make things easier for us:

void updateScoreDisplay(){
	sprintf(str_score,"%d",score);
	VDP_clearText(1,2,3);
	VDP_drawText(str_score,10,2);
}

This function will grab the value from score, convert it to a string stored in str_score, then draw this string to the screen using VDP_drawText. Before it’s drawn we clear any text from the area so that we don’t have any leftover digits flying around.

Before we get into awarding the player points, we’ll have to do a final bit of setting up. Add the following lines to the startGame() function:

VDP_drawText(label_score,10,1);
score = 0;
updateScoreDisplay();
obstacle_x = 320;

The first line draws our score label. Since the label will never change, we only need to draw it once. Next up we set the score value back to 0 (important when we’re restarting the game) and call updateScoreDisplay() to draw it to the screen. And finally we make sure that the obstacle is on the right edge of the screen when the game starts, because otherwise players would get stuck inside when they hit it (because it would just stay where it was when the player crashed into it).

Okay, now we’re getting into the meat of things. Go back to the collision code we just put into our main loop. Then add an else statement with two lines within it, so that the whole thing looks like this:

if(player_x < obstacle_x+8 && player_x + 8 > obstacle_x){
    if(jumping == FALSE){
        endGame();
    } else{
        score++;
        updateScoreDisplay();
    }
    
}

So: When the player sprite crashes into the obstacle while on the ground, we end the game. But if the player sprite is in the air, that means it’s jumping over the obstacle and that deserves a point, wouldn’t you say? So we’re increasing score by one and updating the display so that it’s drawn on screen. Compile and try the game out now!

images/toomuch.gif

As you’ve probably noticed, the game doesn’t just add a single point to the score, but a whole bunch. If you look at the code it’s clear why: A point is added each frame where player_x < obstacle_x+8 && player_x + 8 > obstacle_x equals TRUE, meaning each frame where the player is above the obstacle. That’s not quite what we want, so we’ll have to modify our collision code one more time. Change the else statement to this:

else{
    if(score_added == FALSE){
        score++;
        updateScoreDisplay();
        score_added = TRUE;
    }
}

This is where score_added comes into play. We use this variable to track whether a point has already been added during the current jump. Only if that is not the case do we increase the score. That way players will only receive one point per obstacle.

However, we do have to set score_added back to FALSE somewhere in our code, otherwise players would only ever be able to get a single point. Where in the code would you do that?

And here’s the answer: The best moment to do it is when the player has just landed on the ground. At that point there is no chance of the player flying over an obstacle (since the sprite is on the floor), so it’s safe to set score_added to FALSE. So add a line to the if-statement that checks for ground collision:

if(jumping == TRUE && fix16ToInt(player_y)+player_height > (floor_height)){
    //...
    score_added = FALSE;
}

And there we go! If you try the game you should see that you’re now only getting a single point per obstacle, which is less gratifying but also more consistent.

And…we have a complete game! It has a beginning, gameplay and an ending. It even has scrolling and animations! I hope you’ve enjoyed this tutorial. If you did and want to see more like this, please consider supporting me on Patreon! You’ll get project files for each tutorial step and even an exclusive bonus step for each one. In fact, the bonus step for this one is coming out next week and will teach you how to use tilescrolling to create neat parallax effects!

images/parallax.gif

If you can’t afford Patreon but still want to support me, simply tell others about my work! I have these tutorials but I also develop indie games. Even a single tweet or forum post can make a lot of difference!

Thank you very much for reading, hopefully I’ll see you in the next tutorial!

If you've got problems or questions, join the official SGDK Discord! It's full of people a lot smarter and skilled than me. Of course you're also welcome to just hang out and have fun!

Join my Discord Server!

Hang out, get news, be excellent!

Come hang out!

Want To Buy Me a Coffee?

Coffee rules, and it keeps me going! I'll take beer too, though.

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
  • By using the Disqus service you confirm that you have read and agreed to the privacy policy.

    comments powered by Disqus