Advertisement
  1. Code
  2. Mobile Development
  3. Corona

Create a Plane Fighting Game in Corona: Gameplay

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

Introduction

In the first part of this series, we managed to get the start screen showing and were able to transition to the gamelevel screen. In this tutorial, we continue where we left of and start implementing the gameplay.

1. Local Variables

Open gamelevel.lua, the file we created in the first tutorial, and add the following below the line local scene = storyboard.newScene().

1
local playerSpeedY = 0
2
local playerSpeedX = 0
3
local playerMoveSpeed = 7
4
local playerWidth  = 60
5
local playerHeight = 48
6
local bulletWidth  = 8
7
local bulletHeight =  19
8
local islandHeight = 81
9
local islandWidth = 100
10
local numberofEnemysToGenerate = 0
11
local numberOfEnemysGenerated = 0
12
local playerBullets = {} -- Holds all the bullets the player fires
13
local enemyBullets = {} -- Hold the bullets from "all" enemy planes
14
local islands = {} --  Holds all the islands
15
local planeGrid = {} -- Holds 0 or 1 (11 of them for making a grid system)
16
local enemyPlanes = {}  -- Holds all of the enemy planes
17
local livesImages = {}  -- Holds all of the "free life" images
18
local numberOfLives = 3
19
local freeLifes = {} -- Holds all the ingame free lives
20
local playerIsInvincible = false
21
local gameOver = false
22
local numberOfTicks = 0 -- A number that is incremented each frame of the game
23
local islandGroup -- A group to hold all of the islands 
24
local planeGroup -- A group that holds all the planes, bullets, etc
25
local player
26
local  planeSoundChannel -- SoundChannel for the plane sound
27
local firePlayerBulletTimer
28
local generateIslandTimer
29
local fireEnemyBulletsTimer
30
local generateFreeLifeTimer
31
local rectUp -- The "up" control on the DPAD
32
local rectDown -- The "down" control on the DPAD
33
local rectLeft -- The "left" control on the DPAD
34
local rectRight -- The "right" control on the DPAD

Most of these are self-explanatory, but I've included comments for clarification. From here on out, all code should be inserted above the line return scene.

2. createScene

Start by adding the createScene function to main.lua. The createScene function is called when the scene's view doesn't yet exist. We'll add the game's display objects in this function.

1
function scene:createScene( event )
2
    local group = self.view
3
end
4
scene:addEventListener( "createScene", scene )

3. setupBackground

1
function setupBackground ()
2
    local background = display.newRect( 0, 0, display.contentWidth, display.contentHeight)
3
    background:setFillColor( 0,0,1)
4
    scene.view:insert(background)
5
end

In setupBackground, we create a blue background using the Display object's newRect method. The setFillColor method takes RGB values, as percentages. Invoke the setupBackground function in createScene as shown below.

1
function scene:createScene( event )
2
    local group = self.view
3
    setupBackground()
4
end

4. setupGroups

The setupGroups function instantiates the islandGroup and planeGroup groups, and inserts them into the scene's view. The GroupObject is a special type of display object into which you can add other display objects. It's important to first add the islandGroup to the view to make sure the islands are below the planes.

1
function setupGroups()
2
    islandGroup = display.newGroup()
3
    planeGroup = display.newGroup()
4
    scene.view:insert(islandGroup)
5
    scene.view:insert(planeGroup)
6
end

Invoke the setupGroups function in createScene as shown below.

1
function scene:createScene( event )
2
    local group = self.view
3
    setupBackground()
4
    setupGroups()
5
end

5. setupDisplay

The setupDisplay function draws a black rectangle at the bottom of the screen and inserts dpad and plane images into the view.

1
function setupDisplay ()
2
    local tempRect = display.newRect(0,display.contentHeight-70,display.contentWidth,124);
3
    tempRect:setFillColor(0,0,0);
4
    scene.view:insert(tempRect)
5
    local logo = display.newImage("logo.png",display.contentWidth-139,display.contentHeight-70);
6
    scene.view:insert(logo)
7
    local dpad = display.newImage("dpad.png",10,display.contentHeight - 70)
8
    scene.view:insert(dpad)
9
end

Again, invoke this function in createScene as shown below.

1
function scene:createScene( event )
2
    local group = self.view
3
    setupBackground()
4
    setupGroups()
5
    setupDisplay()
6
end

6. setupPlayer

The setupPlayer function simply adds the player image to the screen. The Display object comes with two read-only properties, contentWidth and contentHeight, representing the original width and height of the content in pixels. These values default to the screen width and height, but may have other values if you're using content scaling in config.lua. We use these properties to align the player in the  scene.

1
function setupPlayer()
2
    player = display.newImage("player.png",(display.contentWidth/2)-(playerWidth/2),(display.contentHeight - 70)-playerHeight)
3
    player.name = "Player"
4
    scene.view:insert(player)
5
end

Invoke the setupPlayer function in createScene.

1
function scene:createScene( event )
2
    local group = self.view
3
    setupBackground()
4
    setupGroups()
5
    setupDisplay()
6
    setupPlayer()
7
end

7. setupLivesImages

The setupLivesImages function sets up six life images and positions them at the top left of the screen. We then insert these images into the livesImages table, so that we're able to reference them later. Lastly, we make sure that only the first three images are visible.

1
function setupLivesImages()
2
for i = 1, 6 do
3
      local tempLifeImage = display.newImage("life.png",  40* i - 20, 10)
4
      table.insert(livesImages,tempLifeImage)
5
      scene.view:insert(tempLifeImage)
6
      if( i > 3) then
7
           tempLifeImage.isVisible = false;
8
      end
9
end
10
end

The setupLivesImages function is also invoked in the createScene function.

1
function scene:createScene( event )
2
        local group = self.view
3
         setupBackground()
4
         setupGroups()
5
         setupDisplay()
6
         setupPlayer()
7
         setupLivesImages()
8
end

8. setupDPad

The setupDPad function sets up the four rectangles rectUp, rectDown, rectLeft, and rectRight. We carefully position them on top of the dpad image, configure them to not be visible, and make sure the isHitTestable property is set to true.

If you set display objects to not be visible, you're initially unable to interact with them. However, by setting the isHitTestable property to true, this behavior is overridden.

1
function setupDPad()
2
    rectUp = display.newRect( 34, display.contentHeight-70, 23, 23)
3
    rectUp:setFillColor(1,0,0)
4
    rectUp.id ="up"
5
    rectUp.isVisible = false;
6
    rectUp.isHitTestable = true;
7
    scene.view:insert(rectUp)
8
9
    rectDown = display.newRect( 34,display.contentHeight-23, 23,23)
10
    rectDown:setFillColor(1,0,0)
11
    rectDown.id ="down"
12
    rectDown.isVisible = false;
13
    rectDown.isHitTestable = true;
14
    scene.view:insert(rectDown)
15
16
    rectLeft = display.newRect( 10,display.contentHeight-47,23, 23)
17
    rectLeft:setFillColor(1,0,0)
18
    rectLeft.id ="left"
19
    rectLeft.isVisible = false;
20
    rectLeft.isHitTestable = true;
21
    scene.view:insert(rectLeft)
22
23
    rectRight= display.newRect( 58,display.contentHeight-47, 23,23)
24
    rectRight:setFillColor(1,0,0)
25
    rectRight.id ="right"
26
    rectRight.isVisible = false;
27
    rectRight.isHitTestable = true;
28
    scene.view:insert(rectRight)
29
end

You've guessed it. This function is also invoked in createScene.

1
function scene:createScene( event )
2
    local group = self.view
3
    setupBackground()
4
    setupGroups()
5
    setupDisplay()
6
    setupPlayer()
7
    setupLivesImages()
8
    setupDPad()
9
end

9. resetPlaneGrid

The resetPlaneGrid function resets the planeGrid table and inserts eleven zeros. The planeGrid table imitates eleven spots across the x axis, in which an enemy plane can be positioned. This will make more sense once we start generating enemy planes.

1
function resetPlaneGrid()
2
planeGrid = {}
3
     for i=1, 11 do
4
         table.insert(planeGrid,0)
5
     end
6
end

Invoke this function in createScene.

1
function scene:createScene( event )
2
    local group = self.view
3
    setupBackground()
4
    setupGroups()
5
    setupDisplay()
6
    setupPlayer()
7
    setupLivesImages()
8
    setupDPad()
9
    resetPlaneGrid()
10
end

10. enterScene

Now that all the display objects are in place, it's time to add event listeners, timers, etc. If you recall from the previous part of this tutorial, the enterScene function is a good place to set these up. Start by inserting the following code snippet.

1
function scene:enterScene( event )
2
    local group = self.view
3
end
4
scene:addEventListener( "enterScene", scene )

11. Removing the Previous Storyboard

When we enter this scene, we need to remove the previous scene. Add the following code to the enterScene function to do this.

1
local previousScene = storyboard.getPrevious()
2
storyboard.removeScene(previousScene)
When you enter a new scene, the previous scene you were on can be referenced by calling getPrevious on the storyboard object. We remove it completely from the storyboard by calling removeScene on the storyboard object.

12. Add Event Listeners to Dpad Rectangles

Add the following code below the code you entered in the previous step. This code snippet adds touch listeners to each of the rectangles, invoking movePlane with every touch. Let's take a look at this movePlane function in the next step.

1
rectUp:addEventListener( "touch", movePlane)
2
rectDown:addEventListener( "touch", movePlane)
3
rectLeft:addEventListener( "touch", movePlane)
4
rectRight:addEventListener( "touch", movePlane)

13. movePlane

The movePlane function is responsible for setting the planes speed. We check if the touch event's phase is equal to began, which means the player has touched down but not lifted their finger back up. If this is true, we set the speed and direction according to which rectangle was touched. If the touch event's phase is equal to ended, then we know the player has lifted their finger, which means we set the speed to 0.

1
function movePlane(event)
2
    if event.phase == "began" then
3
        if(event.target.id == "up") then
4
          playerSpeedY = -playerMoveSpeed
5
        end
6
        if(event.target.id == "down") then
7
          playerSpeedY = playerMoveSpeed
8
        end
9
        if(event.target.id == "left") then
10
          playerSpeedX = -playerMoveSpeed
11
        end
12
        if(event.target.id == "right") then
13
          playerSpeedX = playerMoveSpeed
14
        end
15
    elseif event.phase == "ended" then
16
        playerSpeedX = 0
17
        playerSpeedY = 0 
18
   end
19
end

14. PlaneSound

Let's add some sound to our game. Add the following code snippet to the enterScene function. It loads and plays planesound.mp3. By setting the loops property to -1, the sound will loop forever. If you want to learn more about audio in Corona, be sure to check out the documentation.

1
local planeSound = audio.loadStream("planesound.mp3")
2
planeSoundChannel = audio.play( planeSound, {loops=-1} )

15. enterFrame Event

We also add a runtime event listener named enterFrame that will call the gameLoop function. The frequency with which the enterFrame event occurs depends on the frames per second (FPS) value you set in config.lua. In our example, it will be called 30 times per second. Add this event listener in the enterScene function.

1
 Runtime:addEventListener("enterFrame", gameLoop)

16. gameLoop

In the gameLoop function we update the sprite positions and perform any other logic that needs to take place every frame. If you are interested in reading more on the topic of game loops, Michael James Williams wrote a great article that explains how a common game loop works. Add the following code snippet.


1
function gameLoop()
2
    movePlayer()
3
end

17. movePlayer

The movePlayer function manages the moving of the player's plane. We move the plane according to the playerSpeedX and playerSpeedY values, which will either be 7 or 0, depending on whether the player is touching on the DPad or not. Refer back to the movePlane function if this is unclear. We also do some bounds checking, making sure the plane cannot move off-screen.

1
function movePlayer()
2
    player.x = player.x + playerSpeedX
3
    player.y = player.y + playerSpeedY
4
    if(player.x < 0) then
5
        player.x = 0
6
   end
7
   if(player.x > display.contentWidth - playerWidth) then
8
       player.x = display.contentWidth - playerWidth
9
   end
10
   if(player.y   < 0) then
11
       player.y = 0
12
  end
13
  if(player.y > display.contentHeight - 70- playerHeight) then
14
      player.y = display.contentHeight - 70 - playerHeight
15
  end
16
end

If you test the game now, you should be able to navigate the plane around the screen using the DPad.

Conclusion

This brings the second tutorial of this series to a close. In the next installment of this series, we will continue with the gameplay. Thanks for reading and see you there.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.