分类: C/C++
2008-05-18 13:37:54
Before |
After |
Before |
After |
#includeSince we'll also use the SDL Font routines (tutorial 4), we'll have to include font.h#include #include #include #include #include
#include "font.h"Now come some variables. The first two contain the dimensions of the playfield. They will be set to some values later.
int rows; int cols;screen should be obvious. balls contain the images of the 10 possible ball types. font1 and font2 shouldn't be too much to grasp as well.
SDL_Surface *screen; // The screen surface SDL_Surface *balls[10];// The ball images SDLFont *font1; // 2 fonts SDLFont *font2;playf contains the grid of all the balls. We will initalize it later.
char *playf; // The playfield itselfscrwidth and scrheight contain the default width and height of the screen. Later we'll "parse" the command line arguments checking, if the user wants to run this program at some other screen resolution. Depending on the screen resolution, the grid of balls might and might not fully cover the screen when drawn from the screen coordinates o,o. centx and centy tell us from where to start drawing the array of balls. They will be calculated later.
// The default width and height of the screen int scrwidth=640, scrheight=480; // Used to center the grid nicely onto the screen int centx=0,centy=0;The next variable, bls, tells us how many differently colored balls are there on the screen. bla is used with the mouse. More specifically it's used to see whether a mouse button has been clicked. score should be obvious.
int bls=4; // Number of differently colored balls int bla=0; // Used with the mouse... int score=0; // The current scoreThe function DrawIMG should be clear to all of you by now.
// This function draws a surface onto the screen void DrawIMG(SDL_Surface *img, int x, int y) { SDL_Rect dest; dest.x = x; dest.y = y; SDL_BlitSurface(img, NULL, screen, &dest); }Now the function swap takes references to 2 char variables (the playfield is an array of type char, btw). It then simply swaps them.
// This swaps two type char variables void swap(char &r1, char &r2) { char temp=r1; r1=r2; r2=temp; }The function grid takes the coordinates of the playfield as parameters and returns the value (color of the ball) that's on that specific coordinate. It's used almost everywhere in the rest of the program. The function returns a reference to the char (instead of an instance of the char like we usually do) at the coordinate so that we could manipulate it (swap it with some other, assign a value to it, etc) outside the function.
// Returns the color of a ball in the playfield. inline char &grid(int a, int b) { return playf[a*cols+b]; }The next function, collapse, makes the level collapse. It's called right after we remove a bunch of balls from the grid.
// This function makes balls fall down and rows move left. void collapse() {First we check if we can make any balls fall down. For that we loop through all the columns in the grid. With each column we loop through all the rows. If we find some ball with the value -1, then we remember it's row and move the others onto it. After we move one ball onto the previously marked spot, we decrease the location of the marked spot so that the next ball we move goes onto the top of the spot where we moved our previous ball. After we do this all the balls will be fallen down.
// Make balls fall down int to=-1; for(int j=0;jNow we want to move the balls from right to left. For that we loop through all the columns. If one of those columns should have the bottom cell in the row empty (-1), then we know that the entire column is empty. And if it is empty, we mark the location and move all the other columns onto it. After we move one column, we increase the marked location counter and move our columns there.=0;i--) { if(to==-1 && grid(i,j)==-1) {to=i;} else if(to!=-1 && grid(i,j)!=-1) {swap(grid(i,j),grid(to,j)); to--;} } }
// Move rows to the left to=-1; { for(int j=0;j
After we have done all that we blank out all the leftover columns.{ if(to!=-1) { for(int j=to;jNow the next function recursively clears out a bunch of balls. We use the flood fill algorithm in it. How it works is really simple. We first make the current location (passed as the parameters to this function) in the grid equal -1. We then check all the sides of the current location (top, bottom, left, right). If they equal the color the current location was before, then we call this same function on them. The function runs until all of the connected balls get changed to the value -1. This function also keeps track on the number of balls cleared and returns the cleared number with the return statement at the end. The number of balls cleared is important with the scoring system. You should also know that after we run this function on a specific ball, we call the collapse function (look above for it). // This is a recursive function that clears a bunch of balls. // It uses the flood fill algorithm int pick(int i, int j, int a) { int sum=1; grid(i,j)=-1; if(i != 0 && grid(i-1,j)==a) sum+=pick(i-1,j,a); if(j != 0 && grid(i,j-1)==a) sum+=pick(i,j-1,a); if(iThe next function, bunch, is similar to the previous function. But it only returns 1 when the ball at the given coordinates is connected to any other ball of the same color, or it returns 0, when there aren't any connected balls. // This function checks whether one ball is connected to an other int bunch(int i,int j) { int a = grid(i,j); if(i!=0 && grid(i-1,j) == a) return 1; if(j!=0 && grid(i,j-1) == a) return 1; if(iThe next function, DrawScene is really simple. We first hide the mouse cursor (comment out that line to see why we do that). After that we fill the screen with black. // Draw the screen void DrawScene() { SDL_ShowCursor(0); // Hide the mouse cursor // Clear the entire screen with black SDL_FillRect(screen,NULL,0x000000);Now we loop through the grid and draw all the balls on the screen.// Draw the balls for(int i=0;iThen we draw some informaton, ... // Draw some info and the score drawString(screen,font1,1,scrheight-16, "SPACE restarts, gray + and - change the nr \ of balls (%d). Score: %d",bls,score); // Draw the webspace url to the bottom-right of the screen drawString(screen,font2,scrwidth-stringWidth(font2, "http://cone3d.gamedev.net"),scrheight-16, "http://cone3d.gamedev.net");... flip the screen and show the mouse cursor again.SDL_Flip(screen); // Flip the buffers SDL_ShowCursor(1); // Show the cursor again }The next function, newgame, puts some random balls on the grid of balls over the old ones and also sets the score to zero. After that it calls DrawScene to update the changes.// Resets the entire grid by adding random balls to it void newgame() { // Reset the score score=0; // Fill the grid with random balls for(int i=0;iAnd now the final function, main. We first initalize the random number generator. After that we check if the first command line argument given to this program equals 640, 800, 1024, 1152, 1280 or 1600. If so, we change the screen dimentions to the requested ones. // This is our main function int main(int argc, char *argv[]) { srand(time(NULL)); // We initalize the random number generator // Depending on the command line arguments given to this program, // we change the screen resolution. if(argc>1 && strcmp(argv[1],"640")==0) {scrwidth=640;scrheight=480;} if(argc>1 && strcmp(argv[1],"800")==0) {scrwidth=800;scrheight=600;} if(argc>1 && strcmp(argv[1],"1024")==0) {scrwidth=1024;scrheight=768;} if(argc>1 && strcmp(argv[1],"1152")==0) {scrwidth=1152;scrheight=864;} if(argc>1 && strcmp(argv[1],"1280")==0) {scrwidth=1280;scrheight=960;} if(argc>1 && strcmp(argv[1],"1600")==0) {scrwidth=1600;scrheight=1200;}We then do the usual initalization stuff.// Initalize SDL if ( SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO) < 0 ) { printf("Unable to init SDL: %s\n", SDL_GetError()); exit(1); } atexit(SDL_Quit);Now we set up a random picture of a ball as the icon of the program. So that (almost) every time you run the game, it's icon will be different.// We load in a random ball image as the icon for this program char tempstring[100]; sprintf(tempstring,"data/balls/%d.bmp",rand()%9+1); SDL_WM_SetIcon(SDL_LoadBMP(tempstring),NULL);Now we initalize the screen surface// Initalize the video mode screen=SDL_SetVideoMode(scrwidth,scrheight,32, SDL_SWSURFACE|SDL_HWPALETTE|SDL_FULLSCREEN); if ( screen == NULL ) { printf("Unable to set %dx%d video: %s\n", scrwidth, scrheight, SDL_GetError()); exit(1); }Load in 2 fonts and 10 ball surfaces.// Load in the fonts font1 = initFont("data/font1"); font2 = initFont("data/font2",1,1,0); // Load in all the balls for(int a=1;a<10;a++) { char temp[100]; sprintf(temp,"data/balls/%d.bmp",a); balls[a-1]=SDL_LoadBMP(temp); }Now we see, how many balls can the current screen resolution hold. We simply divide the screen width and height(minus 16 because of the info text) by 45 (the width/height of one ball).// Calculate how much balls fit on the screen with the current // screen resolution rows=(scrheight-16)/45; cols=(scrwidth)/45;And now we calculate how much must we move the balls in order to get the playfield centered on the screen. First we check how much pixels does the grid occupy. We then subtract that value from the width/height of the screen. We then get the amount of free space left over by the playfield. We then divide that value by 2 and get the amount we must move the playfield on the x and y coordinates.// Calculate how much must the balls be moved // so that they would be centered. centx = (scrwidth-cols*45)/2; centy = (scrheight-16-rows*45)/2;We now allocate memory to store the playfield and then make it contain random balls.// Allocate space for the playfield playf = new char[rows*cols]; // Reset the score and generate the playfield newgame();Now comes the game loop.// Loop a bit int done=0; while(done == 0) { SDL_Event event;We check for all sorts of event.while ( SDL_PollEvent(&event) ) { // If someone closes the prorgam, then quit if ( event.type == SDL_QUIT ) { done = 1; }If someone presses ESC, we quit. SPACE restarts the game. Gray + and gray - increase/decrease the number of differently colored balls.if ( event.type == SDL_KEYDOWN ) { // If someone presses ESC, then quit if ( event.key.keysym.sym == SDLK_ESCAPE ) { done = 1; } // Space restarts the game if ( event.key.keysym.sym == SDLK_SPACE ) { newgame(); } // The gray keypad + increases the nr. of different balls if ( event.key.keysym.sym == SDLK_KP_PLUS ) { if(bls<9) {bls++;newgame();} } // The gray keypad - decreases the nr. of different balls if ( event.key.keysym.sym == SDLK_KP_MINUS ) { if(bls>3) {bls--;newgame(); } } } }And now we must check if the user clicked a ball. We first get the coordinates of the mouse. After that we check over which ball the mouse currently is. Because the playfield is centered on the screen, the ball coordinates may be negative. If they are, we restart the loop hoping that on the next run they won't be. If they aren't then we move on.int x,y; // Used to hold the mouse coordinates int x2,y2; // Used to hold the ball coordinates float x3,y3; // Used to temporarily hold the ball coordinates SDL_GetMouseState(&x, &y); // Get the mouse coords x3=(((float)(x-centx))/45); // Get the ball coords y3=(((float)(y-centy))/45); if(x3<0 || y3<0) continue; // If negative, restart the loop else {x2=(int)x3;y2=(int)y3;} // else store them as intsIf at the moment the mouse button is released, then we make bla equal one.// If the mouse button is released, make bla equal 1 if(!SDL_GetMouseState(NULL, NULL)&SDL_BUTTON(1)) { bla=1; }If the mouse button was released the next frame and is held down at the moment then we make bla equal zero. That way we can only reach this if statement the next time the player clicks the mouse button again.// If the mouse button was released the previous frame and // is now held down, then ... if(SDL_GetMouseState(NULL, NULL)&SDL_BUTTON(1) && bla==1) { // Make bla equal zero so that we could get here only // if we release the mouse button and then click it again. bla=0;We now check if the clicked ball coordinates are inside the allowed area and if so, we check if coordinates don't point to an empty ball. And if they don't we check if the ball has other balls connected to it.// Investigate the clicked ball if(x2>=0 && y2>=0 && x2If so we then remove all the connected balls, increase the score by the [number of balls removed-2] squared, make the playfield collapse and redraw the screen. // If it really is clickable then get rid of the balls int a=pick(y2,x2,grid(y2,x2)); // Increase the score by the (removed balls-2)^2 score+=(a-2)*(a-2); // Make the balls fall down collapse(); // And update the screen DrawScene(); } } }And now, when the loop has finished, we clean up a bit.// Let's clean up... freeFont(font1); freeFont(font2); return 0; }Here's the code.