In this tutorial series you will learn how to make a simple single-player Pong-like game for the Sega Mega Drive using SGDK! If you just want to see the full source code, you can get it from Github.

Megapong 8 - Score and HUD

Posted October 7, 2019

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[3] = "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[3] 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 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!

  • 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!
  • 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

    HaxeFlixel Tutorials!

    If you’ve popped over to the tutorial section recently you might have noticed that I’ve added my very first HaxeFlixel tutorial! It shows how to implement a simple, pixel-perfect 2D water shader which I used for Go! Go! PogoGirl. But a few of you might be wondering what a HaxeFlixel is. Well, it’s a 2D game framework that is as powerful as it is underrated! It runs on the (also underrated) Haxe language, is extremely well documented, open source, and has built-in functions for almost anything you’d need.
    Read More

    Streets of Was

    As I’m sure many of you will remember, the original Streets of Rage for the Mega Drive had multiple endings. The real canonical ending has you beat the crap out of Mr. X, thereby ending his reign of terror forever (yeah, right). However, if you confronted Mr. X with a buddy in tow, a new possible path unlocked. A quick refresher is in order. When you confront Mr. X he will ask you to join his organization.
    Read More

    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