分类: C/C++
2008-05-18 13:40:19
// System includes #includeWe now also include 2 files: SDL_mixer.h and SDL.h. We won't use the wrong#include #include #include
// The SDL include files #include "SDL_mixer.h" #include "SDL.h"We must also include CSprite.h and CSpriteBase.h if we want to use the sprite routines and font.h if we want to use the font ones.
// Sprite classes and font #include "CSprite.h" #include "CSpriteBase.h" #include "font.h"As you might have seen from the screenshot above, we'll have some bullets and/or some enemies on the screen. To prevent the screen for being over-crowded we must limit the number of bullets/enemies that can be on the screen at one time. We set this limit at 10. We'll store the number of bullets and enemies as prefenifed constants.
// We set a limit on the number of bullets and enemies that // can be on the screen at a time. #define BULLETS 10 #define ENEMIES 10Now we have some spooky variables that will be used with the frame-rate independent movement. We first have 2 variables: td and td2 (td = time difference). They are used to count how much time one frame lasted. We store the length of one frame in the variable dt (delta time) and if the game isn't paused then we increase sdlgt (short for SDL_GetTicks()) with dt. More on these 4 variables later (when we start using them...).
int td=0,td2=0; // Used when checking the time difference // (how much time has passed since last frame) float dt=0; // The time that has elapsed since // the previous frame float sdlgt=0; // Stores the time that has passedWe now have couple of SDL_Surfaces. screen holds the entire screen that's diplayed, back holds the large image that we scroll in the background, logo stores the logo at the top of the screen, smallship is the image that we use when we show how many lives are left and gameoverimg is the crappy image that you see when you die.
SDL_Surface *screen, *back, *logo, *smallship, *gameoverimg;Next come 2 fonts - a white font and the same font, but in yellow.
SDLFont *font,*yellowfont;// White and yellow fontsNow come the sprites. shipbase stores all the ship graphics and ship is the ship itself. bulletbase stores the bullet's graphic. The array of CSprites, bullets, stores all the individual bullets that you can shoot out of your ship. The array bdraw tells us whether a specific bullet is currently on the screen (1) or not (0).
CSpriteBase shipbase; // Stores the images of the ship CSprite ship; // Stores the ship CSpriteBase bulletbase; // The Bullet sprite's base CSprite bullets[BULLETS]; // Individual bullets int bdraw[BULLETS]; // Shows which elements of the bullets // array are in useAs with the bullets, enemybase stores the graphics of the enemy sprites and the array enemies stores all the individual enemies. edraw tells us whether an enemy is on the screen or not, elife tells us how much life the enemy has left (the amount is defined randomly when the enemy is created) and einfo tells us whether the enemy is blue (and moving up and down, value of einfo is >0 then) or purple (not moving up and down, value of einfo is 0 at that case).
CSpriteBase enemybase; // The enemy sprite's base CSprite enemies[ENEMIES]; // Individual enemies int edraw[ENEMIES]; // Marks active enemies int espeed[ENEMIES]; // Speeds of each enemy int elife[ENEMIES]; // Life of each enemy int einfo[ENEMIES]; // The type of enemy - bouncing or notpaused is 1 when the game is paused and 0 if it isn't. The same is with gameover that tells us whether the game is over or not.
int paused=0; // Is the game paused or not int gameover=0; // Is the game over?Scroll is a simple float that tells us how much of the background image has been scrolled. elast remembers when the last enemy came out. We use it to make a new enemy come out after 750 milliseconds (3/4 seconds). It prevents enemies from popping up too quickly. dtime is similar to elast, but it remembers when you were last brought back to life. We use it to make you immortal for 3 seconds after you die (the blinking ship effect). score and lives should be obvious - score stores your score and lives the number of lives you have left. If lives goes below zero, then you die.
float scroll=0; // How much has the screen scrolled Uint32 elast; // Remembers when the last enemy came out Uint32 dtime; // Remembers when you were resurrected int score=0,lives=5; // The score and the livesThe next 3 variables should be new to you. Mix_Music is the music datatype from SDL_mixer and Mix_Chunk's are simple sound files that we can play when we wish.
Mix_Music *music; // This will store our music Mix_Chunk *shot; // This will store our shot sound Mix_Chunk *explode; // This will store our "explosion" soundAnd that's the end of the global variables. The first function on our list is called Sprite_Collide. It takes references to 2 CSprite objects as parameters and returns 1 if they are colliding or 0 if they aren't. We do the collision detection with reduced rectangles that are 80% of the width and height of the sprites. This basically means that if 2 sprites touch at their outer borders then no collision will occur. We do collision detection with reduced rectangles instead of full rectangles because our sprites aren't fully rectangular (there's empty space at the corners of the sprites). More information on all sorts of sprite collision detections can be found here: http://www.gamedev.net/reference/articles/article735.asp. There's an ascii image there that may make it clearer why we use reduced rectangles instead of full rectangles when checking for collision.
short int Sprite_Collide(CSprite &object1, CSprite &object2) {We store the coordinates of our reduced rectangles in the variables left1, right1, top1 and bottom1 for one sprite and left2, right2, top2 and bottom2 for the second sprite. Then we make them equal the corresponding values from our sprites
// We store the coordinates of our reduced rectangles here double left1, left2; double right1, right2; double top1, top2; double bottom1, bottom2; left1 = object1.getx()+object1.getw()*0.1; left2 = object2.getx()+object2.getw()*0.1; top1 = object1.gety()+object1.geth()*0.1; top2 = object2.gety()+object2.geth()*0.1; right1 = object1.getx()+object1.getw()*0.9; right2 = object2.getx()+object2.getw()*0.9; bottom1 = object1.gety()+object1.geth()*0.9; bottom2 = object2.gety()+object2.geth()*0.9;And finally we do some "magical" comparing and return 1 if the sprites do in fact collide or 0 if they don't. NOTE: you could perfectly replace "bottom1", "top2", "top1", etc with "object1.gety()+object1.geth()*0.9", etc on the next 4 lines. I didn't do it this time because I wanted the code to look clearer... You should do something like this in your own code though.
if (bottom1 < top2) return 0; if (top1 > bottom2) return 0; if (right1 < left2) return 0; if (left1 > right2) return 0; return 1; };Our next 3 functions are common in all the lessons so I won't get into them much this time. ImageLoad loads an image and converts it to the display format for faster blitting and the two DrawIMG's blit an image onto the screen.
// Load in an image and convert it to the display format // for faster blitting. SDL_Surface * ImageLoad(char *file) { SDL_Surface *temp1, *temp2; temp1 = SDL_LoadBMP(file); temp2 = SDL_DisplayFormat(temp1); SDL_FreeSurface(temp1); return temp2; } // Blit an image on the screen surface void DrawIMG(SDL_Surface *img, int x, int y) { SDL_Rect dest; dest.x = x; dest.y = y; SDL_BlitSurface(img, NULL, screen, &dest); } // Blit a portion of an image on the screen surface void DrawIMG(SDL_Surface *img, int x, int y, int w, int h, int x2, int y2) { SDL_Rect dest; dest.x = x; dest.y = y; SDL_Rect dest2; dest2.x = x2; dest2.y = y2; dest2.w = w; dest2.h = h; SDL_BlitSurface(img, &dest2, screen, &dest); }Now we have the function DrawScroll that draw the background on the screen. It's actually quite simple. The first thing that we check is whether the background has scrolled more than it's own width - whether we see the end of one scrolling background on the screen or not. If this is the case then we must draw 2 images on the screen as the background - the start of one scrolling background and the end of an other. We draw 3909-scroll pixels of the first part of the background at position 0,60 and 640-(3909-scroll) pixels of the second part of the background at the spot where the first background blit ended. If the background hasn't scrolled enough so that we could see the end of one background and the start of an other then draw only one image as the background.
// This function draws the scrolling background void DrawScroll() { // We first check, if the background has scrolled more than it's // own width. And if so,then we must draw 2 images on the screen: // the end of one scrolling background and the start of another. if(scroll>3909-640) { DrawIMG(back, 0,60,(int)(3909-scroll),400,(int)scroll,0); DrawIMG(back, (int)(3909-scroll-1),60, (int)(640-(3909-scroll)),400,0,0); // If not then we draw only one background } else { DrawIMG(back, 0,60,640,400,(int)scroll,0); } }The next function, addBullet(), adds a bullet onto the screen at the position where the ship currently is. It is called when we press the space key on the keyboard. In the function we loop through all the possible places where to store the new bullet. If we find an empty spot then we add the bullet there.
// Add a bullet onto the screen void addBullet() { // We loop through all the spots for bullets for(int i=0;iaddEnemy() is similar to addBullet(). It's called every 3/4'th of a second. We first loop through all the spots for enemies. If we find a place where to put a new enemy then we first randomly make the enemy either blue or purple. The blue enemy's image is in frame 1 in the sprite animation so we mark that. All blue enemies move up and down on the screen. We also give the blue enemy some random data to prevent all blue enemies from following the same path on the screen. If we randomly made the enemy purple instead, then mark him purple (frame 0 in the sprite animation) and set einfo to 0 so that the whole world knows that this fella is purple. // Add an enemy onto the screen void addEnemy() { // We loop through all the spots for enemies for(int i=0;iFor safety we stop the enemy from animating preventing it from accidentaly blinking blue/purple all the time. We also give the enemy a random y coordinate to make all enemies start from different places. There are 2 more random things that we need to set - the enemies speed and it's life points. After we do all that we mark the spot saying to draw this enemy. // we stop the animation on the enemy sprite for safety // (so that it wouldn's start blinking blue // and purple all the time) enemies[i].stopAnim(); // We give the enemy a random y coordinate so that each enemy // pops up at a different location... enemies[i].set(640,60+rand()%350); // We also give the enemy random speed and life values espeed[i]=rand()%4+1; elife[i]=rand()%3+1; // fill in the spot edraw[i]=1; return; } } }Now we are nearing the DrawScene() function. We first clear the screen with black. Actually we don't need to clear the entire screen cause the scrolling background already Clears most of it for us - we would only need to clear the score and lives since the logo and credits don't change every frame, but those improvements are for you to implement... Anyway, after we clear the screen, we draw the scrolling background and then the logo at position 0,0.// Draw the scene void DrawScene() { // We first fill the entire screen with black SDL_FillRect(screen,0,0); // Draw the scroll DrawScroll(); // Now we draw some logo on the screen DrawIMG(logo, 0, 0);Now, if we are still alive then draw little icons to the top of the screen representing the number of lives that we have left. The code should be quite understandable.// If we are alive if(lives>0) { // Then draw a small ship for each life that we got for(int i=0;iAfter all that we draw some information: the score and the credits. We draw the word "Score:" in white and the score after it in yellow. We also draw the some info in white and the names after it in yellow. The method I used here, lots of spaces, is actually quite bad. In a real program/game you would calculate where to draw each string, or something. Anyway, don't use the method I'm using... // Draw the score drawString(screen,font,5,37,"Score:"); drawString(screen,yellowfont,50,37,"%d",score); // and credits drawString(screen,font,5,460, "Code by Sprites by\ Music by"); drawString(screen,yellowfont,63,460, "Marius Dann / Jotaroh\ seablue"); drawString(screen,yellowfont,465,460, "http://cone3d.gamedev.net");Now we loop through all the bullets and enemies and if they are drawable, then draw them.// Loop thorough all the spots for bullets for(int i=0;iAfter that we draw our ship and if the game should be over, then we also draw an ugly message saying so. And at the end of this function, we flip the screen. // Draw the main ship ship.draw(); // If the game is over, post a little message if(gameover) { DrawIMG(gameoverimg, 192, 200); } // Filp the screen.. SDL_Flip(screen); }And finally, our main function. We start by initalizing the random number generator and creating a pointer to an array where we would store the state of our keyboard keys...// The main function... int main(int argc, char *argv[]) { // We initalize the random number generator srand(time(NULL)); // We store the state of keys here Uint8* keys;Now, since we're using audio in this program, we must also pass the parameter SDL_INIT_AUDIO to SDL_Init(..) to make everything work fine. In the real life we would actually check if the audio got initalized fine (see lesson 1) so that if it didn't, we could better fulfill the requirements of the folks without sound cards. And then we call a little function that tells our program to call SDL_Quit before exiting no matter what happends...// Initalize the SDL Video, Audio and Joystick subsystems. if(SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO) < 0) { printf("Unable to init SDL: %s\n", SDL_GetError()); exit(1); } atexit(SDL_Quit);Here's a new part - initalizing the audio stream. We do that with the command Mix_OpenAudio(int frequency, Uint16 format, int channels, int chunksize); We want our frequency to be CD audio rate - 44100Hz (44.1KHz). Most games use 22050 though because it requires less CPU on older computers. We want our chunksize to be 2048. The less it is the more hooks will be called, but sound may skip. The larget it is, sound effects may lag behind the action more. You just have to find a happy medium. Maybe 512 or 1024 will be better on your system. So, if the sound is really weird on your computer, adjust the frequency and the chunksize. In a real game you should give the user an option choose the values, if he likes, though... If channels is 2, we'll get 16bit (stereo) audio. If it's 1 then we'll get 8bit (mono) audio. AUDIO_S16SYS means that we get "signed 16-bit samples, in system byte order", whatever that means :).
Check out the SDL_mixer documentation at for more detailed info on Mix_OpenAudio(). If the page is down by any chance, paste the url (except the #SEC9 part) into google and check out the cached version...if(Mix_OpenAudio(44100, AUDIO_S16SYS, 2, 2048) < 0) { printf("Warning: Couldn't set 44100 Hz 16-bit audio\n\ - Reason: %s\n", SDL_GetError()); }Initalizing video should be familiar to you already since we have done that in every lesson so far.// Now we get ourself a 640x480x32 fullscreen surface... screen=SDL_SetVideoMode(640,480,32,SDL_SWSURFACE|SDL_FULLSCREEN); if ( screen == NULL ) { printf("Unable to set 640x480 video: %s\n", SDL_GetError()); exit(1); }Next we load in the music and sound effects. This is done with the Mix_LoadMUS() for the music and Mix_LoadWAV() for the sounds. Altough these functions might be new to you, they are really easy to grasp. But remember now: when you load in music and/or sounds then always remember to free them before exiting.// Load in the music music = Mix_LoadMUS("data/sound/(sblu)moon6.xm"); // And sound effects shot = Mix_LoadWAV("data/sound/shot.wav"); explode = Mix_LoadWAV("data/sound/explode.wav");In the next part of the code we load in all the graphics. We also make the image that we display when the game is over transparent at the pixels where there would normally be the color green.// We also load in all the graphics... back = ImageLoad("data/repeat.bmp"); logo = ImageLoad("data/logo.bmp"); smallship = ImageLoad("data/smallship.bmp"); gameoverimg = SDL_LoadBMP("data/gameover.bmp"); // And make the game over image transparent SDL_SetColorKey(gameoverimg, SDL_SRCCOLORKEY, SDL_MapRGB(gameoverimg->format, 0, 255, 0));Now we initalize the graphics for the sprites and then make all the CSprites in the bullets array look like bullets and the ones in the enemies array look like enemies.// Now we initalize the bases for the sprites. // eg: load in their graphics shipbase.init("data/ship"); bulletbase.init("data/bullet"); enemybase.init("data/enemy"); // We make all the bullets and the enemies use these graphics for(int i=0;iWe must also initalize the ship. For that we make it look like a ship (assign the shipbase to it), set it's position and make it blink. // We also give the ship the correct spritebase ship.init(&shipbase,screen); // We set it's positon at 50,220 ship.set(50,220); // And make it blink ship.setSpeed(1);A few things remaining before the game loop - we hide the cursor and load in the fonts. We actually load in the same font twice, but make it white in one case and yellow in the other. After that we start the main loop.// We also hide the cursor SDL_ShowCursor(0); // Now we load in the fonts - a white one and a yellow one font = initFont("data/font"); yellowfont = initFont("data/font",1,1,0); // Loop while done equals true int done=0; while(done == 0) {Now comes a block of code which we use to get how much time has passed between 2 frames. We make td2 equal the current SDL tick count. At this point, td will hold the SDL tick count that we had at the start of the previous frame. So td2-td (the current time - the previous time) will equal the time that has passed since the previous frame. We'll make the variable dt equal that amount multiplied with 0.1. This gives us a nice thingy: if we move something dt units in some direction every frame, then after 1 second it would have moved 100 units. If we move something 2.5*dt pixels up every frame, then after 1 second it would have moved 250 pixels. After that we make td equal td2 - the tick count at the start of this frame. We do that because we want something so divide from in the next frame.td2=SDL_GetTicks(); dt=((float)(td2-td))*0.1; td=td2;If the game isn't paused, we'll increase the variable sdlgt with the number of milliseconds (dt*10) that have passed since the previous frame. From this moment on, we will not use SDL_GetTicks(); in our program anymore, but the variable sdlgt instead. This is becaused SDL_GetTicks(); returns the amount of time the program has been run, but sdlgt will contain the amount of milliseconds the game has been played. If we would pause the game, we would also stop sdlgt from increasing. So, in conclusion we can use sdlgt to do time based stuff in the game without it realizing that we have paused the game at some point, etc...if(!paused) { sdlgt+=dt*10; }Now comes some common code: the event checking loop. We first check if we should quit or if ESCAPE has been pressed. If so then we make done equal 1 making the loop breake before it's next run.// Check if we have some interesting events... SDL_Event event; while(SDL_PollEvent(&event)) { // If we must quit, then do so if(event.type == SDL_QUIT) { done = 1; } // Has a key been pressed down? if(event.type == SDL_KEYDOWN) { // If it was escape then quit if(event.key.keysym.sym == SDLK_ESCAPE) { done = 1; }If space has been hit and the game isn't paused nor over then we first add a bullet (remember, space makes the player shoot stuff).// If it was space and the game isn't paused nor over if(event.key.keysym.sym == SDLK_SPACE && !paused && !gameover) { // Then add a bullet addBullet();After that we play a shot sound in the 0th mixer channel (the first parameter to the function) using Mix_PlayChannel. By default there are 8 mixer channels, but you can allocate more using Mix_AllocateChannels(16); (That specific example would allocate 16 channels.) Check the SDL_mixer docs for more info (located at ). In each channel only one sound can play at a time. If you start playing some other sound in a channel where some sound is playing at the moment, the first sound will be stopped and the new one will start playing. Since we don't want all the bullets to echo, we play them all in channel 0. The second parameter to the function tells us which sound chunk we want to play. The last 0 that we give to the function tells it that we don't want to loop the sound. The bigger the number, the more the sound will loop. If we would give -1 as the number of times we would want the sound to loop, it would actually loop (unsigned int)(-1) or 4294967295 times. That could be considered almost unlimited though.// And play a corresponding sound Mix_PlayChannel(0,shot,0); }If somebody presses the P key then pause or resume the game.if(event.key.keysym.sym == SDLK_p) { paused=!paused; }If the game is over and ENTER is pressed then reset everything (the bullets, the enemies, the ship's position, score, lives, etc - everything).// If the user pressed ENTER and the game is over then if(event.key.keysym.sym == SDLK_RETURN && gameover) { // Reset all the bullets for(int i=0;iNow we check which keys are being hold down. And if the game isn't paused nor over then // Get the state of the keyboard keys keys = SDL_GetKeyState(NULL); // If the game isn't paused nor over if(!paused && !gameover) {move the ship in that direction of the key(s) that is(are) being hold down 2*dt pixels. We also prevent the ship from going outside the "playfield".// If the keys UP, DOWN, LEFT and RIGHT have been pressed if(keys[SDLK_UP]) { // Move the ship up ship.yadd((int)(-2*dt)); // Prevent the ship from going outside the screen if(ship.gety()<60) ship.yset(60); } if(keys[SDLK_DOWN]) { // Move the ship down ship.yadd((int)(2*dt)); // Prevent the ship from going outside the screen if(ship.gety()>410) ship.yset(410); } if(keys[SDLK_LEFT]) { // Move the ship left ship.xadd((int)(-2*dt)); // Prevent the ship from going outside the screen if(ship.getx()<0) ship.xset(0); } if(keys[SDLK_RIGHT]) { // Move the ship right ship.xadd((int)(2*dt)); // Prevent the ship from going outside the screen if(ship.getx()>570) ship.xset(570); } }Now we check if we aren't playing music at the moment. And if so then play the music once. If the music has run out then with the next run of this loop we start playing it again. Also note that SDL_mixer has a special channel for music that's independent of all these channels you use for sound effects.// If we aren't playing music at the moment if(!Mix_PlayingMusic()) { // then start playing it. Mix_PlayMusic(music, 0); }And here's our main game code. We only run all of this if the game isn't paused and it isn't over.// If the game isn't paused nor over then if(!paused && !gameover) {We loop through all the bullets and if they are visible, move them right 3.5*dt units. If they have moved outside the screen, we'll get rid of them.// Loop through all the bullets for(int i=0;iWe have a similar loop with each enemy. If the enemy is visible then640) { // hide them bdraw[i]=0; } } } // Loop through all the enemies for(int i=0;iWe first move the enemy left (thier speed+1)*0.5*dt units. And if the enemy is blue (moving up/down) we must also adjust it's y coordinate. We'll do this with the cosinus function. We'll make their y coordinate equal the cosinus of their x coordinate + some random stuff (to prevent everyone from following the same path). The cosinus function doesn't like degrees though so we have to multiply the (x coordinate+random stuff) value with pi/180 (0.0174532925) to make it work. The cosinus function will always return some value between -1 and +1. That's too small for us. So we multiply it with 100 to get a value from -100 to +100. We must also add 230 to it to center it onto the screen. So, in conclusion this function makes the y coordinate of blue enemies equal from -100+230 to +100+230 (130 to 330). And since the x coordinate changes all the time, the y coordinate will now create smooth wawey movement. // Move them left (their speed+1)*0.5*dt units enemies[i].xadd(-((float)espeed[i]+1)*dt*0.5); // If the sprites are colored blue then if(einfo[i]>0) { enemies[i].yset( (cos((enemies[i].getx()+einfo[i])*0.0174532925) *100)+230); }If the enemy has moved out of the screen (is no longer visible) then we must get rid of it. Since we have nothing more to check with this enemy, we'll move on to the next enemy (the next run of this enemy-checking for loop).// If the enemy moves out of the screen if(enemies[i].getx()<-50) { // Get rid of it edraw[i]=0; // and then move on to the next enemy continue; }We also check for enemy and bullet collisions here. For that we loop through all the bullets. If the bullet is visible and it collides with the enemy// Check for bullet collisions for(int j=0;jthen we decrease the enemies life. If that resulted in the death of the enemy, we increase the player's score by 10, get rid of the enemy and the bullet and play some spooky explosion sound in the mixer channel 1 (not 0 like with the bullets). We play it in a different channel from the bullets so that we could hear bullets and explosions at the same time. Also know that if you play something in the channel -1, SDL_mixer automatically finds a free channel where to play it. Anyway, if the enemy died, then we stop doing stuff (lots of checks) with this enemy and move on to the next one. // If so then decrease the enemies life elife[i]--; // If the enemy is dead if(elife[i]<1) { // Then don't draw it anymore edraw[i]=0; // Increase the players score by 10 score+=10; // play some spooky sound Mix_PlayChannel(1,explode,0); // Get rid of the bullet bdraw[j]=0; // move on to the next enemy continue;If the enemy didn't completely die then we increase the player's score by one and get rid of the bullet.} else { // If we only injured the enemy then increase // the score by one score+=1; // Get rid of the bullet bdraw[j]=0; } } } }Now we check for enemy/player collisions. If the ship isn't blinking (it has the protective immortality shield that it gets for 3 second after it dies) AND the enemy collided with it (the ship) then decrease the number of lives that we have remaining. If we die then mark the game as over and stop checking the remaining enemies.// Check for player collisions // If the ship is vulnearable to attack - is not blinking // And the enemy collided with the ship then if(ship.getSpeed()==0 && Sprite_Collide(ship,enemies[i])) { // decrease our lives lives--; // if we are dead then if(lives<0) { // mark the game as over gameover=1; // stop moving/checking the enemies, // eg: get out of this loop break; }If we didn't die completely then get rid of the enemy, reset the ship's position, make in blink and mark the time when we died so that we would know how long to make the ship immortal. We'll also play the explosion sound in the mixer channel 1.// Get rid of the enemy edraw[i]=0; // If we didn't completely die then reset our position ship.set(10,245); // Make the ship blink (animate) ship.setSpeed(1); ship.startAnim(); // Mark the time when we died dtime=(Uint32)sdlgt; // Play the spooky explosion sound Mix_PlayChannel(1,explode,0); } } }Now, if if we were resurrected more than 3 seconds ago (sdlgt-dtime is larger than 3000) then we get rid of the ships protective shield (stop it from blinking). We must also show the frame zero in the ship's animation so that we would have a ship on the screen no matter what frame of the animation was visible before.// If 3 seconds have passed since we died/were resurrected if(sdlgt-dtime>3000) { // Stop the ships blinking animation ship.stopAnim(); ship.setSpeed(0); // Show the frame of animation with the ship on it ship.setFrame(0); }We also check if the last enemy came out more than 750 milliseconds ago. If so, we'll release a new enemy and mark the time when it came out.// If 3/4 seconds have passed since the last enemy came out if(sdlgt-elast>750) { // Add a new enemy addEnemy(); // Mark the time when this enemy came out elast=(Uint32)sdlgt; }Now we scroll the background. We increase the varable (float) scroll by dt. If scroll is bigger than the width of the scrolling background (3909) then decrease the variable scroll with the width of the scrolling background.// Scroll the screen by dt units scroll += dt; // If we have scrolled more than the background image then // decrease the scrolled amount by // the width of the background image if(scroll>=3909) scroll-=3909; }Now all that's left for us to do is to draw everything. After that we pause for 1 millisecond to allow the system to catch some breath. If the scrolling is really choppy on your system, try increasing the value a bit or commenting out the line.// Draw the scene DrawScene(); // If the scrolling is really choppy on your system, // try commenting this next line. It allows the system to // catch some breath after each frame. SDL_Delay(1); }When we quit the main loop, we'll fade out all the channels and the music within 1 second using Mix_FadeOutChannel(-1,1000) (-1 means all channels) and Mix_FadeOutMusic(1000). After that we pause for one second so that we would actually hear the music fade.// Fade out all the audio channels in 1 second Mix_FadeOutChannel(-1, 1000); // and the music in 1 second Mix_FadeOutMusic(1000); // wait one second SDL_Delay(1000);And the final thing that we do: we free the music and sound chunks. And return zero after that.// Free the sounds Mix_FreeMusic(music); Mix_FreeChunk(shot); Mix_FreeChunk(explode); return 0; }That's it. I hope you had more much fun reading this tutorial than I had writing it :).
You can download the code for the lesson here: lesson6.zip (~3MB), here: lesson6.tar.gz (~3MB) or here: lesson6.tar.bz2 (~2MB). Pick your favourite :). Know that compiling the code on Visual C++ will give you some errors, but they aren't hard to fix...
Oh, and the credits for the sprites go to Dann / Jotaroh and for the music to seablue. Check out his site at for more of his tunes.