Let's start with the basics. Open the Starfighter project. You have your standard Initialize and OnEnterFrame procedure templates. In Initailize, add Randomize to reset the random number generator and call InitFlareBackground. The rest is left as an exercise:
Excercise 5-7. We need to load in the images of the main ship and enemy ship and store them as memory references. It's best to create a separate unit for this so that we can make the memory references global variables that will be visible to any other source file that uses that unit. Call the unit ImageManager and create it in the Images folder. This means you will need to add Images to the Unit Search Path in Project -> Compiler Options (However, some IDEs will prompt you to add the folder to the path automatically. If this happens, select yes so you do not have to manually edit the search path.) Once you create the unit, declare two variables: MainShipImage and EnemyShipImage of type Pointer (simply a memory reference just like an object). Then make a procedure called LoadImages which you should then call from Initialize in the main source file. In the implementation of LoadImages, you can use the LoadImage(Filename) function from FTGraphics where Filename is 'Images/Ship.bmp' and 'Images/Enemy.bmp' for the two ships.
MainShipImage and EnemyShipImage should be declared in the interface section of the unit so that they will be visible everywhere. A interface definition of LoadImages must also be provided so that it will be visible outside of the unit. LoadImages is implemented in the implementation section.
Now that we have the images loaded, we need to create classes to manage the ships. We have two types of ships: a main ship and an enemy ship. How many classes should we create?
We should probably create 3. We could get away with 2, but it's best to create an abstract base class such as TAbstractShip and then create a main ship class such as TMainShip and an enemy ship class such as TEnemyShip. Each class should be created in a different unit and all 3 units should be saved in a folder called Ships. You'll have to create this folder and add it to the path.
Let's start by creating the abstract base class TAbstractShip. Think about what fields it should have (e.g., ships can move). Also think about what methods we need (e.g., drawing methods).
The unit should look something like this and be saved in the Ships folder (which you will have to create). Note that we declare StepAndDraw as an abstract method because drawing will be different for the main ship versus the enemy ship.
Create the TMainShip class. When you implement StepAndDraw, you can use the FTGraphics procedure DrawTransparentImage(X,Y,ImageRef,TransparentColor) where X and Y are the coordinates of the top left corner of the image, ImageRef is the Pointer to the image and TransparentColor is the background color of the image which should be made transparent when drawn (in the case of the ship images it is clWhite). It's best to center the image on the X and Y coordinates. To do this you will need the Width and Height of the image. GetBitmapExtent(ImageRef,Width,Height) will return the Width and Height of the image in the last two arguments which are passed by reference.
The unit should look something like this and be saved in the Ships folder. TMainShip descends from TAbstractShip so AbstractShipClass will have to be included in the uses clause. StepAndDraw will have to be overriden and then implemented. In order to reference MainShipImage you will also need to add ImageManager to the uses clause.
Back in the main project source file, create a variable called MainShip of type TMainShip. Create an instance of TMainShip in Initialize and then call StepAndDraw during OnEnterFrame. Set the ship's initial position to (0,-8).
The main source file should currently look something like the following. At this point you should be able to run the program and see an image of the ship on the screen.
Create a procedure called HandleUserInput and call it at the top of OnEnterFrame. We do this in order to better organize our code because OnEnterFrame will become quite complex otherwise. Inside of HandleUserInput, make the ship respond to the left and right arrow keys (the key codes are VK_LEFT and VK_RIGHT). The best way to do this is by setting a constant velocity for the ship (let's give it a magnitude of 15). This means that you will also need to modify the StepAndDraw code in TMainShip in order to make the position change when the velocity is non-zero.
HandleUserInput and OnEnterFrame should now look something like this:
In the MainShipClass unit, the following changes should be made to StepAndDraw:
The code from the previous problem allows the main ship to move. The only problem is that the ship can disappear off the screen. Modify the code in StepAndDraw so that the X position will not change if such a change moves it out of the range of -12 to 12.
We can't simply use the following:
The reason is that the ship would get stuck if it ever hits the edges of the window. The way to fix this is to have it move at an edge only if that movement was in the opposite direction of the edge. In other words, at X = -12 only a positive velocity would move it away and at X = 12 only a negative velocity would move it away.
Now let's focus on the enemy ships. This will be a bit more interesting because we will have many enemy ships on the screen at the same time. First, create the class TEnemyShip in a very similar way to how we created TMainShip except with the EnemyShipImage. Then add a constructor in which we initialize the Position to something random for X between -10 and 10 and for Y between 10 and 20 (the latter starts the enemies off the screen to the top). Set the velocity to 0 in X and -10 in Y. Also add a global variable Enemies of type TList to keep track of all of them.
The EnemyShipClass unit should look something like this. The only suprise here is that we place the Width and Height variables as private fields of TEnemyShip. We do this because we'll need them later for collision detection but we don't need them outside of this unit so it's best to make them private.
Create a TList instance for the Enemies list in Initialize. Then create a procedure called DrawEnemies. In this procedure, loop through the Enemies list and call StepAndDraw for each enemy. When an enemy drops off the bottom of the screen, remove it from the list. (Y = -10 is the bottom but we should probably use Y = -12 as a cutoff to make sure the enemy is completely off the screen) DrawEnemies itself should be called before MainShip.StepAndDraw inside of OnEnterFrame. And, importantly, at the top of OnEnterFrame, write a statement that creates enemies such that only 1 exists for the first 5 seconds, then 2 after 5 more seconds, then 3, etc. Hint: Use FrameCount which is defined in FTGraphics for this last part.
Initialize, DrawEnemies, and OnEnterFrame should look like the following. The code (FrameCount div (30*5))+1 accomplishes the addition of enemies because it remains 1 for 0 to 5 seconds, is 2 until 10 seconds, is 3 until 15, and so on. This of course assumes that we're running at about 30 frames per second.