February 24, 2018, 03:19:59 AM *
Welcome, Guest. Please login or register.

Login with username, password and session length
   Home   Blogs Help Search Tags Login Register  
Pages: [1]   Go Down
Author Topic: ASK Tutorial 09 - Keeping Score  (Read 2288 times)
0 Members and 1 Guest are viewing this topic.
Richard Marks

Respect: 3425
Offline Offline

Posts: 1027


« on: March 31, 2010, 12:16:33 PM »

ASK Tutorial 09 - Keeping Score

Yep! Time for yet another Allegro Starter Kit tutorial! Cool

The concept: Add score keeping and implement a score indicator display using custom vector font rendering.

It is assumed that you are using Visual C++ 2008 Express (MSVC9)
If you're not using MSVC9, then you will need to adjust some steps to suit your IDE.

This tutorial builds off the previous tutorial's code, so don't forget to add the code from the previous tutorial.

Step #1 - Create a new Empty Win32 Project (not a Console Application) in your IDE.

File -> New Project
Choose Win32 Project
Enter "ASKKeepingScoreDemo" for the Name
Click OK
Click Application Settings
Check the Empty Project check-box
Click Finish

Step #2 - Save the Empty Project

File -> Save All

Step #3 - Copy the code files from Tutorial 08 into your project's folder.

Copy all the *.cpp and *.h files from the Tutorial 08 project into the folder that holds the ASKKeepingScoreDemo.vcproj file.

Step #4 - Add the files to the Project

Project -> Add Existing Item
Choose all the .h and .cpp files you just copied and click Add.

Step #5 - Add the code to the PrimaryWindow files to implement the demo concept.

The first thing that we need to do is give the player a score variable in PrimaryWindow.h
int playerScore_;

We're adding a new indicator, so we need to add a function as well.
void RenderScoreIndicator();

Now, we are going to implement our own special score rendering system.
We are going to need a function that can render a single digit as if it were a 7-segment LED electronic component.

The function declaration looks like this:
void RenderDigit(int digit, int x, int y, int width, int height, int color) const;

The parameters are digit, which is expected to be a value from 0 to 9 to be displayed. Any value outside this range will be silently ignored by the function.
The x, and y parameters are the X and Y coordinate for the upper-left corner of the bounding rectangle that holds the digit display.
The width, and height parameters are the dimensions of the digit display.
The color parameter is obviously the color of the digit display.

Exactly how the function works will be explained later.

For now, lets implement the scoring system in PrimaryWindow.cpp

Don't forget to add the new function stubs to PrimaryWindow.cpp

LoadContentForPlayer() gets another initialization line:
// player starts with a score of zero
playerScore_ = 0;

And UpdatePlayerShot() after we determine there is a collision between the shot and the ball, add this code to increase the score.
// increase the player's score
playerScore_ += 25;

You can change the amount of points you receive for killing the ball if you like. I chose 25 because I intend on having more enemy types later, and having them be increasingly harder to kill, and worth more points.

Anyway, that is all. Score keeping has been added..but we don't SEE it yet.
So now we need to implement the score rendering system.

The RenderHeadsUpDisplay() function should call the score indicator after the lives indicator.

Now, to implement the RenderScoreIndicator() function.
I made the decision to only display numerical digits for the score. No "SCORE:" label or anything, as it will be obvious that it is the score indicator.
The reason for this decision is primarily because rendering letters on the 7-segment display is downright difficult and ugly.
I might add that functionality later, or leave it up to you to do so. Grin

Anyway, the score indicator will be just a string of 8 digits (anyone scoring more than that in this game is insane!)
The display will be displayed at the center of the heads up display bar at the bottom of the screen.
Each digit will be 8x14 pixels and have a 2 pixel spacing between each digit.
The score will be displayed in white.
We know that we will have a function that can render a single digit, so we will need to convert our score to a string, and then convert each character in the string into a single digit value that our digit rendering function can use.

The ASCII value of the character of zero is 48 in decimal, we need zero to be a value of zero, so we subtract 48 from 48.
Luckily, the ASCII values of all the numbers 0,1,2,3,4,5,6,7,8,9 are sequential, and so we can subtract 48 from each ASCII value and get a value between 0 and 9 inclusive that our digit rendering function can use.

So, we've got a score integer, lets say it has a value of 125 points.
We want to display this score with all 8 digits, filling the unused digits with zero such as 00000125
To do this, we will create an array of the char data type that is large enough to hold the display.
We then will use the sprintf_s function to print the score formatted to a specified format into the character array.

The format is "%08d" which means we want to fill any unused digits with zero, and that it needs to be 8 digits and that our variable is an integer.

OK, now we have the information we need to start the implementation of RenderScoreIndicator()
First thing we will do is define some constant values so that if we decide to modify anything, its easy.
const int fontWidth = 8;
const int fontHeight = 14;
const int fontSpacing = 2;
const int letterWidth = fontWidth + fontSpacing;
const int numberOfDigitsToDisplay = 8;
const int digitBufferSize = numberOfDigitsToDisplay + 1;
const int indicatorWidth = letterWidth * numberOfDigitsToDisplay;
const int xOffset = (SCREEN_W / 2) - (indicatorWidth / 2);
const int yOffset = (SCREEN_H - 18) + (9 - (fontHeight / 2));
const int scoreColor = makecol(255, 255, 255);

I think that you should be able to understand all of those constants.
Maybe xOffset and yOffset confuse you, but if you think really hard about it, you will get it.
They are going to be used to specify the upper-left corner of the score indicator, and are centering the indicator.

This is a technique that you should memorize.

To center object A on object B horizontally:
A.X = (B.X + (B.Width / 2)) - (A.Width / 2)

To center object A on object B vertically:
A.Y = (B.Y + (B.Height / 2)) - (A.Height / 2)

OK, back to RenderScoreIndicator() now.
Create the character array and print the score into it.
char scoreDigitBuffer[digitBufferSize];
sprintf_s(scoreDigitBuffer, digitBufferSize, "%08d", playerScore_);

Next we iterate over the character array which is now filled with numbers, and we calculate the digit value of each.
Also take note that we use a second integer in the for-loop, xPos which is incremented by the width of a single digit plus the spacing, so that we can render each digit in the correct location.
for (int i = 0, xPos = xOffset; i < numberOfDigitsToDisplay; i++)
int digit = (int)scoreDigitBuffer[i];
this->RenderDigit(digit - 48, xPos, yOffset, fontWidth, fontHeight, scoreColor);
xPos += letterWidth;

And that is the end of that!
Up next we need to implement the RenderDigit() function.
I'm showing you this code, however I really would prefer that you did not use this outside of following this tutorial unless you ask me first. I spent a lot of time designing this function, and I hold the rights to it.
Thank you for complying.

An explanation of the code isn't happening. Sorry.
If you can understand it, great and congratulations, if not, then just use it in this tutorial series as if it were a black-box component.
// A simple 7-segment digital digit rendering routine
// (C) Copyright 2010, Richard Marks
// Use in your own programs requires permission.
const int x2 = x + width;
const int y2 = y + (height / 2);
const int y3 = y + height;
const int vx[] = {x, x2, x2, x2,  x, x};
const int vy[] = {y,  y, y2, y3, y3, y2};
const int invalidDigit = 0xB;
const unsigned char pattern[] =
0x7E, 0x30, 0x6D, 0x79, 0x33,
0x5B, 0x5F, 0x70, 0x7F, 0x73,

if (digit < 0 || digit > 9)
digit = invalidDigit;
if (pattern[digit] & 0x40) { line(_backBuffer, vx[0], vy[0], vx[1], vy[1], color); }
if (pattern[digit] & 0x20) { line(_backBuffer, vx[1], vy[1], vx[2], vy[2], color); }
if (pattern[digit] & 0x10) { line(_backBuffer, vx[2], vy[2], vx[3], vy[3], color); }
if (pattern[digit] & 0x08) { line(_backBuffer, vx[3], vy[3], vx[4], vy[4], color); }
if (pattern[digit] & 0x04) { line(_backBuffer, vx[4], vy[4], vx[5], vy[5], color); }
if (pattern[digit] & 0x02) { line(_backBuffer, vx[5], vy[5], vx[0], vy[0], color); }
if (pattern[digit] & 0x01) { line(_backBuffer, vx[5], vy[5], vx[2], vy[2], color); }

OK, we're done with this tutorial.

When you compile and run the project, you should get:
1 - A window with a black background
2 - The bouncing blue ball from the first tutorial
3 - The green triangle from the second tutorial that you can move left and right using the arrow keys.
4 - A single reloading cyan bullet that you can fire using the space bar.
5 - The ball should disappear if the bullet collides with it.
6 - The ball should re-appear (re-spawn) after roughly 1 second has passed after it dies.
7 - The ball should attack the player when it is moving downwards, and above the Y center line.
8 - Three second delay before you can move the player as it is in a "re-spawning" state.
9 - The player should flash on and off while it is in the re-spawning state.
10 - There is now a heads up display at the bottom of the screen showing the number of lives the player has.
11 - Now the player will die when hit by the ball's shots, and this will cause the player to lose lives.
12 - You should have a nice score display indicator, and the score should increase when you shoot a ball.

Thanks for reading! See you in the next tutorial!
Let me know if you were able to follow this tutorial without any trouble.

(Updated April 2, 2010 - Changed usage of underscores in regards to Redslash's notice.)

Tags: ask code  Allegro tutorial allegro starter kit tutorial c++ 
Pages: [1]   Go Up
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.21 | SMF © 2015, Simple Machines Valid XHTML 1.0! Valid CSS!