In Creating an Ad Unit for Rewarded Ads, you set up a rewarded ad unit that serves video ads to Gloop Drop. The idea is that when players watch one of these ads, they’ll earn a reward: the ability to continue their game. At the moment, though, this Continue Game feature doesn’t yet exist, so you’ll need to build it, starting with a few tweaks to the UI.
Open the Assets.xcassets asset catalog and create a new sprite atlas named game_ui. Copy the three image files—[email protected], [email protected], and [email protected]—from the resources/game_ui folder into the newly created sprite atlas. (Don’t forget to delete the default Sprite image set.)
With the new sprite atlas in place, you’re ready to add a new sprite node. Open the GameScene.swift file and add the following property for the Start button sprite:
| // Start game button |
| let startGameButton = SKSpriteNode(imageNamed: "start") |
Next, you need to create a few methods to set up the sprite node and handle the actions to show and hide the button. Below the setupLabels() method, add the following new methods:
| func setupStartButton() { |
| startGameButton.name = "start" |
| startGameButton.setScale(0.55) |
| startGameButton.zPosition = Layer.ui.rawValue |
| startGameButton.position = CGPoint(x: frame.midX, y: frame.midY) |
| addChild(startGameButton) |
| |
| // Add animation |
| let scaleUp = SKAction.scale(to: 0.55, duration: 0.65) |
| let scaleDown = SKAction.scale(to: 0.50, duration: 0.65) |
| let playBounce = SKAction.sequence([scaleDown, scaleUp]) |
| let bounceRepeat = SKAction.repeatForever(playBounce) |
| startGameButton.run(bounceRepeat) |
| } |
| |
| func showStartButton() { |
| startGameButton.run(SKAction.fadeIn(withDuration: 0.25)) |
| } |
| |
| func hideStartButton() { |
| startGameButton.run(SKAction.fadeOut(withDuration: 0.25)) |
| } |
The actions in the first method give the Start button a sort of pulse-like animation. The fade actions in the other two methods keep the Start button from appearing too abruptly.
While you’re here, scroll down a little to the showMessage(_:) method. Locate the following line of code:
| messageLabel.position = CGPoint(x: frame.midX, y: player.frame.maxY + 100) |
and change it to this:
| messageLabel.position = CGPoint(x: frame.midX, |
| y: frame.midY + startGameButton.size.height/2) |
If you skip this change, the Start button and the message label would overlap—you can’t have that, now, can you?
The next step is to update the didMove(to:) method so that it calls the newly created setupStartButton() method.
Scroll up until you see the didMove(to:) method (or use the jump bar), and find the comment // Set up User Interface. Below that comment, and after the call to setupLabels(), add the following code:
| setupStartButton() |
Build and run the project and you’ll see the Start button pulsating just below the text.
You may have noticed that the Start button doesn’t work correctly. It also doesn’t disappear when you start a new game. It’s time to fix that.
In the spawnMultipleGloops() method, below the call to hideMessage(), add the following code:
| // Hide start button |
| hideStartButton() |
Now, in the gameOver() method, below the line that reads popRemainingDrops(), add this:
| // Show start button |
| showStartButton() |
That change takes care of calling the methods that show and hide the Start button. Now you need to update the touch method to handle when the player taps the button.
Locate the touchDown(atPoint:) method and update it to match the following:
| func touchDown(atPoint pos: CGPoint) { |
| let touchedNodes = nodes(at: pos) |
| for touchedNode in touchedNodes { |
| // print("touchedNode: (String(describing: touchedNode.name))") |
| if touchedNode.name == "player" && gameInProgress == true { |
| movingPlayer = true |
| } else if touchedNode == startGameButton && gameInProgress == false { |
| spawnMultipleGloops() |
| return |
| } |
| } |
| } |
This updated method simplifies how touches are handled and now includes a check to see if the touched node is the startGameButton object. If it is, you call spawnMultipleGloops(), and the game starts.
Build and run the project to check it out. If everything works, you’re ready to make the next UI update: adding the Continue and Watch Ad buttons.
In the Assets.xcassets asset catalog, create a new sprite atlas named continue_game. Copy all of the image files from the resources/continue_game folder into the newly created sprite atlas. (Don’t forget to delete the default Sprite image set.)
Now, open the GameScene.swift file and add the following properties:
| // Continue Game |
| let watchAdButton = SKSpriteNode(imageNamed: "watchAd") |
| let continueGameButton = SKSpriteNode(imageNamed: "continueRemaining-0") |
| let maxNumberOfContinues = 6 |
| var numberOfFreeContinues: Int = 0 |
| |
| var isContinue: Bool = false |
You’ll use these properties to create the new sprites and to help maintain the status of the player’s continues.
Next, you’ll be adding an extension to the GameScene class that handles the Continue Game functionality. Although this isn’t a requirement, it helps to keep things organized and focused.
Scroll all the way to the bottom of the GameScene.swift file and add the following block of code:
| extension GameScene { |
| func setupContinues() { |
| watchAdButton.name = "watchAd" |
| watchAdButton.setScale(0.75) |
| watchAdButton.zPosition = Layer.ui.rawValue |
| watchAdButton.position = CGPoint(x: startGameButton.frame.maxX + 75, |
| y: startGameButton.frame.midY - 25) |
| addChild(watchAdButton) |
| |
| continueGameButton.name = "continue" |
| continueGameButton.setScale(0.85) |
| continueGameButton.zPosition = Layer.ui.rawValue |
| continueGameButton.position = CGPoint(x: frame.maxX - 75, |
| y: viewBottom() + 60) |
| addChild(continueGameButton) |
| |
| updateContinueButton() |
| } |
| |
| func updateContinueButton() { |
| if numberOfFreeContinues > maxNumberOfContinues { |
| let texture = SKTexture(imageNamed: "continueRemaining-max") |
| continueGameButton.texture = texture |
| } else { |
| let texture = SKTexture(imageNamed: |
| "continueRemaining-(numberOfFreeContinues)") |
| continueGameButton.texture = texture |
| } |
| } |
| } |
There’s a lot going on here, but nothing you haven’t seen before.
Finally, in the in the didMove(to:) method, below the line that reads setupStartButton(), add a call to setupContinues(), like so:
| setupContinues() |
Build and run the project and you’ll see two new buttons, as shown in the image.
Excellent, you’re ready to implement the methods that handle continuing the game.
With the GameScene.swift file still open, go to the GameScene extension at the bottom of the file. Below the updateContinueButton() method, add the following new method:
| func useContinue() { |
| if numberOfFreeContinues > 0 { |
| isContinue = true |
| numberOfFreeContinues -= 1 |
| spawnMultipleGloops() |
| } |
| } |
You’ll call this new method when the player taps the Continue button. Notice that it first checks to make sure the player has at least one continue before setting isContinue = true and calling spawnMultipleGloops(). It also decrements the numberOfFreeContinues by 1.
Now, jump to the spawnMultipleGloops() method and locate the following code:
| // Reset the level and score |
| if gameInProgress == false { |
| score = 0 |
| level = 1 |
| } |
and replace it with this:
| // Reset the level and score |
| if gameInProgress == false && isContinue == false { |
| score = 0 |
| level = 1 |
| } else { |
| isContinue = false |
| } |
Thanks to this change, when the player continues the game, the score and level are not reset.
You now need to update how touches are handled. Jump to the touchDown(atPoint:) method and update it to match this:
| func touchDown(atPoint pos: CGPoint) { |
| let touchedNodes = nodes(at: pos) |
| for touchedNode in touchedNodes { |
| // print("touchedNode: (String(describing: touchedNode.name))") |
| if touchedNode.name == "player" && gameInProgress == true { |
| movingPlayer = true |
| } else if touchedNode == watchAdButton && gameInProgress == false { |
| // TODO: Add call to gameSceneDelegate?.showRewardVideo() |
| return |
| } else if touchedNode == continueGameButton && gameInProgress == false { |
| useContinue() |
| return |
| } else if touchedNode == startGameButton && gameInProgress == false { |
| spawnMultipleGloops() |
| return |
| } |
| } |
| } |
Finally, give the player one free continue. Go back to the properties section and modify the numberOfFreeContinues property to match this:
| var numberOfFreeContinues: Int = 1 { |
| didSet { |
| updateContinueButton() |
| } |
| } |
This update gives the player one free continue and automatically calls the updateContinueButton() method whenever the value changes. The updateContinueButton() is what is responsible for changing the texture of the Continue button sprite to match the number of continues available.
Build and run the project. Score a few points and then lose the game. Instead of starting a new game, tap the Continue button to keep playing.
Update the Message Text | |
---|---|
At this point, you may want to update the calls to showMessage(_:) to make them more in line with the UI updates. For example, showMessage("Game Over Start a New Game or Continue") in gameOver() and showMessage("Tap Start to Play the Game") in didMove(to:). |
Now that you’ve got the Continue Game functionality in place and working, you’re ready to implement the rewarded ads—and for that, you’ll use the AdMob helper file again.