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";
``````

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;
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++;
}

}
``````

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!

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{
score++;
}
}
``````

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)){
//...
}

``````

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!

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!

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!

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