Programming with Lua & Corona
Self-Paced Course

Game Creation
© Robert P. Cook 2014

Introduction

The course is designed for kids or adults that do not know much about computers but who would like to learn to program using game technology.  All software is free.  If you are a K-8 student, you might first want to complete the very gentle, free introduction to programming at www.code.org.

One way to teach game programming is first to cover the concepts and then to rely on the students' creativity.  A second approach, which is adopted here, is to start with game creation with a few concepts embedded here and there.

All programming starts with planning.  The old adage "measure twice, cut once" applies.  Rather than invent a new game, we start with a simple, but popular, game called "Don't Touch The White Tile" by Wang Boxun.

Don't Touch Game
The tiles move from the bottom of the screen to the top and the goal is to tap the colored tiles but not to tap a white tile.

Now the planning part.  One of the most important principles in programming is "Step-Wise Decomposition", which means divide hard problems into simple steps.  Ask yourself "how can I make the problem simpler?".

For example, "what is simpler than a game with green and white tiles?".  Answer: a game with only green tiles.

What is simpler than a game with multiple green tiles?  Answer: a game with only one green tile.

What is simpler than moving a green tile from the bottom of a screen to the top?  Answer: not moving a tile at all.

The second important programming principle is "Step-Wise Refinement", which means starting a new program with a simple goal and then gradually adding more features until the original goal is achieved.

Steps

The goal of the first step is to display a green tile.  If the user taps the green tile, they win!  Otherwise, the game is lost.  Notice that the game runs on an iPhone.  That is no problem as the Corona SDK builds applications for iPhones.  But maybe you do not have an iPhone! Luckily, Corona provides an emulator that supports iPhone development on a PC or Mac (see the next Figure).

Corona iPhone Emulator

Install Corona (skip to START if already installed)

Install the Corona Starter SDK. The link to the SDK is at the very bottom of the coronalabs.com web page:
GetCorona
Starter: Publish Free

Click on the Windows Desktop tile. Desktop Your system might have a different picture but the tile will have the word "Desktop".

The Desktop should have an orange "Corona Simulator" icon (picture). Corona icon

START HERE

Corona applications are specified using the Lua programming language. Lua is the primary scripting language for modding games.

Once you finish this tutorial, read my Lua book and take the more challenging online course at professorcook.org.

Lua programs are typed into a main.lua text file that can be created with the Windows WordPad editor.  Do not use Microsoft Word as it intermingles formatting codes with the text so that the file becomes unusable for programming.

The main.lua file, as well as any image or sound files that a program uses, must be stored in a directory (also termed a Folder in Windows).

If step-wise decomposition is applied, we must create a directory named "game1" and then a text file called "main.lua" in that directory before starting to program.

Right-click on any empty space on the Desktop.  Select the "new" menu item then "Folder" as illustrated next.

Folder menu

Name the folder (directory) "game1" by replacing the "New folder" text.

Game1 directory

Double-click on the game1 folder, then Select the "View" menu item and click on "Options".

Folder View

Then select the "View" tab and make sure that "Hide extensions..." is unchecked. Click the OK button to close the dialog and to save changes.

File Extensions

Now right-click in the blank area in the game1 window, then select the "new" menu item, then select the "Text Document" menu item.  A new, empty text file should be created as follows.

File

Click on the "New Text Document" text and change the file name to "main".  Also, select the "txt" suffix, but not the period, and change it to "lua".  A dialog will pop up warning about changing suffixes. Click the "Yes" button to ignore the warning and to accept the change.

The next steps associate the WordPad text editor as the application to "Open With" .lua files. Right-click main.lua, choose OpenWith from the first menu, ChooseDefaultProgram for the second menu, then select WordPad and check the box that says "use this app for all .lua files". The resulting window should appear as follows. Observe that the icon to the left of the name changed.  Double-clicking main.lua should now open the file in the WordPad text editor.

Window

Finally, after creating one game directory, you can create another by just right-clicking on the folder name and selecting Copy from the menu.  Then right-click a second time and select the Paste menu item.  Name the copied directories game2, game3 etc.

A Rectangle Program

You need to understand that a game tile is a four-sided, geometric figure known as a rectangle.

Double-click the main.lua file name.  Copy or type the following two lines into the WordPad document window. Close the WordPad application saving the changes. Every character is critical when programming.  Even the smallest error will result in obscure messages so be sure that all code is copied exactly.  Upper and lowercase letters must be typed exactly as shown.


Copy and paste these two lines into main.lua.
display.newRect(200, 200, 100, 200)
print('Hello World')


Double-click the Corona Simulator beach-ball-like icon on the Desktop. Notice that two separate windows appear.  One is a duplicate window of a physical device, such as an Android phone; the other window is a console window, which displays messages and all printed output.

Click the OPEN icon (gray circle enclosing an up-arrow). 


  1. Navigate to the game1 directory in the file dialog. 
  2.  Double-click the name game1,
  3. then select the file main.lua,
  4. and click the Open button in the dialog. 

    The program runs automatically and displays a white rectangle on a black background in the device window and the text Hello World in the console window. Observe that there are two Corona windows.
  5. Find the print output Hello World in the console window and the rectangle in the simulator window.
  6. Keep both Corona windows visible.
Rectangle and Hello

The next rule of programming is "the best way to write correct code is to copy correct code". To that end, the Corona application has a Help menu item that links directly to online documentation.  Drawing rectangles is documented here. If needed, review Cartesian Coordinates.  The upper-left corner is the x=0,y=0 position.  X coordinates increase from left to right; Y coordinates increase from top to bottom, which is the opposite of the Cartesian system taught in schools.

The first two numbers following newRect select the x-y coordinate of the center of the rectangle to draw.  The second two numbers indicate the width of the rectangle in pixels and the height, respectively.

Coordinates

In the Corona device emulator window, select the menu items File/OpenInEditor.  Corona opens your main.lua file in the WordPad editor automatically.  NEVER use any of the formatting features in WordPad on a main.lua file as that would change the internal data from a "plain text file" to a "rich text file" that Corona could not understand.

After changes, select the menu item File/Save in the WordPad editor.  Corona automatically recognizes the change and asks the user about relaunching the simulator. Select the Yes button. You may wonder why the Corona window is small.  It is the size of a cell phone window.  The View/ViewAs menu item displays all the platforms on which any program that you write can execute, which is pretty cool.

The next rule of programming is "understand your tools".  It is not enough to read the documentation.  Write small programs to experiment with the x, y, width and height arguments to the "newRect" function and change the words in "print" to make sure that you understand their capabilities.  Draw two rectangles by copying and changing the code.

The rectangle that is drawn by the program  is not green and does not have a border as shown in the game.  The interior color of a figure is referred to as a fill.  The border color of a figure is referred to as a stroke, like a brush stroke.  Border colors can also be drawn with different widths (in pixels).

Stroke/Fill

All entities in a Lua program have properties, some less, some more. To access or set a property requires naming a Lua entity.  A Lua rule is "names must be defined before use".  Names must begin with a letter and can be composed of letters and numbers. The program code for a definition is listed next. Multiple names can be defined by using a comma-separated list following the keyword local.  Keywords identify the different features of a language.  They are shown in bold, but only plain text is used in programming.

Name Definition
local x, X, longer, y67, TwoWords
local aReallyLongWord

What do the numbers in newRect mean?
local rect
rect = display.newRect(234, 99, 100, 50)
Access value
Set value
rect.x
rect.x = 234
rect.y
rect.y = 99
rect.width
rect.width = 100
rect.height
rect.height = 50

rect:setFillColor(0, 1, 0.5, 1)  --RGBA

rect:setStrokeColor(0, 1, 0.5, 1)
rect.strokeWidth rect.strokeWidth = 4

Most of the rectangle properties are pretty easy to understand.  However, the color values, such as 0 or 0.5, have no obvious interpretation.  Read the color introduction. Click here. You may need to refer to the color chart here from time to time.  Corona colors are set with real numbers (0.0 to 1.0). The four numbers, from left to right, represent the red, green, blue, and opacity of a color.  Zero indicates no contribution; one means "as much as possible".  An opaque value of 1 means that you cannot see through it.  Decreasing fractions increase transparency.  Zero means invisible.

Color values are typically referred to as RGBA, where R is red, G is green, B is blue, and A stands for "alpha", which is a synonym for transparency in computer-talk. (0,0,0,1) is RGBA for the color black, (1,1,1,1) is for white.

The following steps can be followed to easily draw fill and stroke colors by using a name in the color chart to convert to the Lua standard.

Follow these steps in use a Color Chart Name in Programs

  1. Look up a color name, such as orange (#FFA500), in the color chart listed above.
  2. If the listing has fewer than 6 symbols, fill out with leading zeroes e.g. #FF to #0000FF
  3. Copy two characters at a time to setFillColor or setStrokeColor as follows:
                                setFillColor(0xFF/255, 0xA5/255, 0x00/255, 1)
  4. The / character is the "divide by" operator in Lua while the * character is the multiply operator.
  5. Try the code. Click on the Corona application File/OpenInEditor.  Edit game1/main.lua as follows.  When you select Save or close WordPad, Corona detects the change and asks if you want to relaunch the application. Click "Yes".  The  "--" characters in Lua cause all following characters through the next end-of-line to be ignored. Thus, programmers can add comments that clarify what the code is doing.
  6. Change the fill color from orange to green.

Create a new game2 directory and main.lua for this code.
local rect
rect = display.newRect(200, 200, 100, 200)
rect:setFillColor(0xFF/255,0xA5/255,0x00/255,1) --orange
rect:setStrokeColor(0x70/255,0x80/255,0x90/255,1) --slate gray
rect.strokeWidth = 4  --border width of 4 pixels
print('Hello World')

The second challenge in the program is that the background is black, not white.  Insert the following line at the beginning of the program.

Setting the Background Color
Insert as the 1st line of the previous program.

display.setDefault('background', 1, 1, 1)  --white

At this point, you could modify the program to draw multiple green and white rectangles.  However, let us finish the one-square, one-touch game first.

Having the rectangles appear in the same place every time is not very challenging.  Further, leaving the tile in the same place makes it pretty easy to touch.  Players lose interest quickly for games that are too easy to win. The following code shows how to perform an action at fixed intervals (in milliseconds or 1/1000 of a second). A Lua "function" simply encapsulates, or bundles, a sequence of code so that it can be executed by referring to a single name.

The code moves the tile to random locations around the screen at one-second intervals. The first step is to copy the example code into the program from the documentation web site.

Performing an Action after a Delay

                       Create a new directory and main.lua for this code.
Look at the Corona Console window for print output.

local function listener( event ) --code is executed every second print( "listener called" ) end local stepper = timer.performWithDelay( 1000, listener )


PROGRAM OUTPUT IN CONSOLE WINDOW
Hello World
listener called

However, we want to move the tile continuously, not just once.  The documentation states that adding a third argument of zero will cause the interval to repeat.  Further, we discover that there is a math.random() function that returns a random, fractional value between zero and one.  Also, display.contentWidth is the screen width and display.contentHeight is a property value for the screen's height.  The code follows.

Random Tile Moves at 1s Intervals

                                        Create a new directory and main.lua for the following code.

display.setDefault('background',1,1,1)  --set the background color
local rect
rect = display.newRect(200, 200, 100, 200)           --centerX, centerY, width, height
rect:setFillColor(0xFF/255,0xA5/255,0x00/255,1) --orange
rect:setStrokeColor(0x70/255,0x80/255,0x90/255,1) --slate gray
rect.strokeWidth = 4  --set the border width in pixels
print('Hello World')

local function listener( event )
    rect.x = math.random() * display.contentWidth  --random location across the screen
    rect.y = math.random() * display.contentHeight --random vertical location
end

local stepper = timer.performWithDelay( 1000, listener, 0 ) --relocate at 1s intervals and repeat


The next step is to learn about code that checks whether a user taps the tile or the background.  A "tap" event is signaled after both a mouse click and release. A "touch" event is signaled for each of a click/touch and a release event. Corona provides event functions that will monitor any action on a shape or on the screen background.

Perform an Action on Tap Events

                                                    Add the following lines to the previous program.

local function tapped( event )  --action when tile tap occurs
    print('tile tapped')
    return true
end

rect:addEventListener('tap', tapped)  --tap on tile

local function notTapped( event )  --action when background tap occurs
    print('not tapped')
    return true
end

Runtime:addEventListener('tap', notTapped) --tap on background


When playing the game on the emulator, we discovered that it was hard to click and release the mouse in a second.  We changed the interval to 1500 (1.5s), which seemed to work better.  Programmers always learn more about an application as it is being implemented.  Be prepared for changes and surprises and mysteries.

The next actions in the step-wise refinement process are to tell the user whether they have won or lost, turn off the timer, and remove the rectangle from the screen.  The Corona showAlert function can be invoked to send a message to the user.

Another rule of programming is "try out new features in a separate program before using them in an existing program".  I keep a gametemp directory with a main.lua just to try out new Corona features before adding the code to a work in progress.  The reason for isolating the new code in this fashion is to expose errors in an environment in which only the new code can be at fault.

Send a Message to the User

                                  Create a new directory and main.lua to try this code.
                                  Look for the print output in the Corona Console window.


local function onComplete( event )  --action when the user taps OK
    print('end of message')
    return true
end

native.showAlert( "Congratulations", "YOU WON!", { "OK" }, onComplete )


By checking the timer and display documentation, we learn how to cancel a timer and how to remove a displayed shape.  The code is listed next.

Display WON/LOST and Terminate the Game

                           Create a new directory and main.lua for this code.

display.setDefault('background',1,1,1)  --set the background color
local rect
rect = display.newRect(200, 200, 100, 200)           --centerX, centerY, width, height
rect:setFillColor(0xFF/255,0xA5/255,0x00/255,1) --orange
rect:setStrokeColor(0x70/255,0x80/255,0x90/255,1) --slate gray
rect.strokeWidth = 4  --set the border width in pixels
print('Hello World')

local function listener( event )
   
rect.x = math.random() * display.contentWidth  --random location across the screen
   
rect.y = math.random() * display.contentHeight --random vertical location
end

local
stepper = timer.performWithDelay( 1500, listener, 0 ) --relocate at 1s intervals and repeat

local function onComplete( event ) --actions to terminate the game
    timer.cancel(stepper)                   --turn off the ticker
    display.remove(rect)                   --remove the tile from the screen
    return true
end

local function tapped( event )  --actions to win the game
    native.showAlert( "Congratulations", "YOU WON!", { "OK" }, onComplete )
    return true
end

rect:addEventListener('tap', tapped)

local function notTapped( event )  --actions to lose the game
    native.showAlert( "So Sorry :(", "YOU LOST!", { "OK" }, onComplete )
    return true
end

Runtime:addEventListener('tap', notTapped)

Won/Lost

CONGRATULATIONS!! YOU CODED THE WORLD'S SIMPLEST GAME.

Refinements

One of the key elements in successful computer games is drama.  Displaying a dialog upon winning is not very dramatic.  Three common dramatic elements are sound, explosions and fireworks.  We will review code for each effect next.

Sound Effects

Always follow the copyright or other restrictions on code or media.  I used the following web site as my source for royalty-free, restriction-free sound files.  Even for free files, it is courteous to list the source in your code.

We added three sound effects to the game: background music (played in a loop), a "won" noise, and a "lost"noise.  The Corona documentation for audio (including examples) is here.  Corona supports sound files with a ".wav" or ".mp3" suffix.  Again, we recommend trying out these new functions in a simple test program.

To download a sound file, right-click on its link in the web page.  Select "Save link As" in the menu.  Then click on Desktop.  Make sure that the download name has a .wav or .mp3 suffix.  Once on the Desktop, a sound file can be Cut/Pasted or drag-and-dropped to the game directory. Media files MUST be copied to the same directory as the main.lua program that uses them.

Sometimes web files are automatically placed in the Downloads directory. Click on the FileManager icon in the Windows task bar. File Manager Then double-click the Downloads directory.  Scroll down to your file and then Cut/Paste to the game directory.

Here are the links to the three files that we used: youwin.wav, youlose.wav and 18418.mp3.  Download the three files and copy to the game1 directory.  Examine the directory to make sure the files are there.

Playing Sound Files


                            Create a new directory and main.lua for this code.

local sound2 = audio.loadSound("18418.mp3") --from http://eng.universal-soundbank.com/
audio.setVolume(1.0)
audio.play(sound2, { channel=2, loops=-1, fadein=5000 }) --play background continuously


Corona supports 32 channels of sound.  The background music is played on channel 2 to avoid interference with the winning and losing sounds.  The program fragment shown next addresses a new Lua requirement.  Names that are removed, disposed or deleted should be set to nil.


Create a new directory and main.lua for this code.

display.setDefault('background',1,1,1)  --set the background color
local rect
rect = display.newRect(200, 200, 100, 200)           --centerX, centerY, width, height
rect:setFillColor(0xFF/255,0xA5/255,0x00/255,1) --orange
rect:setStrokeColor(0x70/255,0x80/255,0x90/255,1) --slate gray
rect.strokeWidth = 4  --set the border width in pixels
print('Hello World')

local function listener( event )
   
rect.x = math.random() * display.contentWidth  --random location across the screen
   
rect.y = math.random() * display.contentHeight --random vertical location
end

local
stepper = timer.performWithDelay( 1500, listener, 0 ) --relocate at 1s intervals and repeat

local sound = audio.loadSound("youwin.wav")
local sound2 = audio.loadSound("18418.mp3") --from http://eng.universal-soundbank.com/
local sound3 = audio.loadSound("youlose.wav")
audio.setVolume(1.0)

local function onComplete( event )
    timer.cancel(stepper)
    display.remove(rect)
    audio.pause(sound)
    audio.pause(sound3)
    audio.stop()
    audio.dispose(sound)
    audio.dispose(sound2)
    audio.dispose(sound3)
    stepper=nil rect=nil sound=nil sound2=nil sound3=nil
    return true
end

local function tapped( event )
    audio.pause(sound2)                --turn off background music
    audio.play(sound, {loops=4})  --play youwon
    native.showAlert( "Congratulations", "YOU WON!", { "OK" }, onComplete )
    return true
end

rect:addEventListener('tap', tapped)

local function notTapped( event )
    audio.pause(sound2)                  --turn off background music
    audio.play(sound3, {loops=4})  --play youlost
    native.showAlert( "So Sorry :(", "YOU LOST!", { "OK" }, onComplete )
    return true
end

Runtime:addEventListener('tap', notTapped)

audio.play(sound2, { channel=2, loops=-1, fadein=5000 })

An Explosion Effect

Most animations in computer games are implemented using sprite sheets, which contain sequences of individual pictures (called frames).  A motion picture is actually composed from thousands of still pictures.  Movies take advantage of the human perception that still frames shown at 20, or more, times a second appear to mimic looking at the same scene in real life. Read the Wikipedia article on animation here.

Copy the following code into main.lua in a new directory.  The code rotates the game tile 11 degrees (see Wikipedia) every clock tick, which is initially set at 1.5 seconds.  Decrease the time from 1500 to determine the value at which the rotation "looks" continuous.

Animate a Rectangle




                                        Create a new directory and main.lua for this code.

display.setDefault('background',1,1,1)
local rect
rect = display.newRect(200, 200, 100, 200)
rect:setFillColor(0xFF/255,0xA5/255,0x00/255,1) --orange
rect:setStrokeColor(0x70/255,0x80/255,0x90/255,1) --slate gray
rect.strokeWidth = 4

local function step( event )
  rect:rotate(11)  --11 degrees per step
end
timer.performWithDelay(1500, step, 0)


The following sprite sheet for an explosion is 320x320 pixels.  The image contains 25 frames that are arranged in five rows and five columns.  Thus, each frame (or step in the explosion) is 320/5 wide and 320/5 high.

Explosion Sprite Sheet
Explosion sprite sheet

One of the special characteristics of sprite sheets is that each frame's background color has an opaque value of zero; that is, when a frame is drawn over any background, the background will "show through" transparent pixels. The Corona documentation for animating sprite sheets is here.  The following code illustrates how to play a sprite sheet. Google different sprite sheets on the Internet to learn about animation.

Download explosion.png here.  When an animation is set to loop (as in the example), it plays over from the beginning by default.  If the "loopDirection" property is set to "bounce", the frames are played forward to the last frame and then backwards to the first frame, and so on.

Playing a Sprite Sheet


                                                 Create a new directory and main.lua for this code.

local options = { --explosion is 320x320 pixels, 5 columns, 5 rows
   width = 64,    --width of each frame, typically imageWidth/columns
   height = 64,   --height of each frame, typically imageWidth/rows
   numFrames = 25  -- consecutive frames
}
--from http://www.gamedev.net/blog/309/entry-2046307-explosions/
local sheet = graphics.newImageSheet( "explosion.png", options )

local sequenceData =
{
    name="explosion",
    start=1,         --a sheet can have more than 1 animation
    count=25,        --number of consecutive frames
    time=2000,       --2 seconds to play all 25 frames
    loopCount = 0,   -- Optional ; default is 0 (loop indefinitely)
    loopDirection = "forward"    -- Optional ; "forward" or "bounce"
}

local ani = display.newSprite(sheet, sequenceData)
ani.x = display.contentCenterX
ani.y = display.contentCenterY
ani:scale(10, 10)                             --make the explosion more dramatic (bigger)
ani:play()


Copy the initialization code for the explosion to your game then replace the notTapped function with the following lines.


                       Replace the previous notTapped code with the following.

local function notTapped( event )
    audio.pause(sound2)
    audio.play(sound3, {loops=4})
    ani.x = display.contentCenterX
    ani.y = display.contentCenterY
    ani:scale(10,10)
    ani:play()
    native.showAlert( "So Sorry :(", "YOU LOST!", { "OK" }, onComplete )
    return true
end


Fireworks Effect

The sprite sheet for the fireworks effect can be downloaded here. If you examine the image with the Windows Paint program (open Search, type paint), the different frames are all different widths.  Corona has an option to select frames from anywhere on a sprite sheet (and even from different sheets).  The code is listed next.  Again, try it out separately before changing the game to display fireworks for the winner.

Fireworks Sprite Sheet

Fireworks

Create a new directory and main.lua for this code.

display.setDefault('background', 1,1,1)
local options = { --fireworks is 377x70 but each frame is a different size
   frames =
    {
        { --frame 1
            x = 6, y = 0, width = 34, height = 70
        },
        { --frame 2  
            x = 45,y = 0, width = 50, height = 70
        },
        { --frame 3
            x = 96, y = 0, width = 56, height = 70
        },
        { --frame 4 
            x = 157, y = 0, width = 64, height = 70
        },
        { --frame 5 
            x = 226, y = 0, width = 66, height = 70
        },
        { --frame 6 
            x = 295, y = 0, width = 66, height = 70
        }
    }
}
local sheet = graphics.newImageSheet( "fireworks.png", options )
local sequenceData =
{
    name="explosion",
    start=1,         --a sheet can have more than 1 animation
    count=6,        --number of consecutive frames
    time=2000,       --2 seconds
    loopCount = 0,   -- Optional ; default is 0 (loop indefinitely)
    loopDirection = "forward"    -- Optional ; "forward" or "bounce"
}

local ani = display.newSprite(sheet, sequenceData)
ani.x = display.contentCenterX
ani.y = display.contentCenterY
ani:scale(8, 8)
ani:play()

Forwards/Backwards Effect

It is very common  in games to animate figures that can move forwards and backwards.  It would be time-consuming to have to draw animation frames for both directions.  Luckily, the common scale method can be used to animate a figure to move forwards or backwards (just negate the x-scale value).  The following sprite sheet (from the Corona sample code) shows a mountain lion running. The dimensions are 256x256 with 2 columns and four rows. Download runningcat here.  Create a new directory and main.lua to play the cat animation to practice using a sprite sheet.  Change the parameters in the explosion code.  Change the scale to (4,4) since the frames are larger. Change the scale to (-4,4) to run the lion backwards.

runningcat Sprite Sheet

Running Cat

Final Points

Splash Screen

No game would be complete without a splash screen (game equivalent of a movie title). Watch the Vimeo video A Brief History of Titles. I thought of different catchy titles for the game but settled on "Finger Tip".  The following image shows the splash screen.  Read the Corona documentation on displaying text here.

Splash Screen

Typically, a splash screen would also have a Settings, Credits and Instructions buttons.  The code to display the splash screen is listed next.  Download the image here.

Display a Splash Screen


Create a new directory and main.lua for this code.

display.setDefault('background', 1,1,1)
local background = display.newImage( "finger.jpg", true )
background.x = display.contentWidth / 2
background.y = display.contentHeight / 2
local text = display.newText('Finger Tip', background.x, background.y, native.systemFontBold, 128)
text:setFillColor(0,0,1) --blue
text:rotate(30)
local text2 = display.newText('Touch the tile, but not the background, to win.',
                     background.x, display.contentHeight-80, --location
                     display.contentWidth, 0,                           --wrap text if too long
                     native.systemFontBold, 48)
text2:setFillColor(0,1,0) --green

local function startGame( event )
  Runtime:removeEventListener('tap', startGame)
  display.remove(text)
  display.remove(text2)
  display.remove(background)
  text=nil text2=nil background=nil
  print('Put code here to start game.')
end
Runtime:addEventListener('tap', startGame)


Scoring

The game, as implemented so far, is over all too quickly.  Players might like a scoring option better than one-and-done.  Just add a name "local score" with all the other names, then set score to zero "score = 0". Now it is just a matter of incrementing score "score = score+10" when the tile is tapped and decrementing the score "score = score-5" when the player misses.  We can declare the game "lost" if the score goes negative and "won" if the score reaches 100.  The code for this option can be downloaded here.

The conditional tests are implemented with the Lua conditional-test statement.  The statement is termed "conditional" because if the Boolean expression evaluates to true, the statements between the "then" and "end" keywords are executed.  If the Boolean expression evaluates to false, the statements are bypassed.

Lua Conditional Test
if score < 0 then lostFunction() end
if score >= 100 then wonFunction end

Sliding Rectangle

The original game, which was the inspiration for this lesson, moves tiles from the bottom of the screen to the top, not randomly.  Just to be complete, we discuss how to implement this feature using the Lua statements and Corona methods that have been discussed so far plus parameterized functions.  The iPhone application image shows four four rows of four tiles each. The following code illustrates how to create some of the sixteen tiles.

Sixteen Rectangles
Create Three Tiles

Create a new directory and main.lua for this code.

display.setDefault('background', 1,1,1)
display.setDefault('anchorX', 0)
display.setDefault('anchorY', 0)
local width = display.contentWidth/4
local height = display.contentHeight/4
local X = display.screenOriginX
local Y = display.screenOriginY+display.topStatusBarContentHeight
local rect11 = display.newRect(X, Y, width, height)
rect11:setFillColor(0, 1, 0) --green
rect11:setStrokeColor(0, 0, 0) --black
rect11.strokeWidth=2
local rect23 = display.newRect(X+width*2, Y+height*1, width, height)
rect23:setFillColor(0, 1, 0) --green
rect23:setStrokeColor(0, 0, 0) --black
rect23.strokeWidth=2
local rect32 = display.newRect(X+width*1, Y+height*2, width, height)
rect32:setFillColor(1, 1, 1) --white
rect32:setStrokeColor(0, 0, 0) --black
rect32.strokeWidth=2

To create sixteen tiles requires sixteen variables and four statements each.  That is a lot of copying and editing.  One of the advanced features of Lua is functions with parameters.  The goal is to avoid repetitive typing of Lua statements that vary from one another in a stylized way.  Consider the following function definition.

A Function to Create Tiles


Create a new directory and main.lua for this code.

local function createTile(row, column)
  local r = display.newRect(X+width*(column-1), Y+height*(row-1), width, height)
   r:setFillColor(1, 1, 1) --white
   r:setStrokeColor(0, 0, 0) --black
   r.strokeWidth = 2
  r.green = false  --property we added to keep track of marked tiles
  return r
end --createTile

local rect11 = createTile(1, 1)  --repeat 16 times to end up with 16 lines
local rect12 = createTile(1, 2)
--local rect13 14 21 22 23 24 31 32 33 34 41 42 43 = createTile(1,3) (1,4)
local rect44 = createTile(4, 4)


As with game1, we "move" the tiles with a timer.  Actually, we do not move the tiles; we just move the colors.  Row one gets the colors from row two, row two from row three, row three from row four, and row four randomly gets a green tile.  We define a second function (setColor) to set the color of a tile to green or white.

Move Green from One Row to the Next
DO NOT type unless you understand it

local function setColor(rect, isGreen)
  if isGreen then rect:setFillColor(0,1,0) rect.green=true end
  if not isGreen then rect:setFillColor(1,1,1) rect.green=false end
end --setColor

local function moveTiles()
  rect11.green = rect21.green
  setColor(rect11, rect21.green)  --repeat for rect12 through rect34

  local rand = math.random(1,4)  --pick one random column in row 4
  rect41.green = rand == 1
  setColor(rect41, rand == 1)
  rect42.green = rand == 2
  setColor(rect42, rand == 2)
  rect43.green = rand == 3
  setColor(rect43, rand == 3)
  rect44.green = rand == 4
  setColor(rect44, rand == 4)
end --moveTiles

timer.performWithDelay(1500, moveTiles, 0)

Tiles

Just to be complete, each rectangle needs a "tap" listener.  They can all share the same event listener because all events have a property "target" that identifies the object upon which the event was generated.  Thus, "target" identifies the rectangle that was tapped.  Each rectangle has a "green" property that indicates whether it is marked. The code follows.

Listen for a Tile Tap
DO NOT type unless you understand the code.

local function onComplete()
  --dispose and delete stuff
end --onComplete

local function check( event )
  if event.target.green then
    native.showAlert( "Congratulations", "YOU WON!", { "OK" }, onComplete )
  end
  if not event.target.green then
    native.showAlert( "Sorry :(", "YOU LOST!", { "OK" }, onComplete )
  end
end -- check

rect11:addEventListener('tap', check) --must be repeated 16 times for rect12, rect13 etc

If you enjoyed this lesson and would like to learn more, check out my book and the much more challenging online lessons at professorcook.org.