Squeeze Out a Little More Juice

Before wrapping up this chapter, you’re going to squeeze a little more juice out of the game. In this section, you’ll tweak the drop pattern of the gloop drops for better gameplay, add juice to the catch and miss routines, give Blob a little personality, and finally, add the banner to the game.

Fixing the Drop Pattern

You may have noticed that when the drops fall from the sky, more often than not the previous drop is too far from the next drop, which makes it nearly impossible to catch everything. While you could argue that this impossibility adds to the game’s challenge, it more than likely will lead to frustration instead. To fix this drop pattern and make it follow more of a snake-like pattern, you’ll use a little math and some range control.

First, you need to add a new property to hold the previous drop’s location.

In the GameScene.swift file, add the following new property:

 var​ prevDropLocation: ​CGFloat​ = 0.0

Next. locate the spawnGloop() method and change the randomX property from a constant (let) to a variable (var). In other words, change this line:

 let​ randomX = ​CGFloat​.​random​(in:
  dropRange.lowerLimit...dropRange.upperLimit)

to this:

 var​ randomX = ​CGFloat​.​random​(in:
  dropRange.lowerLimit...dropRange.upperLimit)

You need to switch randomX from a constant to a variable because you’ll be changing its value based on the previous drop location, and you can’t change the value of a constant once it’s been set.

Below the line you just modified, add the following code:

 /* START ENHANCED DROP MOVEMENT
  this helps to create a "snake-like" pattern */
 
 // Set a range
 let​ randomModifier = ​SKRange​(lowerLimit: 50 + ​CGFloat​(level),
  upperLimit: 60 * ​CGFloat​(level))
 var​ modifier = ​CGFloat​.​random​(in:
  randomModifier.lowerLimit...randomModifier.upperLimit)
 if​ modifier > 400 { modifier = 400 }
 
 // Set the previous drop location
 if​ prevDropLocation == 0.0 {
  prevDropLocation = randomX
 }
 
 // Clamp its x-position
 if​ prevDropLocation < randomX {
  randomX = prevDropLocation + modifier
 } ​else​ {
  randomX = prevDropLocation - modifier
 }
 
 // Make sure the collectible stays within the frame
 if​ randomX <= (frame.minX + margin) {
  randomX = frame.minX + margin
 } ​else​ ​if​ randomX >= (frame.maxX - margin) {
  randomX = frame.maxX - margin
 }
 
 // Store the location
 prevDropLocation = randomX
 
 /* END ENHANCED DROP MOVEMENT */

A lot of stuff is going on with this code, but it’s not as complicated as it may seem. Essentially, what you’re doing here is clamping (restricting) the randomness to keep the drops within a certain range of each other, while also keeping them within the scene’s visible frame.

Build and run the project and notice how much smoother the drop pattern is and how it now follows a snake-like pattern.

Adding Numbers to the Drops

Another nice tweak for the drop pattern is to add the current number of the drop. For that, you’ll add a child label node to the drop sprite node.

Still inside the spawnGloop() method, and below the line that reads /* END ENHANCED DROP MOVEMENT */, add the following code:

 // Add the number tag to the collectible drop
 let​ xLabel = ​SKLabelNode​()
 xLabel.name = ​"dropNumber"
 xLabel.fontName = ​"AvenirNext-DemiBold"
 xLabel.fontColor = ​UIColor​.yellow
 xLabel.fontSize = 22.0
 xLabel.text = ​"​​(​numberOfDrops​)​​"
 xLabel.position = ​CGPoint​(x: 0, y: 2)
 collectible.​addChild​(xLabel)
 numberOfDrops -= 1 ​// decrease drop count by 1

When you add a child node, keep in mind that its position is relative to its parent, which is why you’re using xLabel.position = CGPoint(x: 0, y: 2). This position places the label node in the center and slightly above its parent.

Build and run the project again and you’ll see little yellow labels above each drop. Watch as they count down to 1.

images/JuicingYourGamesWithSoundAndEffects/spritekit-build-03.png

Tweaking the Catch Routine

Those labels above the drops look really neat, don’t they? Let’s also add a label node above the player sprite node. This label node will show up when the player catches a drop, but you won’t add it as a child node of the player sprite node. Instead, you’ll add it directly to the scene.

In the GameScene.swift file, locate the didBegin(_:) method. After the line that reads checkForRemainingDrops(), add the following code:

 // Add the 'chomp' text at the player's position
 let​ chomp = ​SKLabelNode​(fontNamed: ​"Nosifer"​)
 chomp.name = ​"chomp"
 chomp.alpha = 0.0
 chomp.fontSize = 22.0
 chomp.text = ​"gloop"
 chomp.horizontalAlignmentMode = .center
 chomp.verticalAlignmentMode = .bottom
 chomp.position = ​CGPoint​(x: player.position.x, y: player.frame.maxY + 25)
 chomp.zRotation = ​CGFloat​.​random​(in: -0.15...0.15)
 addChild​(chomp)
 
 // Add actions to fade in, rise up, and fade out
 let​ fadeIn = ​SKAction​.​fadeAlpha​(to: 1.0, duration: 0.05)
 let​ fadeOut = ​SKAction​.​fadeAlpha​(to: 0.0, duration: 0.45)
 let​ moveUp = ​SKAction​.​moveBy​(x: 0.0, y: 45, duration: 0.45)
 let​ groupAction = ​SKAction​.​group​([fadeOut, moveUp])
 let​ removeFromParent = ​SKAction​.​removeFromParent​()
 let​ chompAction = ​SKAction​.​sequence​([fadeIn, groupAction, removeFromParent])
 chomp.​run​(chompAction)

This code creates a label node and then runs some actions to fade in the label, move it upward, and then fade it out. Alternatively, you could have created a new method with this code and then called that new method.

Build and run the project and watch as little labels that read “gloop” appear and disappear as you catch the drops.

images/JuicingYourGamesWithSoundAndEffects/spritekit-build-04.png

Tweaking the Miss Routine

Have you noticed that when you miss a drop, its landing feels a little stiff? Let’s tweak the miss routine to give the drops a little squishiness when they hit the platform.

Open the Collectible.swift file and update the missed() method to match this:

 func​ ​missed​() {
 let​ move = ​SKAction​.​moveBy​(x: 0, y: -size.height/1.5, duration: 0.0)
 let​ splatX = ​SKAction​.​scaleX​(to: 1.5, duration: 0.0) ​// make wider
 let​ splatY = ​SKAction​.​scaleY​(to: 0.5, duration: 0.0) ​// make shorter
 
 let​ actionGroup = ​SKAction​.​group​([playMissSound, move, splatX, splatY])
 self​.​run​(actionGroup)
 }

This code uses two scale actions to squish the sprite node, giving it a more realistic appearance when it hits the platform.

images/JuicingYourGamesWithSoundAndEffects/spritekit-build-05.png

Making Blob Mumble

You’re getting down to the wire now. Let’s give Blob some personality and make him randomly mumble.

Open the Player.swift file, and above the walk() method, add the following new method, which you’ll use to make Blob mumble:

 func​ ​mumble​() {
 let​ random = ​Int​.​random​(in: 1...3)
 let​ playSound = ​SKAction​.​playSoundFileNamed​(​"blob_mumble-​​(​random​)​​"​,
  waitForCompletion: ​true​)
 self​.​run​(playSound, withKey: ​"mumble"​)
 }

This code uses a random number between 1 and 3 to load one of the blob_mumble-?.wav audio files, where ? represents the number 1, 2, or 3. It then uses an action to play the sound.

With the new method in place, you’re ready to use it. Open the GameScene.swift file and locate the spawnMultipleGloops() method. Above the line that reads player.walk(), add the following line of code that calls the newly created method:

 player.​mumble​()

Build and run the project—and let’s get ready to mumble. (I hope you read that in your best announcer voice.)

Adding the Banner

Your main game scene is just about complete, but it’s missing something: a banner with the game’s name.

Select the Assets.xcassets asset catalog and drag the three files, [email protected], [email protected], and [email protected] from the resources/Images folder into the root of the Assets.xcassets asset catalog to create a banner image set, which you’ll use for the banner.

Now, open the GameScene.swift file and locate the didMove(to:) method. Below the line that reads addChild(foreground), add the following code to get the banner added to the scene:

 // Set up the banner
 let​ banner = ​SKSpriteNode​(imageNamed: ​"banner"​)
 banner.zPosition = ​Layer​.background.rawValue + 1
 banner.position = ​CGPoint​(x: frame.midX, y: ​viewTop​() - 20)
 banner.anchorPoint = ​CGPoint​(x: 0.5, y: 1.0)
 addChild​(banner)

Build and run the project and you’ll see the new banner located at the top of the screen in the center.

images/JuicingYourGamesWithSoundAndEffects/spritekit-build-06.png

Making the Drops Radioactive

The SKEffectNode is a class not many SpriteKit developers use. While this class can render its children into a separate buffer and apply some elegant node effects using Core Image filters, the rumors about its poor performance, coupled with its complexity, keep some developers from using it. For these reasons, you won’t spend too much time learning about SKEffectNode objects in this book.

Open the Collectible.swift file, and at the end of the init(collectibleType:) method, add the following code:

 // Add glow effect
 let​ effectNode = ​SKEffectNode​()
 effectNode.shouldRasterize = ​true
 addChild​(effectNode)
 effectNode.​addChild​(​SKSpriteNode​(texture: texture))
 effectNode.filter = ​CIFilter​(name: ​"CIGaussianBlur"​,
  parameters: [​"inputRadius"​:40.0])

This code creates an SKEffectNode object and sets its shouldRasterize property to true. When you set this property to true, you generally gain a bump in performance as it caches the filtered image; however, with a cached image, you’ll use more memory and take a hit with the initial render time. You then add the new effect node to the collectible sprite. But because effect nodes operate on their child nodes, you also need to create a new sprite node on which to apply the effects—in this case, the CIGaussianBlur effect.

Build and run the project and you will see a faint glow around the top of each drop.

images/JuicingYourGamesWithSoundAndEffects/spritekit-build-07.png

This was just a small taste of what effect nodes can accomplish. For more information on the SKEffectNode class,[34] the CIFilter class,[35] and Core Image,[36] refer to Apple’s online documentation.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset