Part 8 – Grab the Treasure

Introduction to Mobile Game Development

Written July, 2015, by Rachel J. Morris

Creative Commons License Introduction to Mobile Game Development by Rachel J. Morris is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. Based on a work at http://www.moosader.com/learn/ccckc/introduction-to-mobile-game-development/.

Return to the main page


For this lesson, let’s start making a playable game! It will start out simple, but we will add more features over the next few lessons. This is how you write games, anyway! One step at a time!

Gameplay

For this version of the game, we’re going to have a background, and a “treasure” item. The treasure is placed randomly on the screen. The goal is to tap the treasure to gain points. If the player is too slow, the treasure will move to a new location after a few seconds.

Set up your project folder

Go ahead and create the project in Gideros. From the “Sample Graphics” folder, add the following files. I would suggest creating a “content” folder within your project directory, to keep things clean. :)

  • Under the Sample Files folder, there is a folder named Grab the Treasure sets. There are several folders with different themes, which includes a background.png, enemy.png, player.png, and treasure.png. Choose a set and add it to your project folder.
  • A “success” sound effect from the Sound folder.
  • A background song from the Music folder.
  • Add a font as well.

In Gideros, you can also create a folder in your project by right-clicking, and clicking New Folder. This is what my project looks like:

project

The art sets look like this:

sampleassetsb

Setting up the game

First, let’s just load all of our assets, draw everything to the screen, and play our background music.

To load in our images, we need to create Texture objects, which load a file name. Then, we can create Bitmap objects that will be displayed in the game.

treasure_texture = Texture.new( "content/treasure.png" )

As a shortcut, you can load the texture at the same time when you are loading your Bitmap. This is handy if you’re only going to use an image once, like for the background.

background = Bitmap.new( Texture.new( "content/background.png" ) )

For sounds, you just create a new Sound object, and load in a sound file.

For text, we need to create a TTFont object, and load in a font file and the font size we will be using. Then we can create a TextField object that actually stores the text that we’re going to draw. A new TextField needs the Font and it needs the text that will be displayed (which can be updated later).

-- Load our images
treasure_texture = Texture.new( "content/treasure.png" )
treasure = Bitmap.new( treasure_texture )
background = Bitmap.new( Texture.new( "content/background.png" ) )

-- Load sounds
pickupSound = Sound.new( "content/Pickup_Coin3.wav" )
music = Sound.new( "content/DancingBunnies_Moosader.mp3" )

-- Load text
font = TTFont.new( "content/Amburegul.ttf", 20 )
scoreText = TextField.new( font, "Score: 0" )
scoreText:setPosition( 40, 30 )
scoreText:setTextColor( 0x8d8d8d )

Remember that the stage is how we can draw our images out to the screen.

To draw Bitmap and TextField objects, we simply use the addChild function from the stage object.

-- Draw images
stage:addChild( background )
stage:addChild( treasure )

-- Draw text
stage:addChild( scoreText )

For sound effects, we can just call the :play function, but for music, we will want to store our music in a sound channel so we can modify it later (pause, change volume, or set up looping.)

-- Play music
musicChannel = music:play()
musicChannel:setLooping( true )

Now when you run the program, our images and text should be showing, and the music should be playing. Try it now.

Handling user input

When the player touches the screen, we want to see whether they’ve touched the treasure. If they’ve grabbed the treasure, we are going to add to their score, and move the treasure somewhere else.

stage can also be used to detect input using the event listener functionality.

stage:addEventListener( Event.MOUSE_DOWN, HandleClick, self )

We will need to write the HandleClick function now, otherwise our program will have an error when you try to run it.  Inside HandleClick, we will want to test to see whether the treasure was touched. To do this, we call the hitTestPoint function. hitTestPoint is a function that belongs to all Bitmap objects, and it needs an x and y coordinate. In this case, event.x and event.y will give us the coordinates of where the mouse clicked (or finger tapped).

Also remember that our HandleClick function needs to go ABOVE our stage:addEventListener function that is using the same function.

function HandleClick( event )
    if ( treasure:hitTestPoint( event.x, event.y ) ) then
        -- Collected treasure!
    end
end

So, what do we want to do when the player collects treasure? Well…

  1. Play a happy sound effect.
  2. Add to the score. (we’re going to have to make a variable for this!)
  3. Update our scoreText label.
  4. Put the treasure somewhere else.

Playing a sound effect is easy:

pickupSound:play()

but for changing the score, we’re going to first need a score variable!  At the beginning of the program, let’s make a score variable, and set it to 0.

-- outside of the HandleClick function, 
-- like when we're loading our assets.
score = 0

and then, inside of HandleClick, we can increment it like this:

score = score + 1

Then we can update the TextField‘s text with the :setText function. We can write the text string “Score: “, and to insert a variable afterwards, we need to use two periods .. in order to concatenate the score onto the label.

scoreText:setText( "Score: " .. score )

Now, finally, we want to put the treasure somewhere else. This is a great chance to make a new function to handle this for us, so we don’t have to write the same “move treasure” code over and over.  Let’s call this function RandomlyPlaceTreasure. We’ll write the function in a bit, but within our HandleClick function, we need to call it:

RandomlyPlaceTreasure()

So, our entire HandleClick function will look something like this:

function HandleClick( event )
    if ( treasure:hitTestPoint( event.x, event.y ) ) then
        pickupSound:play()
        score = score + 1
        scoreText:setText( "Score: " .. score )
        RandomlyPlaceTreasure()
    end
end

And let’s start on our next function:

function RandomlyPlaceTreasure()
    -- Change the X and Y coordinate of the treasure.
end

If you run the game at this point, we will be able to rack up a score easily because the treasure won’t move, but the sound and the score counter should work fine. Make sure to test before we move on! It’s much easier to fix bugs when you test frequently, than if you try testing after you’ve written a lot of code.

Moving the treasure

OK, so what does RandomlyPlaceTreasure need to do?

  1. Randomly generate an X coordinate.
  2. Randomly generate a Y coordinate.
  3. Set the treasure Bitmap’s position to that X and Y coordinate.

The Lua scripting language has a math library, which we can use to easily do math, like trigonometry, or getting a square root, but for now we’re only interested in the random function.

math.random( minimum, maximum )

With the math.random, we can generate a random number between the minimum and maximum value, which we will pass in when we call it.

Our app should be running at a resolution of 320×480.

local randomX = math.random( 0, 320 )
local randomY = math.random( 0, 480 )

First, note that using local before our variable name is another way to declare a variable – if we use local from inside of a function, this variable is only meant to be used from within the function, and cannot be used from elseware in the program.  Since it’s a temporary variable, let’s use local.

Secondly, if we generate random coordinates that can go up to 320 horizontally and 480 vertically, our treasure could be generated just off-screen. When we’re setting its (x, y) coordinate, this is the treasure graphic’s left-top corner. If the left side of the graphic is at the corner of the screen (at position 320), it will not appear. So, we will want to adjust our random coordinates:

local randomX = math.random( 0, 320 - 50 )
local randomY = math.random( 0, 480 - 50 )

I’ve chosen 50 here because our character and item sprites are 50 pixels wide by 50 pixels tall (if you’re using the art I’m providing). Once we have a random X and Y coordinate, we can then update our treasure Bitmap’s position:

treasure:setPosition( randomX, randomY )

Now when we tap the treasure, it will be relocated.  The RandomlyPlaceTreasure function should now look like this:

function RandomlyPlaceTreasure()
    local randomX = math.random( 0, 320 - 64 )
    local randomY = math.random( 0, 480 - 64 )
    treasure:setPosition( randomX, randomY )
end

Finally, when we start the game, we should call this function so the treasure starts at a random position. Otherwise, the treasure will always start in the same location.  Add a call to RandomlyPlaceTreasure at the very bottom of our code file, after all our functions have been created, and after the stage:addEventListener functions.

function RandomlyPlaceTreasure()
    local randomX = math.random( 0, 320 - 64 )
    local randomY = math.random( 0, 480 - 64 )
    treasure:setPosition( randomX, randomY )
end

stage:addEventListener( Event.MOUSE_DOWN, HandleClick, self )

-- Game Start
RandomlyPlaceTreasure()

Test out the game now. When it begins, it should have the treasure in a random location. When you tap the treasure, it should move to a new location, and your score should go up.

samplegame1

It runs OK, but this isn’t very game-y. After all, where’s the challenge?  As a final feature for this chapter, let’s add a timer. If you’re too slow to grab the treasure, it will move to a new location.

Countdown timer

Currently, our game only updates if the player taps the screen. We can use another event listener to update the game periodically, so the game will keep running even if the user isn’t tapping the screen. We can do this uUsing the Event.ENTER_FRAME event.

stage:addEventListener( Event.ENTER_FRAME, Update, self )

Then, inside of an Update function that we have to write ourselves, we can figure out what we want the game to do every frame.

First, let’s create a counter variable. Every frame, it will go down by 1, and if it reaches 0, we will move the treasure. Create the variable near where we created the score variable:

score = 0 
moveTreasureCounter = 100

Then, let’s create an Update function:

function Update( event )    
    -- Update Counter
    -- If counter is 0, move the treasure.
end

Our update function will do a couple things:

  1. Subtract 1 from the moveTreasureCounter variable.
  2. Check whether moveTreasureCounter is now equal to 0. If it is, call RandomlyPlaceTreasure. Otherwise, we won’t do anything else!

We can use subtraction on our variable like this:

moveTreasureCounter = moveTreasureCounter - 1

In the past, we’ve assigned variables with something like moveTreasureCounter = 100. This is just another way to set the variable – we’re setting it to the same variable, but less by 1.

Then, we will use an if then statement to see whether our counter has hit 0 yet. These statements look like this:

if ( moveTreasureCounter == 0 ) then
    -- Do something
end

In math, you might have compared numbers together with greater-than, less-than, greater-than-or-equal-to, equal-to, not-equal, and so on. We use the same thing in programming. We have to use different symbols, however, since we don’t have symbols like “not equal” or “less than or equal to”. So instead, we use symbols available to us on our keyboards:

Math Lua Description
lt < Less Than
gt > Greater Than
lte <= Less Than or Equal To
gte >= Greater Than or Equal To
equal == Equal To
notequal ~= Not Equal To

So, to check whether two numbers (or two text strings) are equal, we use two equal-signs: = =.  If moveTreasureCounter == 0, then we’re going to move our treasure.

function Update( event )    
    moveTreasureCounter = moveTreasureCounter - 1
    
    if ( moveTreasureCounter == 0 ) then
        RandomlyPlaceTreasure()
    end
end

Now, we’re going to have to make an update to our RandomlyPlaceTreasure function as well. Whenever we move the treasure, we should be resetting our counter:

function RandomlyPlaceTreasure()
    local randomX = math.random( 0, 320 - 64 )
    local randomY = math.random( 0, 480 - 64 )
    treasure:setPosition( randomX, randomY )
    
    moveTreasureCounter = math.random( 50, 200 )
end

I’m setting moveTreasureCounter to a random amount of time, between 50 and 200 frames. You can hard code this if you would like, or change the minimum and maximum values.

Now, the first version of our game should be good to go. Run it, check whether the treasure is moved to a new location after a certain period of time, and that you can still tap the treasure to gain score. All good?

Suggested changes

If you want to further customize your game, you might try some of these changes.

  • If you miss tapping on a treasure before the timer runs down, remove one point from the player’s score. If the player’s score becomes less than 0, then display a “game over” message, and use stage:removeChild to remove our treasure Bitmap.

Sample code is available on the GitHub page at:

https://github.com/Rachels-Courses/Intro-to-Mobile-Game-Development-with-Gideros

 

Print Friendly