Lab 5 Advanced Blitz Graphics Pre-requisites Read Chapter 7, 8, 9 Objectives Translating ships Detecting collisions Animating Explosions Dealing with multiple enemy fighters Vocabulary adding velocity to move tie bounce vs rollover detecting collision w/beam damaged tie reducing hitpoints explosion many tie fighters—lists music? AI – evasive strategy Discussion and Procedure This lab will bring our Star Wars demonstration program to a close. Next week we will cover 3D Graphics. Today we are going to make the TIE fighters move, respond to laser beams, explode, and multiply into vast swarms of evil intent!! Hang on for the ride, it's all systems go! Part 1. Adding velocity to move the Tie fighter Start with the program StarWars4.bb, which we developed in the last class. This program uses page flipping to speed up the animation, and lets the X-wing fire lasers toward a common intersection point. The X-wing moves with the arrow keys. But we are not really able to attack anything yet, and the Tie fighter does not move. 1. In this step, we're going to add member variables to the ship type to allow us to represent velocity (speed) moving in the x and y directions. We will call these fields vx and vy: 1 Type Ship Field x,y ;the position of the ship Field vx,vy ;the velocity of the ship Field hitpoints ;how many hits left End Type Add 2. When we initialize the Tie fighter we can add the following lines to give the ship an initial random velocity: Global enemy.ship = New ship enemy\x = Rand(0,800) enemy\y = Rand(0,50) enemy\vx = Rand(-3,3) enemy\vy = Rand(1,5) enemy\hitpoints = 3 Add Add This means the Tie ship can be moving left or right (vx of -3 to 3) but will always head down (vy of 1 to 5). You can modify these numbers to change the initial settings of the ships velocity. 3. Now that we have defined the field and stock values into the variables, we can start to modify the ship's position inside the main game loop. The x-position can be modified with the statement x=x+vx and similarly for the y position. If KeyDown(UPKEY) player\y=player\y - 5 EndIf If KeyDown(LEFTKEY) player\x=player\x - 5 EndIf If KeyDown(RIGHTKEY) player\x=player\x + 5 EndIf If KeyDown(DOWNKEY) player\y=player\y + 5 EndIf enemy\x= enemy\x + enemy\vx enemy\y= enemy\y + enemy\vy Add Add 4. Better yet, since the main loop is getting cluttered let's make a function called UpdatePosition( ) and copy all the statements from the above example code block into it: 2 Function UpdatePosition() If KeyDown(UPKEY) … …ALL statements from previous example code block… enemy\y = enemy\y + enemy\vy End Function 5. And put a function call to UpdatePosition( ) at the appropriate place in the main game loop. This is a practice that will serve you well in the future – cleaning up your code using functions. NOTES A) Try modifying the initial random velocity settings of the enemy ship to x of -10 to 10 and y of -5 to 5. Then change to something you prefer. B) Try changing the initial x, y position to random values as well: 0 to 800 in x, 0 to 600 in y Part 2. Bounce vs Rollover Now that you have a moving TIE fighter, you have to deal with what happens when the ship goes off screen. You could just leave it off screen and not worry about it. You could make it "roll over" to the other side of the screen, or you could make it "bounce" off the edge back into the middle of the screen. In this section will explore how to set up rollover verses bounce. The rollover effect simply checks if the ship's position is out of bounds, and resets the coordinate so it appears at the other edge. Games like asteroids use this method to give the illusion of open space yet keeping the main objects engaged onscreen. 6. At the bottom of the UpdatePosition() function, add the lines marked "add" below, which take care of the x-coordinate of the enemy: enemy\x= enemy\x + enemy\vx enemy\y= enemy\y + enemy\vy If enemy\x>800 Then enemy\x=0 EndIf If enemy\x<0 Then enemy\x=800 EndIf ADD ADD ADD ADD ADD ADD NOTES A) Add your own code to handle rollover in the vertical (y) coordinate. B) Better yet, put this code into a function called Rollover() and replace the code in UpdatePosition( ) with a function call to Rollover(). 3 7. To model the BOUNCE effect, the test is similar but instead of resetting the x or y position, we flip the sign on the velecity (x or y, depending on which edge is being crossed). The following code sets up a Bounce( ) funciton to handle the bounce effect on the x-axis. To use this code, comment out any reference or function call to the Rollover code from above. Add this function to the bottom of the file, then put a function call to it inside UpdatePosition() Function Bounce() If enemy\x>800 Or enemy\x<0 enemy\vx = -enemy\vx EndIf End Function Then NOTES A) Notice how the code can be simplified with a combination test of both x>800 or x<0 since the end result—reversing the velocity—is the same. B) Add your own code to handle bouncing in the vertical (y) coordinate. C) Now you can choose—Rollover or Bounce—simply by changing a function call inside UpdatePosition()…another great use of functions. Part 3. Using collision( ) and the damaged TIE image We now have a TIE fighter that bounces or rolls over the edges, and an X-wing in pursuit that can fire its laser beam. To allow them to interact, we need to tell if there is a collision between the laser beam and the center of the TIE image. 8. Inside the Display function, we have already (last week) added code to check if the spacebar is pressed, and draw red lines if it is: If KeyDown(SPACE) Then ; DRAW LASER BEAMS Color 255, 0, 0 ; red = 255, 0, 0 Line player\x-40, player\y-30, player\x-5, player\y-80 Line player\x+35, player\y-30, player\x-5, player\y-80 Oval player\x-7, player\y-80, 5, 5, 1 Color 255,255,255 ; white= 255,255,255 black= 0,0,0 EndIf 9. To see if there is a hit, simply add the marked lines to the if-statement as shown: If KeyDown(SPACE) Then ; DRAW LASER BEAMS Color 255, 0, 0 ; red = 255, 0, 0 Line player\x-40, player\y-30, player\x-5, player\y-80 Line player\x+35, player\y-30, player\x-5, player\y-80 Oval player\x-7, player\y-80, 5, 5, 1 Color 255,255,255 ; white= 255,255,255 black= 0,0,0 4 If Collision(player\x-7, player\y-80, enemy\x, enemy\y) Then ADD Print "A Hit!" ADD Stop ADD EndIf ADD EndIf NOTES A) You can adjust the tolerance on your collision detection by modifying this line in the Collision function: If Distance(x,y,x1,y1) < 10 Then B) The 10 is the size (radius) of the collision region. Making it larger makes it easier to hit the TIE fighter. The above code makes the program stop whenever you hit the TIE fighter. This is good for debugging purposes to check where the laser is when a hit occurs. As for the game, a more dramatic possibility (discovered by Qing Xu) is to switch the TIE fighter image for one showing "damage" in red (like it's glowing red hot). 10. To make this work, you need to load in a new image, damagedTie4.bmp supplied in the Lab5 Directory: Global tieimg=LoadImage("Tie4black.bmp") Global damagedtieimg=LoadImage("damagedTie4.bmp") ADD 11. Then modify your code in the Display( ) function to overlay the damaged TIE image on the original image: If Collision(player\x-7, player\y-80, enemy\x, enemy\y) Then Print "A Hit!" DELETE Stop DELETE DrawImage damagedtieimg, enemy\x, enemy\y ADD EndIf By the way, this is just one way of checking collisions. There is an entire chapter, Chapter 9, that covers many different ways of dealing with collisions between bitmap images. Part 4. Checking hitpoints and the exploding animation The next step is to entertain our destructive impulses by making the TIE fighter explode after we've hit it with the laser a certain number of times. This is going to allow us to explore animation because we will get to make a little film clip on how an explosion should look. It will be crude, and you can certainly modify it with your own artistic talents, but it will be a good start. Since we don't want the enemy ship to explode the first time we headed with the laser, we'll need to use another field in the Ship Type to keep track of how many times we hit the ship. Well, it turns out we already have such a variable. It's called hitpoints. 5 If you look at the code where we're initializing the value for the enemy ship, you'll see we already set the hitpoints field to 3: Global enemy.ship = New ship enemy\x = Rand(0,800) enemy\y = Rand(0,50) enemy\vx = Rand(-3,3) enemy\vy = Rand(1,5) enemy\hitpoints = 3 *** CHANGE NOTHING!! *** 12. What we're going to do is modify the collision detection code and add statements that remove a point from hitpoints every time there is a collision. To blow up the ship, we'll add another if statement to check whether hitpoints has reached zero and respond accordingly. Function Display() …statements not shown… If Collision(player\x-7, player\y-80, enemy\x, enemy\y) Then DrawImage damagedtieimg, enemy\x, enemy\y enemy\hitpoints = enemy\hitpoints – 1 ADD EndIf EndIf If enemy\hitpoints <= 0 Then ADD Print "Boom" ADD Stop ADD EndIf ADD End Function NOTES A) You may find that only 3 hitpoints allows a single hit before the ship goes boom. what can you do to make the enemy ship survive longer? Now for the explosion. In order to get an idea how animation works, try running the demo08-01.bb and demo08-02.bb programs and then look at the staticboy.bmp and animatedboy.bmp Images. What you can see is that an animated bitmap is a sequence of images pact one after another inside a .bmp picture file. The program simply instructs the computer which frame of the sequence to display at any given time. In order to create an exploding TIE animation, we're going to need to work in Microsoft Paint. If you're short on time, just simply use the ExplosionAnim.bmp file provided. Otherwise, you can try to create your own exploding animation using the following instructions. 13. Before we start, right-click on the damagedtie4.bmp file and note its size under Properties/Summary (123 x 89). Do the same for explosionStart.bmp (264 x 148). 6 What we're going to do, is make a panel of images morphing from damaged TIE, to the Full explosion (and maybe a tapering off if you like). However, since the images are of different size, we'll make all the panels the same size as the largest one, in other words, the full explosion. 14. Open the damagedtie4.bmp and explosionStart.bmp files in Paint and tile them so you can see both at the same time. 15. Calculate 264x8 = 2112, the width of the new animation. Stretch the explosion image out to that width, keeping the height at 148. Easier said than done, you have to keep grabbing the lower right corner and stretching it a few pixels at a time. 16. Copy the Explosion image 7 more times, filling up the panes with a total of eight copies. Or just use explosionAnim1.bmp. 17. using all the graphics technique you can muster, copy over reduced images of the explosion, placing them on to the TIE fighters and redrawing the red lines as needed. 18. Save the file as MyExplosionAnim.bmp and modify the statements below accordingly (they refer to explosionAnim.bmp which is provided for you). Now we can modify our program to make use of the animation we just created. 19. First we have to load the animated image sequence right where we load all the other images at the top of the program file: Global dstarimg=LoadImage("DeathTrench.bmp") Global dotfieldimage = CreateImage(LENGTH,HEIGHT) ;Load the animated image of explosion Global explodingtie = LoadAnimImage("explosionAnim.bmp",264,148,0,8) ADD ADD 20. Then, in order to sequence frames during the explosion, we need to add another field to the Ship Type to keep track of which frame we're supposed to show at any given time. Type Ship Field Field Field Field End Type x,y ;the position of the ship vx,vy ;the position of the ship hitpoints ;how many hits left frame ; frame number used to display explosion ADD 21. Then when we create a ship, we will set the frame to 0, meaning we're ready to start the animation at the beginning: Global player.ship = New ship 7 …statements not shown… player\hitpoints = 3 player\frame = 0 ADD Global enemy.ship = New ship …statements not shown… enemy\hitpoints = 10 enemy\frame = 0 ADD The next step is tricky. We need to add statements in the Display function that handle the drawing of the exploding fighter. Specifically, when the hit points drops below zero, it's time to launch the animation. But we have to keep the rest of the game going—if we stopped everything to do the whole animation the game would look funny, everything else would be static while the ship exploded. In reality, we want to slip in the correct image from the eight slides while the game is running, and then make the ship disappear completely. 22. The modification is shown below. What we're doing, is first, drawing the animation frame indicated by the current value of the enemy\frame variable. Then adding one to the frame variable so the next time we see the next frame. Then we have to check if the animation is finished. If the frame variable is 8 ( or > 7), it's done. How do we make the ship disappear? Well, there's a number of possibilities. In this case, we move the ship way off the screen (X position 1000) and we set the horizontal velocity to 0 so it can never come back. And we set the hitpoints to 1 so it never tries to run the animation again: If enemy\hitpoints <= 0 Then Print "Boom" DELETE Stop DELETE DrawImage explodingtie, enemy\x, enemy\y, enemy\frame enemy\frame = enemy\frame + 1 If enemy\frame > 7 Then enemy\x = 1000 enemy\vx=0 enemy\hitpoints=1 EndIf EndIf ADD ADD ADD ADD ADD ADD ADD OK, that should do it. Run the game, try to knock out a TIE fighter, and observe your handiwork. But don't get too relaxed, because here comes… Part 5. The Imperial Fleet—using a List of EnemyShips to represent many TIE fighters 8 OK, so this part may not make a lot of sense. It uses a data structure called a list which is something we cover in advanced C++ (CSIS 10B). The syntax is fairly odd looking as well. Basically, we are going to modify the code to create a set of Tie fighters and wherever we processed one enemy, we are now going to process the set. The first thing we have to do is create a new data type to separate the enemy ships from the player's ship. 23. Simply copy the type definition for the ship Type and paste it below, then change the name to EnemyShip Type Ship Field Field all of the Field Field End Type COPY x,y ;the position of the ship COPY vx,vy ;the position of the shipThen we have to create ships we want hitpoints ;how many hits left COPY frame ; frame number being used to display COPY COPY Type EnemyShip PASTE (Change name) Field x,y ;the position of the ship PASTE Field vx,vy ;the position of the ship PASTE Field hitpoints ;how many hits left PASTE Field frame ; frame number being used to display PASTE End Type PASTE 24. Then, we have to create all the ships we want using a for loop. To do this, we take the code that created the regional enemy ship, and place a for loop around it. Inside the for loop we create a new ship and when we are done, we say next, which repeats the loop as many times as we want to. The loop is controlled by the counter going from 0 to 10 (gives us 11 enemy ships). If you want more, change the number 10 to a largernumber. For counter= 0 To 10 enemy.EnemyShip=New EnemyShip Global enemy.ship = New ship enemy\x = Rand(0,800) enemy\y = Rand(0,50) enemy\vx = Rand(-3,3) enemy\vy = Rand(1,5) enemy\hitpoints = 10 enemy\frame = 0 Next ADD ADD DELETE ADD 9 25. Once we've created our fleet of enemy ships, we have to modify the rest of our code to process the whole set instead of just one. To do this, we have to surround all the enemy processing statements with a for-each-next loop. The display function will look like below. Notice we've added three for loops, one to display the basic TIE image for all the ships, one to check for collisions with the laser beam, and one to run the explosion animation. Use copy-paste to paste the For enemy.EnemyShip = Each EnemyShip Continued going through the code Function Display() ; DrawImage dstarimg, 400,300 TileImage dotfieldimage For enemy.EnemyShip = Each EnemyShip ADD DrawImage tieimg, enemy\x, enemy\y Next ADD …statements not shown… Color 255,255,255 ; white= 255,255,255 black= 0,0,0 For enemy.EnemyShip = Each EnemyShip ADD If Collision(player\x-7, player\y-80, enemy\x, enemy\y) Then DrawImage damagedtieimg, enemy\x, enemy\y enemy\hitpoints = enemy\hitpoints – 1 EndIf Next ADD EndIf For enemy.EnemyShip = Each EnemyShip ADD If enemy\hitpoints <= 0 Then DrawImage explodingtie, enemy\x, enemy\y, enemy\frame enemy\frame = enemy\frame + 1 If enemy\frame > 7 Then enemy\x = 1000 DELETE enemy\vx=0 DELETE enemy\hitpoints=1 DELETE Delete enemy ADD EndIf EndIf Next ADD End Function 26. Continue going through the code adding the same for-each-next statements around all the enemy processing steps. This is how the UpdatePosition function should look: Function UpdatePosition() If KeyDown(UPKEY) …statements not shown… If KeyDown(DOWNKEY) player\y=player\y + 5 10 EndIf For enemy.EnemyShip = Each EnemyShip enemy\x= enemy\x + enemy\vx enemy\y= enemy\y + enemy\vy Next Bounce() ADD ADD End Function 27. This is how the Rollover function should look: Function Rollover() For enemy.EnemyShip = Each EnemyShip ADD …statements not shown… Next ADD End Function 28. And finally, this is how the Bounce function should look Function Bounce() For enemy.EnemyShip = Each EnemyShip …statements not shown… Next End Function ADD ADD You should now see a fleet of enemies, that you can knock out one by one. 29. However, when they are all deleted, you can add a final message using a CheckGameOver() function Function CheckGameOver() For enemy.EnemyShip = Each EnemyShip If enemy <> Null Then Return 0 EndIf Next Return 1 End Function And put a function call in the main game loop: While Not KeyHit(1) …statements not shown… UpdatePosition() If CheckGameOver() = 1 Then Print " Congratulations LUKE!!" ADD ADD 11 Stop EndIf Wend ADD ADD Congratulations! You can now start your X-wing pilot training program. This brings us to the end of the Star Wars demonstration. By now you probably have a number of ideas for modifying the code. For instance, just think about what would be involved in having the enemies fire back at the player. If you like, you can also trying switching back some of the code that we have comments about. For instance, bring back the death star image instead of the star background. The code is still in the program, only commented out. You can also switch from bounce to rollOver and see how that affects the behavior of the game. Game programming is a very challenging field, And you shouldn't feel discouraged if you can't succeed in making changes to these sophisticated programs. The way to go about programming is to start out small and work your way up. If you're interested in game programming, you can take CSIS 10A C++ or CSIS 55 Visual Basic and learn programming very thoroughly, so you can return to Blitz Basic and have a better sense of what to do. Good luck and have fun – next week we tackle 3D Graphics!!! 12