Advertisement
Anaryl

question

Apr 2nd, 2014
108
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.40 KB | None | 0 0
  1. Skip to main content
  2. Page pathMy home /
  3. Game Programming with C++ 2014 /
  4. /
  5. Exercise - Creating Space Invaders Part 3
  6.  
  7. Introduction and Objective
  8.  
  9. The goal of this tutorial is to introduce you to making use of functions and structures within the context of a C++ program. Functions are used to break our program down in to more manageable sections, a function can be used to neatly wrap up a section of code that can then be used to perform a specific task.
  10.  
  11. Structures are used to break our application down into more manageable components, not just a slap-dash collection of variables, structures are our first steps along the road to Object Oriented Programming (OOP).
  12.  
  13. This tutorial assumes that you have completed and fully implemented the features outlined in the previous Space Invaders tutorials.
  14.  
  15.  
  16.  
  17. Process
  18.  
  19.  
  20.  
  21. Ok so now that we have a simple version of space invaders coded up, where we can navigate from a menu screen into our gameplay state and back again, we can now begin to implement some functions to get our game playing a little bit more like a game and looking less like a few space invaders screen grabs.
  22.  
  23. First let's add some functions to our program that will allow us to break it down into the two major sections that have so far, processing the main menu state, and processing the game state.
  24.  
  25. To do this let's add some function declarations above our main function, we have to make sure we declare our functions before we attempt to call them. If we don't typically the compiler will tell us that we're trying to invoke or call a method that it hasn't been notified of, this is a good example of a compile time error.
  26.  
  27. So let's add the following code just above out main() function.
  28. void UpdateMainMenu();
  29. void UpdateGameState();
  30.  
  31.  
  32. All we have done here is make a function declaration, this is akin to telling C++ that we are going to use some functions that have the above syntax somewhere later on in our program.
  33.  
  34. We're now going to flesh these functions out so that the code that was in out switch statement inside our main function now resides in these two functions, we are also going to alter our main functions switch statement to call these two functions, which one will depend upon which state we are currently in.
  35.  
  36. UpdateMainMenu()
  37. void UpdateMainMenu()
  38. {
  39. DrawSprite(iArcadeMarquee);
  40. DrawString( pkInsertCoins, iScreenWidth * 0.37f, iScreenHeight * 0.5f , 0.75f);
  41. DrawString( pkCredits, iScreenWidth * 0.25f, iScreenHeight * 0.4f , 0.7f);
  42.  
  43. }
  44.  
  45.  
  46. Note: I have a variable called iArcadeMarquee which is simple just a sprite I have drawn on my menu to make it look more like the original arcade you can find a large version of the Arcade game's marquee here.
  47.  
  48. UpdateGame()
  49. void UpdateGameState()
  50. {
  51. //\==================================================================================
  52. //\ Player movement handling code
  53. //\==================================================================================
  54. if( IsKeyDown( 'A' ) )
  55. {
  56. fPlayerX -= 2.f;
  57. if( fPlayerX < (fPlayerWidth*.5f) )
  58. {
  59. fPlayerX = (fPlayerWidth*.5f);
  60. }
  61. }
  62.  
  63. if( IsKeyDown( 'D' ) )
  64. {
  65. fPlayerX += 2.f;
  66. if( fPlayerX > (iScreenWidth - (fPlayerWidth*.5f)) )
  67. {
  68. fPlayerX = iScreenWidth - (fPlayerWidth*.5f);
  69. }
  70. }
  71. MoveSprite( iPlayerCannon, fPlayerX, fPlayerY );
  72. DrawSprite( iPlayerCannon );
  73.  
  74. for( int i = 0; i < 15; ++i )
  75. {
  76. DrawSprite(alienShips[i]);
  77. }
  78.  
  79. DrawLine( 0, 40, iScreenWidth, 40, SColour( 0x00, 0xFC, 0x00, 0xFF ));
  80.  
  81. SetFont( pInvadersFont );
  82. DrawString( pkScore1, iScreenWidth * 0.025f, iScreenHeight - 2 );
  83. DrawString( pkHiScore, iScreenWidth * 0.425f, iScreenHeight - 2 );
  84. DrawString( pkScore2, iScreenWidth * 0.8f, iScreenHeight - 2 );
  85. DrawString( pkCredit, iScreenWidth * 0.7f, 38 );
  86.  
  87.  
  88. }
  89.  
  90.  
  91. main( int argc, char* argv[] )
  92. unsigned int iArcadeMarquee;
  93. //Player global variables
  94. unsigned int iPlayerCannon ;
  95. float fPlayerX;
  96. float fPlayerY;;
  97. float fPlayerWidth = 64.f;
  98. float fPlayerHeight = 32.f;
  99. unsigned int alienShips[15];
  100.  
  101. int main( int argc, char* argv[] )
  102. {
  103. //\ Lets initialise the AIE Framework and give the window it creates an appropriate title
  104. Initialise( iScreenWidth, iScreenHeight, false, "Space Invaders" );
  105. SetBackgroundColour( SColour( 0x00, 0x00, 0x00, 0xFF ) );
  106. AddFont( pInvadersFont );
  107.  
  108. iArcadeMarquee = CreateSprite( "./images/Space-Invaders-Marquee.png", iScreenWidth, iScreenHeight, true );
  109. MoveSprite( iArcadeMarquee, iScreenWidth * 0.5f, iScreenHeight * 0.5f );
  110. //\Now lets create the sprite for our players cannon. That's right in space invaders we control a cannon
  111. //\So lets create that with an appropriate variable name and move it to a suitable location (say the middle of our screen)
  112. iPlayerCannon = CreateSprite( "./images/cannon.png", fPlayerWidth, fPlayerHeight, true );
  113. fPlayerX = iScreenWidth * 0.5f;
  114. fPlayerY = 80.f;
  115. MoveSprite( iPlayerCannon, fPlayerX, fPlayerY );
  116.  
  117. float fEnemyX = iScreenWidth * 0.2f;
  118. float fEnemyY = iScreenHeight * 0.7f;
  119. for( int i = 0; i < 15; ++i )
  120. {
  121. alienShips[i] = CreateSprite( "./images/invaders/invaders_1_00.png", fPlayerWidth, fPlayerHeight, true );
  122. MoveSprite( alienShips[i], fEnemyX, fEnemyY );
  123. fEnemyX += 0.12 * iScreenWidth;
  124. if( fEnemyX > iScreenWidth * 0.8f)
  125. {
  126. fEnemyX = iScreenWidth * 0.2f;;
  127. fEnemyY -= 0.08 * iScreenHeight;
  128. }
  129.  
  130. }
  131.  
  132. GameStates eCurrentState = eMAIN_MENU;
  133. do
  134. {
  135. ClearScreen();
  136. float fDeltaT = GetDeltaTime();
  137. switch (eCurrentState )
  138. {
  139. case eMAIN_MENU:
  140. UpdateMainMenu();
  141. if( IsKeyDown( KEY_ENTER ) )
  142. {
  143. eCurrentState = eGAMEPLAY;
  144. }
  145. break;
  146. case eGAMEPLAY:
  147. UpdateGameState();
  148. if( IsKeyDown( KEY_ESCAPE ) )
  149. {
  150. eCurrentState = eMAIN_MENU;
  151. }
  152. break;
  153. default:
  154. break;
  155. }
  156. } while ( FrameworkUpdate() == false );
  157.  
  158. DestroySprite(iPlayerCannon);
  159. DestroySprite(iArcadeMarquee);
  160. Shutdown();
  161.  
  162. return 0;
  163. }
  164.  
  165.  
  166.  
  167. Now if we build an run our Space Invaders game we should get a few errors about variables not being declared etc. Visual Studio should identify these with a red underline in the text editor. We need to go through and declare these variables outside of out main() function. I've already done this in the above code. So if you had copied that then built you would have seen that nothing has changed with the running of the game, however our code is now broken up into three functions main(), updateMainMenu(), and updateGame().
  168.  
  169. Alright so that that we have this in place lets keep on going down this track of adding functions, lets add a function in to create our Space Invader Aliens, Dont forget to add in the function declaration before the main() function, and don't forget that you need to call this function in order to create your enemies.
  170.  
  171. void CreateEnemies()
  172. {
  173. float fEnemyX = iScreenWidth * 0.2f;
  174. float fEnemyY = iScreenHeight * 0.7f;
  175. for( int i = 0; i < 15; ++i )
  176. {
  177. alienShips[i] = CreateSprite( "./images/invaders/invaders_1_00.png", fPlayerWidth, fPlayerHeight, true );
  178. MoveSprite( alienShips[i], fEnemyX, fEnemyY );
  179. fEnemyX += 0.12 * iScreenWidth;
  180. if( fEnemyX > iScreenWidth * 0.8f)
  181. {
  182. fEnemyX = iScreenWidth * 0.2f;;
  183. fEnemyY -= 0.08 * iScreenHeight;
  184. }
  185. }
  186. }
  187.  
  188.  
  189. Ok so now that we have some functions in place now might be a good time to look at implementing a few structures as you'll have no doubt noticed we've got a lot of variables floating around now for player X Position, and Y Position along with all the variables that are going towards making our Enemies and placing them around the screen, a lot of these are now global too. Global is bad, well it's bad in the sense of breaking Object Orientation rules. But apparently as far as online tutorials for OpenGL go global everything is perfectly normal, go figure.
  190.  
  191. Add the following structure to our main.cpp so that we can create and update our players cannon a little bit easier. Remember less global variables is better.
  192. struct PlayerCannon
  193. {
  194. unsigned int iSpriteID;
  195. float fWidth;
  196. float fHeight;
  197. float x;
  198. float y;
  199. };
  200.  
  201. PlayerCannon player;
  202.  
  203.  
  204. What we've done here is wrap our Player Cannon up as a type of small object. As we can see our Players Cannon has an attribute called spriteID, an attribute for it's X and Y positions. There are also some variables associated with our Cannon that will control it's movement. Just underneath our struct we have declared a variable of type 'PlayerCannon' called player that's right we've just created our first user defined type.
  205.  
  206. Now that we have created this structure let's use it in our code an replace the code we had that previously.
  207. //\Now lets create the sprite for our players cannon. That's right in space invaders we control a cannon
  208. //\So lets create that with an appropriate variable name and move it to a suitable location (say the middle of our screen)
  209. player.fHeight = 32.f;
  210. player.fWidth = 64.f;
  211. player.iSpriteID = CreateSprite( "./images/cannon.png", player.fWidth, player.fHeight, true );
  212. player.x = iScreenWidth * 0.5f;
  213. player.y = 80.f;
  214.  
  215.  
  216. Note: I've not shown how to replace the code in the UpdateGameState() function to use this structure, as from the above that should be fairly common sense.
  217.  
  218. We should also perhaps add a function or two into our Player Cannon structure so that we don't just access these variables directly (We'll learn more about why this is bad in an upcoming lesson on classes soon.). That's right structures can also have functions, which they can make use of to conduct operations on their attributes (or variables). So lets get our cannon structure and add the following functions.
  219. struct PlayerCannon
  220. {
  221.  
  222. unsigned int iSpriteID;
  223.  
  224. float fWidth;
  225. float fHeight;
  226. void SetSize( float a_fWidth, float a_fHeight )
  227. {
  228. fWidth = a_fWidth;
  229. fHeight = a_fHeight;
  230. }
  231.  
  232. float x;
  233. float y;
  234. void SetPosition( float a_x, float a_y )
  235. {
  236. x = a_x;
  237. y = a_y;
  238. }
  239.  
  240. unsigned int iMoveLeftKey;
  241. unsigned int iMoveRightKey;
  242. void SetMovementKeys( unsigned int a_moveLeft, unsigned int a_moveRight )
  243. {
  244. iMoveLeftKey = a_moveLeft;
  245. iMoveRightKey = a_moveRight;
  246. }
  247. };
  248.  
  249.  
  250. To access these functions we can make use of the dot operator on our PlayerCannon variable player, for example to set our width and height we could do the following.
  251. player.SetSize( 64.f, 32.f );
  252. player.SetMovementKeys( 'A', 'D' );
  253.  
  254.  
  255. Cool so now we've got a structure in place for our player cannon, we've got a few functions in place for setting up our movement keys, etcetera. What we could do now is set up our player cannon so that it knows how to move itself (to an extent) and ensure that it remains within the screens bounds.
  256.  
  257. So let's modify our Player Cannon structure a little bit more. Add the following into our PlayerCannon structure.
  258. unsigned int iLeftMovementExtreme;
  259. unsigned int iRightMovementExtreme;
  260. void SetMovementExtremes( unsigned int a_leftExtreme, unsigned int a_RightExtreme )
  261. {
  262. iLeftMovementExtreme = a_leftExtreme;
  263. iRightMovementExtreme = a_RightExtreme;
  264. }
  265.  
  266. void Move( float a_fTimeStep, float a_fSpeed )
  267. {
  268. if( IsKeyDown( iMoveLeftKey ) )
  269. {
  270. x -= a_fTimeStep * a_fSpeed;
  271. if( x < (iLeftMovementExtreme + fWidth*.5f) )
  272. {
  273. x = (iLeftMovementExtreme + fWidth*.5f);
  274. }
  275. }
  276. if( IsKeyDown( iMoveRightKey ) )
  277. {
  278. x -= a_fTimeStep * a_fSpeed;
  279. if( x > (iRightMovementExtreme - fWidth*.5f) )
  280. {
  281. x = (iRightMovementExtreme - fWidth*.5f);
  282. }
  283. }
  284. MoveSprite( iSpriteID, x, y );
  285. }
  286.  
  287.  
  288. Ok so we should now have a version of our Space Invaders game that has some functions and a player structure in place of all the global player variables that we previously had. Go ahead and insert these functions in the location we previously had our player movement code, do not forget to set your movement Extreme values.
  289.  
  290. If you are paying attention you will notice that there is a float parameter passed into our Move function called a_fDeltaTime this is the change in time per frame of our game. This is passed in from our main function, so I have also gone ahead an modified our UpdateGameState() function to accept a float parameter so it's declaration and implementation now have the following function signature UpdateGameState( float a_fDeltaTime ). Inside the main() function I am calling an AIE framework function to get the number of seconds that have passed since the last frame. Add the following line just inside the start of our do/while loop in our main() function
  291. float fDeltaT = GetDeltaTime();
  292.  
  293.  
  294. What this means is that our movement code is now no longer limited by the speed of the CPU that our code is running on and will instead move appropriately over a consistent time frame.
  295.  
  296. We should go ahead and create a structure for our Alien Enemies, and add some functionality to make is similar to our player cannon structure, although our move function would want to behave a bit differently.
  297.  
  298. If we look at the game of Space Invaders our enemies should behave in a very identifiable pattern, they move left until the left most unit encounters the edge of the screen or play area, then they all move towards the planet a few pixels then proceed to move right until the rightmost unit encounters the edge of the play area, then they move forward a few more pixels. This pattern then repeats.
  299.  
  300. We should be able to manipulate our Enemy Move function to replicate this behaviour, although there are some considerations that we will need to think about. Our Enemies progress towards the player as a whole, once one enemy encounters the edge of the playing field they should all move in a unified direction forwards then swap to the opposite left/right direction.
  301.  
  302. We can use our Enemy move function to return a boolean value, so that when one enemy crosses the bounds for the playing field our main update loop is notified of this and can then set the appropriate move direction that our enemies should undertake on their next update. Perhaps the following function will help you understand how to implement the Move functionality in your Enemy class.
  303. struct Enemy
  304. {
  305. bool Move( float a_fDeltaTime, int a_Direction )
  306. {
  307. if( a_Direction == eLeft )
  308. {
  309. //Move Left
  310. if( /*Moved too far Left*/ )
  311. {
  312. return true;
  313. }
  314. }
  315. if( a_Direction == eRight )
  316. {
  317. //Move Right
  318. if( /*Moved too far Right*/ )
  319. {
  320. return true;
  321. }
  322. }
  323. if( a_Direction == eDown )
  324. {
  325. //Move towards the planet
  326. }
  327. }
  328. };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement