Megapong 8 - Score and HUD

Posted October 7, 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!

It’s the final stretch! We already have the core gameplay, but right now our game is less a game and more of an activity center. We should add a score to make it a bit more challenging and exciting. Let’s do it!

Score and HUD

First we’re going to define the variables that we’ll need. At the beginning of the file, next to all our other variables, add this:

/*Score variables*/
int score = 0;
char label_score[6] = "SCORE\0";
char str_score[4] = "0";

int score will store our current score value. char label_score[6] will be the label of our score HUD element. char str_score[4] will store our current score value as a string.

Now we have everything we need. In main(), add this after loading the tiles:

/*Draw the texts*/
VDP_setTextPlane(BG_A);
VDP_drawText(label_score,1,1);

(Note: In versions of SGDK before 1.5, the function is called VDP_setTextPlan() without the final ‘e’.)

Remember VDP_drawText from the very first installment of this tutorial? It’s back! But before we use it, we first tell SGDK to draw all texts on plane A, because drawing them on the same plane you have tiles on can lead to issues. There is actually a more sophisticated way to draw HUD elements, but this way will suffice for now. Then we draw our score label at position (1,1). Keep in mind that VDP_drawText uses tiles as the unit!

images/scorelabel.png

That draws our label to the screen. Now we need to put our score in there. For that we’ll need some tools, so add the following line to very beginning of main.c:

#include <string.h>

This gives us access to some code that makes dealing with strings easier. And we’ll need a small helper function soon, so let’s just define it right now:

int sign(int x) {
    return (x > 0) - (x < 0);
}

This function returns the sign of int x. I’d recommend putting this definition above all the other functions, so that there won’t be any issues later (remember: In C you can only call functions that have already been defined further up in the code).

Now we’ll create a function that will display our actual score value in the HUD. Add it before main():

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

The function sprintf might look a bit confusing, but it’s doing something rather simple. Basically, it takes the int value of score and puts it into the variable str_score. In very crude terms, it converts an int into a char[] so that we can use it for our HUD. It’s a general C function and not unique to SGDK, so you can find more info elsewhere.

Once str_score has our current score value, we first clear any text that might have been there before. We do this by calling VDP_clearText(1,2,3);. This function takes 3 parameters: The x-coordinate (in tiles), the y-coordinate (in tiles) and the length (also in tiles) we want to clear out. If we didn’t clear the text, all characters would remain on screen until they are replaced – if they are ever replaced.

Finally, we draw our current score at position (1,2). This is one tile below our score label.

We’ll want to call this function once at the beginning, so that the HUD displays a score of 0 when we start the game. Add a call after we’re drawing label_score in main(). For reference, this is all the code we’re using to display the HUD at the start of the game:

VDP_setTextPlane(BG_A);
VDP_drawText(label_score,1,1);
updateScoreDisplay();

Now there are only two more things to do: We need to increase the score when the player hits the ball, then we have to update the HUD by calling updateScoreDisplay(). We’ll do both of these things in moveBall(). Navigate to the if-statements checking for collisions with the player paddle, and put the following two lines in the inner if-statement:

score++;
updateScoreDisplay();			

This way, every time the paddle hits the ball, the score gets increased by 1 and the HUD is automatically updated. Neat!

But while we’re here, let’s add a little spice to the game. Put the following right after the previous code:

if( score % 10 == 0){
	ball_vel_x += sign(ball_vel_x);
	ball_vel_y += sign(ball_vel_y);
}

As you can see, this will increase the velocity of the ball by 1 on every 10th hit (using the sign function we had defined previously). It would be pretty boring if the ball kept its speed all the time! For reference, this is what the whole moveBall() function now looks like:

void moveBall(){
	//Check horizontal bounds
	if(ball_pos_x < LEFT_EDGE){
		ball_pos_x = LEFT_EDGE;
		ball_vel_x = -ball_vel_x;
	} else if(ball_pos_x + ball_width > RIGHT_EDGE){
		ball_pos_x = RIGHT_EDGE - ball_width;
		ball_vel_x = -ball_vel_x;
	}

	//Check vertical bounds
	if(ball_pos_y < TOP_EDGE){
		ball_pos_y = TOP_EDGE;
		ball_vel_y = -ball_vel_y;
	} else if(ball_pos_y + ball_height > BOTTOM_EDGE){
		ball_pos_y = BOTTOM_EDGE - ball_height;
		ball_vel_y = -ball_vel_y;
	}

	//Check for collision with paddle
	if(ball_pos_x < player_pos_x + player_width && ball_pos_x + ball_width > player_pos_x){
		if(ball_pos_y < player_pos_y + player_height && ball_pos_y + ball_height >= player_pos_y){
			//On collision, invert the velocity
			ball_pos_y = player_pos_y - ball_height - 1;
			ball_vel_y = -ball_vel_y;

			//Increase the score and update the HUD
			score++;
			updateScoreDisplay();

			//Make ball faster on every 10th hit
			if( score % 10 == 0){
				ball_vel_x += sign(ball_vel_x);
				ball_vel_y += sign(ball_vel_y);
			}
		}
	}

	//Position the ball
	ball_pos_x += ball_vel_x;
	ball_pos_y += ball_vel_y;

	SPR_setPosition(ball,ball_pos_x,ball_pos_y);	
}

Alright, that was a lot! Now compile the game and give it a spin. Each hit should net you one point, and the difficulty should increase on every 10th hit.

images/scoreget.gif

Things do get difficult rather quickly…or they would, if there was actually a way to fail. You’ll sooner or later notice that the ball will never leave the screen, even when it hits the bottom. This means that the game is infinite. That’s good for marketing, but not fun in actual practice…so we’ll change that in the next installment!

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!

  • Megapong 1 - Hello Mega Drive World!
  • Megapong 2 - Setting Up The Environment
  • Megapong 3 - Importing Resources
  • Megapong 4 - Palettes
  • Megapong 5 - Sprites
  • Megapong 6 - Input and Paddles
  • Megapong 7 - Collisions
  • Megapong 8 - Score and HUD
  • Megapong 9 - Game Over
  • Megapong BONUS - Flashing!
  • By using the Disqus service you confirm that you have read and agreed to the privacy policy.

    comments powered by Disqus