64x64 icon dark hosting
Get a Tuts+ subscription for just $45! Deploy New Relic now to claim.

Create a Plane Fighting Game in Corona: More Gameplay

This post is part of a series called Create a Plane Fighting Game in Corona.
Create a Plane Fighting Game in Corona: Gameplay
Create a Plane Fighting Game in Corona: Finishing Gameplay
Final product image
What You'll Be Creating


In the previous tutorial of this series, we started implementing the game's gameplay and already managed to get the plane moving around on the screen. In this tutorial, we'll continue implementing the gameplay. Let's dive right in with the startTimers function.

1. startTimers

As its name indicates, the startTimers function starts the timers. Add the following code to gamelevel.lua.

Invoke this function in the enterScene method as shown below.

2. firePlayerBullet

The firePlayerBullet function creates a bullet for the player.

Here we use the Display object's newImage method to create the bullet. We position it in such a way that it's in the center of the plane on the x axis and at the very top of the plane on the y axis. The bullet is then inserted into the playerBullets table for later reference and also into the planeGroup.

3. Calling firePlayerBullet

We need to call the firePlayerBullet function periodically to make sure the player's plane is automatically firing bullets. Add the following code snippet in the startTimers function.

As its name indicates, the Timer's performWithDelay method calls a specified function after a period of time has passed. The time is in milliseconds, so here we are calling the firePlayerBullet function every two seconds. By passing -1 as the third argument, the timer will repeat forever.

If you test the game now, you should see that every two seconds a bullet appears. However, they aren't moving yet. We will take care of that in the next few steps.

4. movePlayerBullets

In movePlayerBullets, we loop through the playerBullets table and change the y coordinate of every bullet. We first check to make sure the playerBullets table has bullets in it. The # before playerBullets is called the length operator and it returns the length of the object it is called upon. It's useful to know that the # operator also works on strings.

We need to invoke movePlayerBullets in the gameLoop function as shown below.

5. checkPlayerBulletsOutOfBounds

When a bullet goes off-screen, it is no longer relevant to the game. However, they're still part of the playerBullets table and continue to move like any other bullet in the table. This is a waste of resources and, if the game were to go on for a very long time, it would result in hundreds or thousands of unused objects.

To overcome this, we monitor the bullets and, once they move off-screen, we remove them from the playerBullets table as well as from from the display. Take a look at the implementation of checkPlayerBulletsOutOfBounds.

It's important to note that we are looping through the playerBullets table in backwards. If we loop through the table forwards, then, when we remove one of the bullets, it would throw the looping index off and causing an error. By looping over the table in reverse order, the last bullet is already processed. The removeSelf method  removes the display object and frees its memory. As a best practice, you should set any objects to nil after calling removeSelf.

We invoke this function in the gameLoop function.

If you want to see if this function is working properly, you can temporarily insert a print("Removing Bullet") statement immediately after setting the display object to nil.

6. generateIsland

To make the game more interesting, we generate an island every so often, and move it down the screen to give the appearance of the plane flying over the islands. Add the following code snippet for the generateIsland function.

We make use of the newImage method once again and position the island by setting a negative value for the islandHeight. For the x position, we use the math.random method  to generate a number between 0 and the display's contentWidth minus the islandWidth. The reason we subtract the width of the island is to make sure the island is completely on the screen. If we wouldn't subtract the island's width, there would be a chance that part of the island wouldn't be on the screen.

We need to start a timer to generate an island every so often. Add the following snippet to the startTimers function we created earlier. As you can see, we are generating an island every five seconds. In the next step, we'll make the islands move.

7. moveIslands

The implementation of moveIslands is nearly identical to the movePlayerBullets function. We check if the islands table contains any islands and, if it does, we loop through it and move each island a little bit.

8. checkIslandsOutOfBounds

Just like we check if the player's bullets have moved off-screen, we check if any of the islands had moved off-screen. The implementation of checkIslandsOutOfBounds should therefore look familiar to you. We check if the islands y position is greater than display.contentHeight and if it is, we know the island has moved off-screen and can therefore be removed.

9. generateFreeLife

Every so often, the player has a chance to get a free life. We first generate a free life image and if the player collides with the image they get an extra life. The player can have a maximum of six lives.

If the player already has six lives, we do nothing by returning early from the function. If not, we create a new life image and add it to the screen. Similar to how we positioned the islands earlier, we set the image at a negative y position and generate a random value for the image's x position. We then insert it into the freeLifes table to be able to reference it later.

We need to call this function every so often. Add the following snippet to the startTimers function.

10. moveFreeLives

The implementation of moveFreeLifes should look familiar. We are looping through the freeLifes table and move every image in it.

All we need to do is call moveFreeLifes in the gameLoop function.

11. checkFreeLifesOutOfBounds

The following code snippet should also look familiar to you by now. We check if any of the images in the freeLifes table have moved off-screen and remove the ones that have.

We call this function in the gameLoop function.

12. hasCollided

We need to be able to tell when game objects collide with each other, such as the player's plane and the free life images, the bullets and the planes, etc. While Corona offers a very robust physics engine that can easily handle collisions between display objects for us, doing so adds a bit of overhead with the calculations the engine has to do every frame.

For the purposes of this game, we will be using a simple bounding box collision detection system. What this function does, is make sure the rectangles or bounding boxes around two objects don't overlap. If they do, the objects are colliding. This logic is implemented in the hasCollided function.

I found this code snippet on the CoronaLabs website. It works really well, because the game objects in our game are rectangular. If you're working with object that aren't rectangular, then you better take advantage of Corona's physics engine as its collision detection is very well optimized for this.

13. checkPlayerCollidesWithFreeLife

We want to check if the player's plane has collided with a free life object. If it has, then we award the player a free life.

In the checkPlayerCollidesWithFreeLife function, we loop through the freeLives table backwards for the same reason I described earlier. We call the hasCollided function and pass in the current image and the player's plane. If the two object collide, we remove the free life image, increment the numberOfLives variable, and call the hideLives and showLives function.

We invoke this function in the gameLoop function.

14. hideLives

The hideLives function loops through the livesImages table and sets the isVisible property of each life image to false.

15. showLives

The showLives function loops through the livesImages table and sets each image's isVisible property to true.


This brings the third part of this series to a close. In the next and final installment of this series, we will create enemy planes and finalized the game's gameplay. Thanks for reading and see you there.