How To Make a Letter / Word Game with UIKit and Swift: Part 3/3

This third and final part of the series will be the most fun of them all! In this part, you’re going to be adding a lot of cool and fun features By Caroline Begbie.

Leave a rating/review
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

Ladies & Gentlemen – Explosions!

Gamers love explosions. Making them at least – cool gamers don’t look at explosions and just keep on playing :]

Tough luck, though, as UIKit does not have explosions – right? Guess again. If you think that, then you probably haven’t read iOS 5 by Tutorials, which includes a little chapter on particle effects with UIKit.

An abbreviated version of that chapter is also available online. Check that out for more details on particle effects since there isn’t enough room here to go into it.

Your Xcode starter project already includes a particle image file. Drill down in the file explorer to Anagrams/Assets/Particles, find particle.png and open it.

As you can see, there’s nothing fancy about the image – it’s just a white blur in fact, but that’s all it takes to create an explosion.

A particle image

You will now create two particle systems to use in the game.

Add a new Swift file in Anagrams/Classes/Views called ExplodeView.swift.

As the name hints, this system will emulate an explosion. From a given point in space, many particles will accelerate in all directions, until they disappear quite fast. Like so:



Believe it or not, this will be quite easy. To begin, inside ExplodeView.swift, add the following:

import UIKit

class ExplodeView: UIView {
  //1
  private var emitter:CAEmitterLayer!
  
  //2 configure the UIView to have an emitter layer
  override class func layerClass() -> AnyClass {
    return CAEmitterLayer.self
  }
}
  1. emitter is a convenience property used to give you access to the UIView‘s layer property, cast as a CAEmitterLayer.
  2. layerClass() is a class method of UIView. You override this method when you want to change the underlying layer of your view class. Here you return the CAEmitterLayer class and UIKit ensures that self.layer returns an instance of CAEmitterLayer rather than the default CALayer.

All right, you’re ready to start configuring the particle system. Create the initializers for the class:

required init(coder aDecoder:NSCoder) {
  fatalError("use init(frame:")
}

override init(frame:CGRect) {
  super.init(frame:frame)
    
  //initialize the emitter
  emitter = self.layer as! CAEmitterLayer
  emitter.emitterPosition = CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2)
  emitter.emitterSize = self.bounds.size
  emitter.emitterMode = kCAEmitterLayerAdditive
  emitter.emitterShape = kCAEmitterLayerRectangle
}

The above code stores a reference to self.layer in emitter, cast to type CAEmitterLayer because you know that’s what it is. (Remember, you overrode layerClass() to make it so.) Having the correct type makes it easier to manipulate your layer later on.

Then the code sets various properties on the emitter layer. Read through the code and if in doubt about a certain CAEmitterLayer property, consult the Apple docs or our particle systems tutorial.

The settings in init(frame:) only adjust the particle emitter layer. To actually start emitting particles, you need an emitter cell. You will create and configure your object’s emitter cell at the point when the view is added into the view controller’s hierarchy – that is, as soon as you add the view to a parent view.

Add the following implementation of didMoveToSuperview(), which is the method that gets called on a UIView object when it’s added to its parent:

override func didMoveToSuperview() {
  //1
  super.didMoveToSuperview()
  if self.superview == nil {
    return
  }
 
  //2
  let texture:UIImage? = UIImage(named:"particle")
  assert(texture != nil, "particle image not found")
    
  //3
  let emitterCell = CAEmitterCell()
    
  //4
  emitterCell.contents = texture!.CGImage
    
  //5
  emitterCell.name = "cell"
    
  //6
  emitterCell.birthRate = 1000
  emitterCell.lifetime = 0.75
    
  //7
  emitterCell.blueRange = 0.33
  emitterCell.blueSpeed = -0.33
    
  //8
  emitterCell.velocity = 160
  emitterCell.velocityRange = 40
    
  //9
  emitterCell.scaleRange = 0.5
  emitterCell.scaleSpeed = -0.2
    
  //10
  emitterCell.emissionRange = CGFloat(M_PI*2)
    
  //11
  emitter.emitterCells = [emitterCell]
}

That’s quite a bit of code. Let’s go through it one step at a time:

  1. Call the parent class’s implementation of didMoveToSuperview() and then exit the method if there is no superview set on this object. This might happen if the object was removed from its parent.
  2. Load the particle.png image into a UIImage instance. There’s another one of those assert statements that crashes the app if it can’t find the file it’s looking for.
  3. Create a new emitter cell. Most of the rest of the function is spent configuring this object.
  4. Set the cell’s contents property to the texture you loaded. This is the image that will be used to create each of the particles that this cell will emit. The final shape and color of the particle will be determined by the other settings you make in this method.
  5. Name the cell “cell”. The name will be used later to modify the emitter layer’s properties via key-value coding. You can check out Apple’s docs if you want to learn more about key-value coding.
  6. Set the cell’s birthRate property to 1000, which tells it to create 1000 particles per second. You also set the cell’s lifetime property to 0.75, which makes each particle exist for 0.75 seconds.
  7. Here you set the cell’s color to randomly vary its blue component. Setting blueRange to 0.33 will get you particles with random colors between [1,1,1] (rgb) and [1,1,0.67] (rgb) – basically anywhere between white and orange. You also set a negative blueSpeed – this will decrease the blue component of the blending over time, decreasing the intensity of the particle’s color.
  8. The birth velocity of the particles has a base value of 160 and a range of 40, so the actual velocity will be anywhere between 120 and 200.
  9. The particles are emitted by default with a scale of 1.0. Therefore, setting a range of 0.5 will get you random particles from 0.5 to 1.5 times their original size. Finally you set a negative speed for the scale, thus continuing to shrink the particles over time. Not much shrinking will happen over a 0.75-second lifetime, but it for sure adds to the effect.
  10. Here you set a range (an angle) for the direction the emitter will emit the cells. You set it to a range of 360 degrees – that is, to randomly emit particles in all directions. Remember, this function takes its value in radians, not degrees, so 2*pi radians.
  11. Finally, you add the cell you created to the emitter layer. emitterCells is an array of CAEmitterCells for this emitter. (You can have more than one.)

Aaaaand you’re done. :]

You have now an awesome explosion view. Now add it behind a tile that has been dropped onto a proper target. Open up GameController.swift and add this code to the end of placeTile(_:targetView:):

let explode = ExplodeView(frame:CGRectMake(tileView.center.x, tileView.center.y, 10,10))
tileView.superview?.addSubview(explode)
tileView.superview?.sendSubviewToBack(explode)

You make a new instance of ExplodeView, positioned at the center of the dropped tile view, and add it to the gameView. To make sure it does not cover the tile, you also call sendSubviewToBack() to make the explosion happens behind the tile.

Go! Go! Go! Build and run the project! Wowza!

Particle effect

That wasn’t that difficult, right? However, the effect looks much more like a gas leak than a single explosion. And also – it never stops!

What you would like to do now is kill the emitter after a second to make it look more like an explosion.

Go back to ExplodeView.swift and add a method to stop the emitter cell.

func disableEmitterCell() {
  emitter.setValue(0, forKeyPath: "emitterCells.cell.birthRate")
}

This is that key-value coding I mentioned earlier. The above string accesses the birthRate property of the object named cell that is contained in the array returned from the emitterCells property. By instructing the cell to emit 0 particles per second, you effectively turn it off. Nice.

Now add the following to the end of didMoveToSuperview():

//disable the emitter
var delay = Int64(0.1 * Double(NSEC_PER_SEC))
var delayTime = dispatch_time(DISPATCH_TIME_NOW, delay)
dispatch_after(delayTime, dispatch_get_main_queue()) {
  self.disableEmitterCell()
}
    
//remove explosion view
delay = Int64(2 * Double(NSEC_PER_SEC))
delayTime = dispatch_time(DISPATCH_TIME_NOW, delay)
dispatch_after(delayTime, dispatch_get_main_queue()) {
  self.removeFromSuperview()
}

Here you are using Grand Central Dispatch to dispatch blocks to disable the emitter and remove it from its superview. First you schedule a delay of 1/10th of a second to disable the emitter, and after a delay of 2 seconds, you make another call to remove the explosion view from its parent. Why not just remove the view? You want to let the exploded particles fly away and dissolve before killing the effect.

Build and run the game again and enjoy some nice explosions!


Note: If you want to create your own particle systems, check out original author Marin’s UIEffectDesigner app, which allows you to visually design your particle system effects and then show them onscreen with just a couple of lines of code. It’s still an early beta, but if you are interested in creating particle systems for UIKit or AppKit, give it a try.

Now you’re going to add one more effect. It’s quite similar to ExplodeView, but involves a bit of gravity, since this effect will be a long lasting emitter rather than a simple explosion.

Create a new Swift file called StardustView in Anagrams/Classes/Views.

Add to the end of StardustView.swift:

import UIKit

class StardustView:UIView {
  private var emitter:CAEmitterLayer!
  
  required init(coder aDecoder:NSCoder) {
    fatalError("use init(frame:")
  }

  override init(frame:CGRect) {
    super.init(frame:frame)
    //initialize the emitter
    emitter = self.layer as! CAEmitterLayer
    emitter.emitterPosition = CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2)
    emitter.emitterSize = self.bounds.size
    emitter.emitterMode = kCAEmitterLayerAdditive
    emitter.emitterShape = kCAEmitterLayerRectangle
  }
  
  override class func layerClass() -> AnyClass {
    //configure the UIView to have emitter layer
    return CAEmitterLayer.self
  }
  
  override func didMoveToSuperview() {
    super.didMoveToSuperview()
    if self.superview == nil {
      return
    }
    
    //load the texture image
    let texture:UIImage? = UIImage(named:"particle")
    assert(texture != nil, "particle image not found")
    
    //create new emitter cell
    let emitterCell = CAEmitterCell()
    emitterCell.contents = texture!.CGImage
    emitterCell.name = "cell"
    emitterCell.birthRate = 200
    emitterCell.lifetime = 1.5
    emitterCell.blueRange = 0.33
    emitterCell.blueSpeed = -0.33
    emitterCell.yAcceleration = 100
    emitterCell.xAcceleration = -200
    emitterCell.velocity = 100
    emitterCell.velocityRange = 40
    emitterCell.scaleRange = 0.5
    emitterCell.scaleSpeed = -0.2
    emitterCell.emissionRange = CGFloat(M_PI*2)
    
    let emitter = self.layer as! CAEmitterLayer
    emitter.emitterCells = [emitterCell]
  }
  
  func disableEmitterCell() {
    emitter.setValue(0, forKeyPath: "emitterCells.cell.birthRate")
  }
}

As you can see, the code is almost identical to ExplodeView. The only differences are a longer lifetime for the particles, and values set to xAcceleration and yAcceleration to give acceleration to the particles. This is the way to simulate gravity in your particle systems.

This new particle will emit sparkles for as long as it exists, like a lit fuse on a stick of dynamite.

You’re going to animate this effect to go through the screen behind the solved puzzle!

Switch to GameController.swift and create the animation at the end of checkForSuccess():

// win animation
let firstTarget = targets[0]
let startX:CGFloat = 0
let endX:CGFloat = ScreenWidth + 300
let startY = firstTarget.center.y

This piece of code grabs the very first target on the screen and captures its y-coordinate. You also prepare two constants with the start and end x-coordinates.

Add the next bit of code right after what you just added:

let stars = StardustView(frame: CGRectMake(startX, startY, 10, 10))
gameView.addSubview(stars)
gameView.sendSubviewToBack(stars)

This actually creates the effect and adds it to the view, behind everything else.

Add the following after that:

UIView.animateWithDuration(3.0,
      delay:0.0,
      options:UIViewAnimationOptions.CurveEaseOut,
      animations:{
        stars.center = CGPointMake(endX, startY)
      }, completion: {(value:Bool) in
        //game finished
        stars.removeFromSuperview()
      })

Here you fire off a UIKit animation that moves the effect position through the screen and removes the particle effect from its parent when it’s complete. The following image shows the path it will take:

Animation Path

Build and run the project, and try solving a puzzle. When you do, you’ll see the emitter going through the screen spreading sparkles all over the place. :]

The following screenshot shows it in action, but you really need to see it moving to get the full effect:

Sparkles

That’s some Sparkle Motion! You can refine the effect further by making it follow different paths, zooming in and out, and so on, as you wish.

Contributors

Over 300 content creators. Join our team.