Advertisement

Build a Groundhog Whack Game - Gameplay Logic

by

In this tutorial series we will create a Whack-a-Groundhog game. The objective of the game is tap on the groundhogs before they disappear. Read on!


Where We Left Off...

In the previous part of this series we added the game background and began animating our groundhogs. In this the final part of the series, we will implement the gameplay, options, and start screen of our app, and then end up with a complete game!


1. setupGameScreen()

As the game is now, the groundhogs animate forever and do not seem to go back inside their holes. We need to add some more keys to our sequenceData to fix this. Add the following within the setupGameScreen() function:

local sequenceData = {
    {name="show", start=2 , count = 3,time=800,loopCount=1,loopDirection="bounce"},
    {name = "blank",start=1,count=1},
    {name = "hit1",start=5,count=1},
    {name = "hit2", start=6,count=1},
    {name = "hit3", start=7,count=1}
}

The "blank" sequence is a blank transparent image which is part of the sprite sheet, and the "hit1", "hit2", and "hit3" sequences are 3 different "hit" states of the groundhog. Look at the "groundhogsheet.png" image to see this for yourself.

Make sure you set the loopCount equal to 1 on the "show" sequence.

Now change the following code block:

tempGroundHog:setSequence("blank")
tempGroundHog:addEventListener('tap', groundHogHit);

Here we have set the sequence to "blank" and have added a tap listener to the groundhog sprite.

Next, remove the following line of code:

tempGroundHog:play()

If you test now, none of the groundhogs should be animating. We will soon be getting a random groundhog animating!


2. groundHogHit()

When the groundhog is tapped we will determine whether or not it was out of its hole. If so, we will change its sequence to one of the three hit states that we added in the previous step.

Add the following inside the groundHogHit() function:

local thisSprite = e.target
thisSprite:removeEventListener( "sprite", groundHogSpriteListener ) 
    local function hide()
        thisSprite:setSequence("blank")
    end
    if(thisSprite.sequence == "show") then
        local randomIndex = math.random(3)
	thisSprite:setSequence("hit"..randomIndex)
	timer.performWithDelay(1000, hide) 
    end

Here we reference the Sprite that was clicked by e.target and remove its Event Listener. We then check if its sequence is equal to "show". If it is, we generate a number between 1 and 3 and set its sequence equal to "hit"..randomIndex. All this does is generate the strings "hit1", "hit2", or "hit3". Finally, we call the local hide function after 1000 milliseconds which sets the Sprites sequence to "blank".


3. getRandomGroundHog()

The getRandomGroundHog() function gets a random groundhog and allows it to begin animating. Enter the following inside the getRandomGroundHog() function:

local randomIndex = math.random(#allGroundHogs)
    local randomGroundHog = allGroundHogs[randomIndex]
    if(randomGroundHog.sequence ~="blank") then
         getRandomGroundHog()
    else
        randomGroundHog:addEventListener( "sprite",groundHogSpriteListener ) 
	randomGroundHog:setSequence("show")
	randomGroundHog:play()
    end

Here we get a randomIndex from the allGroundHogs table. Next, we set the randomGroundHog equal to the index. We then check if its sequence does not equal "blank", and, if it doesn't, we call getRandomGroundHog() again. Otherwise, we add the groundHogSpriteListener to set its sequence to "show" and play the sequence.


4. groundHogSpriteListener()

The groundHogSpriteListener() checks if the "show" sequence has finished playing. If so, it sets it to the "blank" sequence. Enter the following within the groundHogSpriteListener():

local thisSprite = event.target --"event.target" references the sprite
    if ( event.phase == "ended" ) then
        if(thisSprite.sequence == "show") then
            thisSprite:setSequence("blank")
        end -- a half second delay
    end

5. Animating Random GroundHogs

With all the above in place we get random groundhogs animating. Add the following to the bottom of the
setUpGameScreen() function:

groundHogTimer = timer.performWithDelay(groundHogSpeed, getRandomGroundHog,0)

If you test now you should see the groundhogs randomly popping out of their holes. While you are there, try clicking on the groundhogs while they are out of their holes. You should see one of the 3 hit states, and then the blank state.

When you are done testing, remove the line you just entered. We only needed it for testing.


6. setUpIntroScreen()

In this step we will begin to setup the intro screen. Enter the following code inside the setUpIntroScreen function:

media.playSound("gameTrack.mp3",soundComplete)
introScreenGroup = display.newGroup()
local titleScreen = display.newImage("titleScreen.png")
local playButton = display.newImage("playButton.png",100,100)
local optionsButton = display.newImage("optionsButton.png",100,170)
introScreenGroup:insert(titleScreen)
introScreenGroup:insert(playButton)
introScreenGroup:insert(optionsButton)

Here we start the soundtrack, setup the intro screen, and add the playButton and optionsButton graphics.

Now call the setUpIntroScreen() function beneath where you are calling the setUpGameScreen() function..

setUpGameScreen()
setUpIntroScreen()

If you test now you should see the Intro Screen. We need to add Event Listeners to the buttons and that is what we'll do in the next steps.


7. Play Button Listener

Enter the following beneath the code you entered in the step above:

playButton:addEventListener("tap",function ()
    transition.to(introScreenGroup, {time = 300, x = -480, onComplete = function() 
        groundHogTimer = timer.performWithDelay(groundHogSpeed, getRandomGroundHog,0) 
     end
     })
    isPlaying = true
end
)

When the play button is pressed, we animate the intro screen off to the left, set our groundHogTimer to generate random groundhogs, and then set the isPlaying variable to true.

If you test now, you should be able to start a new game, but we would like some options to be available as well. We'll do that next.


8. Options Button Listener

Enter the following beneath the code you entered in the step Above.

optionsButton:addEventListener("tap", function()
    transition.to(optionsScreenGroup, {time = 300, y = 0, onComplete = function() 
         introScreenGroup. x = -480
    end
    })
end
)

This code animates the option screen down from above. When the animation is complete, it puts the introScreenGroup off of the main screen to the left.

If you test now and press the options button you'll not see anything happen, and that is because we have not yet created the options screen.


9. setUpOptionsScreen()

Enter the following inside the setUpOptionsScreen() function:

optionsScreenGroup = display.newGroup()
    local optionsScreen = display.newImage("optionsScreen.png")
    local backButton = display.newImage("backButton.png",130,240)
    local soundOnText = display.newText( "Sound On/Off", 75,105, native.systemFontBold, 16 )
    local groundHogSpeedText = display.newText("Speed", 75,145,native.systemFontBold,16)
    optionsScreenGroup:insert(optionsScreen)
    optionsScreenGroup:insert(backButton)
    optionsScreenGroup:insert(soundOnText)
    optionsScreenGroup:insert(groundHogSpeedText)
    optionsScreenGroup.y = -325

Here we setup the optionsScreen, add the backButton, and add a couple of texts.

Now call this function beneath where you are calling setUpIntroScreen:

setUpIntroScreen()
setUpOptionsScreen()

If you test the game now you should see the options screen slide down from above.


10. soundCheckBox

We will be using the checkbox widget to allow the user to turn on/off the sound. Enter the following at the very top of the "main.lua" file.

local widget = require( "widget" )

To be able to use the Switch and other widgets we must first require the "widget" module

Now enter the following beneath the code you entered above in the setUpOptionsScreen.

local soundCheckBox = widget.newSwitch
{
   left = 210,
   top = 98,
   style = "checkbox",
   initialSwitchState = true,
   onPress = function(e)
   	local check = e.target
   	if(check.isOn) then
   		media.playSound("gameTrack.mp3",soundComplete)
   	else
   	  media.stopSound()
   	end
   end
}
optionsScreenGroup:insert(soundCheckBox)

This sets up our checkbox widget by setting "style" equal to "checkbox". We check if the checkbox isOn (if it is selected), and if so we play the "gameTrack.mp3". If not, we stop the sound.

If you test now and go to the options screen, you should be able to turn the sound on or off.


11. speedControl

We use a SegmentedControl to allow the user to choose how fast the groundHogs should appear. Enter the following beneath the code for the checkbox you entered in the step above:

local speedControl = widget.newSegmentedControl
{
   left = 210,
   top = 140,
   segments = { "slow", "medium", "fast"},
   segmentWidth = 50,
   defaultSegment = 1,
   onPress = function(event)
   	local target = event.target
        if(target.segmentNumber == 1)then
        	groundHogSpeed = 1500
        elseif (target.segmentNumber == 2)then
            groundHogSpeed = 1000
        else
        	groundHogSpeed = 700
        end
   end
}

optionsScreenGroup:insert(speedControl)

Here we create a SegmentedControl with 3 segments ("slow","medium","fast"). Depending on which segment the user has pressed we set the groundHogSpeed variable accordingly.

If you test now you should see the SegmentedControl and be able to select a segment, but we need to wire up the back button to get back to the intro screen.


12. backButton

The backButton takes us back to the intro screen. Enter the following code beneath the above step:

backButton:addEventListener("tap", function()
    if(isPlaying == false)then
        introScreenGroup.x = 0
     end
     transition.to(optionsScreenGroup, {time = 300, y = -325, onComplete = function() 
         if(isPlaying == true) then
	     groundHogTimer = 	timer.performWithDelay(groundHogSpeed, getRandomGroundHog,0) 
	  end
     end
     })
end
)

Here we check whether the game has started. If not, we put the introScreenGroup back into the playing area. We then transition the options screen back up. If the game has started, we set the groundHogtimer to generate random groundhogs.


13. setUpGameScreen() - Adding Options

We need to be able to get to the options screen from the main game screen. Add the following beneath where you are inserting the gameBackground into the gameScreenGroup().

gameScreenGroup:insert(gameBackground)
local optionsButton = display.newImage("optionsButton.png",1,270)
gameScreenGroup:insert(optionsButton)

If you test now you should see the options button on the game screen.

Now we need to wire up the options button with an EventListener. To do so, enter the following code beneath the loop that creates all the groundhogs.

optionsButton:addEventListener("tap", function(e)
    timer.cancel(groundHogTimer)
    transition.to(optionsScreenGroup, {time = 300, y = 0, onComplete = function() 

    end
    })
end
)

Here we cancel the groundHogTimer and transition our options screen down.

Testing now, you should be able to get to the options screen from the game screen. You should also be able to set the options as desired.


14. soundComplete()

If you have played long enough for the soundtrack to finish, you may have noticed it did not start over. Enter the following within the soundComplete() function.

media.playSound( "gameTrack.mp3", soundComplete )

This just starts the soundtrack playing again once it completes.


Conclusion

In this series, we learned how to create a fun and interesting Whack-a-Groundhog game. In doing so, you've learned how to utilize sprite sheets and sound. I hope you found this tutorial useful, and thanks for reading!