SceneKit Tutorial with Swift Part 5: Particle Systems

Chris Language

Thumb

Welcome back to our Scene Kit Tutorial with Swift series!

This tutorial series will show you how to create your first game with Scene Kit, Apple’s built-in 3D game framework.

In the first part of the series, you learned how to make an empty Scene Kit project as a good starting point.

In the second part of the series, you started making your game, learning about Scene Kit nodes along the way.

In the third part of the series, you learned how to make your geometry move through the power of Scene Kit physics.

In the fourth part of the series, you learned how to make your geometry spawn over time through the Scene Kit render loop.

In this fifth and final part of the series, you’ll add some cool particle systems to the game and wrap it up along the way! :]

Let’s dive back in!

Note: This tutorial begins where the previous tutorial left off. If you didn’t follow along, no sweat – you can simply use the starter project for this tutorial.

Getting Started

Picture yourself in the movie theatre, popcorn in hand; on the screen, a bad guy from The Fast and the Furious smashes his roaring, high-performance dragster into a highly volatile fuel tanker which explodes in a massive fireball of death and carnage. Yeah! :]

Now, think of that same scene, but without the massive fireball explosion. You can almost feel the collective disappointment of audiences around the world. :[

Just like a Hollywood blockbuster, your game needs special effects to amp up the excitement level. These special effects come in the form of what’s known as particle systems. You can use particle systems for a multitude of effects, from moving star fields, to burning rocket boosters, to rain and snow, to metal sparks — and yes, even massive exploding fireballs!

It’s time to take a look at how you can add some of these neat special effects to Geometry Fighter.

Particle Systems

In SceneKit, SCNParticleSystem manages the creation, animation and removal of particles from the scene graph.

A particle is essentially a small image sprite. The particle system doesn’t add the individual particles into the scene graph itself, so you don’t have direct access to each particle; the particle system manages the particles, including their look, size and location.

However, you can influence the particle system by modifying various properties on it, such as:

  • Appearance: Each particle of the system can be rendered as a single image, or an animated sequence of images. You can adjust the size, tint color, blending mode and other rendering parameters of the particles generated.
  • Life Span: The system uses a particle emitter, which gives birth to each individual particle. The lifespan of the particle determines how long it stays visible in the scene.
  • Emitter behavior: You can control various parameters of the emitter, such as where particles spawn and the spawn rate.
  • Variation: Introducing variations into your particle system can make it look more, or less, random.
  • Movement: You can adjust how particles move once they’ve spawned. Particles use a simplified physics simulation to speed up performance, but the particles can still interact with objects managed by the physics engine.

Creating a Trail Particle System

Before you add a particle system to your game world, you’ll need a group to house this particle system to keep your project organized. Right-click on the GeometryFighter group and select New Group, like so:
CreateParticlesGroup0

Name this new group Particles. Right-click on the group and select New File. Select the iOS/Resource/SceneKit Particle System template and click Next to continue:

AddParticleSystem0

On the next screen, select Fire for Particle system template, then click Next. Save the file as Trail.scnp and click Create. Once you’re done, you should see the following in your scene:

AddParticleSystem1

Hot stuff! :] Say “hello” to Xcode’s built-in particle system editor.

Here’s a quick overview of the various sections of the editor as annotated above:

  1. Center stage: The center holds a visual representation of your particle system. You can use this to get an idea of what the end result will look like.
  2. Gesture controls: You can use gestures to manipulate the camera view; it’s similar to how you’d move the camera around in a scene.
  3. Pause/Play button: You can pause your particle system simulation and inspect it in more detail. While paused, the pause button changes to a play button which you can use to resume the simulation.
  4. Restart button: This lets you restart your particle simulation from the beginning.
  5. Camera reset button: Use this to reset your camera view to its default position.
  6. Color button: This lets you set an appropriate background color for your editor; for example, it’s easier to see snowflakes against a black background.
  7. Particle system properties: Selecting the Attributes Inspector reveals a host of properties which you’ll learn about in the next section.

Configuring the Trail Particle System

In this section, you’ll take an in-depth look at the particle system attributes on the right-hand side of the editor. As you go through each section, copy the values of each setting in the screenshots into your own particle system.

Keep an eye on the particle system editor as you change each attribute; you’ll see how each parameter affects the behavior of the particle system. Later on, you’ll use this particle effect in your game to create a trail of particles falling from your spawned objects.

Emitter Attributes

The particle emitter is the origin from where all particles spawn. Here are the emitter attributes:

TrailParticleSettings0

  • Birth rate: Controls the emission rate of particles. Set this to 25, instructing the particle engine to spawn new particles at a rate of 25 particles per second.
  • Warmup duration: The amount of seconds the simulation runs before it renders particles. This can be used to display a screen full of particles at the start, instead of waiting for the particles to fill the screen. Set this to 0 so that simulation can be viewed from the very beginning.
  • Location: The location, relative to the shape, where the emitter spawns its particles. Set this to Vertex, which means the particles will use the geometry vertices as spawning locations.
  • Emission space: The space where emitted particles will reside. Set this to World Space so that the emitted particles are emitted into the world space, and not the local space of the object node itself.
  • Direction mode: Controls how spawned particles travel; you can move them all in a constant direction, have them travel radially outwards from the surface of the shape or simply move them in random directions. Set it to Constant, keeping all emitted particles moving in a constant direction.
  • Direction: Specifies an initial directional vector to use when direction mode is constant. Set this vector to (x: 0, y: 0, z:0), setting the direction to nothing.
  • Spreading angle: Randomizes the emitting angle of spawned particles. Set this to , thus emitting particles exactly in the previously set direction.
  • Initial angle: The initial angle at which to emit particles. Set this to as this does not matter with a zero direction vector.
  • Shape: The shape from which to emit particles. Set the shape up as a Sphere, thus using a sphere shape as the geometry.
  • Shape radius: The existence of this attribute depends on which shape you’re using; for an spherical emitter, this determines the size of the sphere. Set this to 0.2, which defines a sphere just large enough for what you need.
Note: Note that some of the attributes have two input areas, one of which has a Δ= symbol next to it (see Birth Rate and Initial angle). The first input area contains the base value, and the Δ= input area contains the delta value. Each time a particle is spawned, it uses the base value plus a random value in the range (-delta value, +delta value). This allows you to get some random variance for these properties.

Simulation Attributes

The simulation attributes manage the motion of particles over their lifetimes. This lets you manage their movement without having to use the physics engine:

TrailParticleSettings1

  • Life span: Specifies the lifetime of a particle in seconds. Set this to 1 so a single particle will only exist for one second.
  • Linear velocity: Specifies the linear velocity of the emitted particles. Set this to 0 so the particles will spawn with no direction or velocity.
  • Angular velocity: Specifies the angular velocity of the emitted particles. Set this to 0 so the particles will not spin.
  • Acceleration: Specifies the force vector applied to the emitted particles. Set this to (x: 0, y: -5, z: 0) — which is a downwards vector — to simulate a soft gravity effect on the particles, once spawned.
  • Speed factor: A multiplier that sets the speed of the particle simulation. Set this to 1 to run the simulation at a normal speed.
  • Stretch factor: A multiplier that stretches particles in their direction of motion. Set this to 0 to not stretch the particle image.

Image Attributes

The image attributes control the visual aspects of the particles. They also govern how the appearance of those particles can change over their lifetimes:

TrailParticleSettings2

  • Image: Specifies an image with which each particle will be rendered. Select the CircleParticle.png image, giving the particle its primary shape.
  • Color: Sets the tint of the specified image. Set the color to White, giving the particle system a base color of white.
  • Animate color: Causes particles to change color over their lifetimes. Uncheck this, because the particle color is not going to change at all.
  • Color variation: Adds a bit of randomness to the particle color. You can set this to (h: 0, s: 0, b: 0, a: 0) because the particle color will not vary.
  • Size: Specifies the size of the particles. Set this to 0.1 so the emitted particles are small in size.

Image Sequence Attributes

To create an animated image for your particles, you arrange each frame of the animation into a grid on a single image (like a sprite sheet in a game). Then you simply use that grid image as the image for your particle emitter. The image sequence attributes let you control the basic animation properties of the particle:

TrailParticleSettings3

  • Initial frame: Sets the first zero-based frame of the animation sequence. The zeroth frame corresponds to the top left image in the grid. You’re using a single frame image, so set this to 0.
  • Frame rate: Controls the rate of the animation in frames per second. Set this to 0 as this only applies when using an image that contains multiple frames.
  • Animation: Specifies the behavior of the animation sequence. Repeat loops the animation, Clamp only plays it once and Auto Reverse plays it from the start to the end, then back again. You can leave this on Repeat as this doesn’t matter when using a single frame image.
  • Dimensions: Specifies the number of rows and columns in the animation grid. Set this to (Rows: 1, Columns: 1) because you’re using a single frame image.

Rendering Attributes

The rendering attributes define how the render phase treats the particles:

TrailParticleSettings4

  • Blending: Specifies the blend mode of the renderer when drawing the particles into the scene. Set this to Alpha, which will use the image alpha channel information for transparency.
  • Orientation: Controls the rotation of the particles. Set this to Billboard screen-aligned, which will keep the flat particles facing the camera view at all times so you won’t notice that the particles are indeed flat images.
  • Sorting: Sets the rendering order of the particles. This property works in conjunction with the blend mode and affects how the blending is applied. Set this to None so the particle system will not make use of sorting.
  • Lighting: Controls whether SceneKit applies lighting to the particles. Uncheck this so the particle system ignores any lights in the scene.

Physics Attributes

The physics attributes let you specify how particles behave in the physics simulation:

TrailParticleSettings5

  • Affected by gravity: Causes the scene’s gravity to affect the particles. Uncheck this as you don’t want the particle system to participate in the physics simulation.
  • Affected by physics fields: Causes physics fields within the scene to affect the particles. Uncheck this as you don’t want physics fields to have an effect on the particles.
  • Die on Collision: Lets physics bodies in your scene collide and destroy the particles. Uncheck this as you don’t want to remove particles when they collide with node objects in the scene.
  • Physics Properties: Basic physics properties that control the physics behaviour of the particles during the physics simulation. You can leave all these at their default values since the particle system will not make use of this.

Life Cycle Attributes

The life cycle attributes let you control the overall life cycle of your particle system:

TrailParticleSettings6

  • Emission duration: Controls the length of time the emitter will emit new particles. Set this to 1, which will activate the particle emitter for a total length of 1 second.
  • Idle duration: Looping particle systems emit particles for the specified emission duration, then become idle for the specified idle duration, after which the cycle repeats. Set this to 0 so the particle system will only emit once.
  • Looping: Specifies whether the particle system emits particles once, as in an explosion, or continously, like a volcano. Set this to Loops continuously so the emitter emits for as long as it can before it’s removed from the scene again.

Phew! There are a lot of attributes to consider for a single particle system, but this gives you a lot of control to get the exact special effect you’re looking for.

If you diligently copied over the values from the screenshots to your particle system, you will have a system that represents the following effect:

TrailParticleSystem

If yours doesn’t look like this, try rotating the camera. It might also help to change the background color to a dark blue like you see here to make the particle system easier to see.

It’s finally time to add the cool particle effect to your game. Add the following to the GameViewController.swift class:

// 1
func createTrail(color: UIColor, geometry: SCNGeometry) -> 
  SCNParticleSystem {
  // 2
  let trail = SCNParticleSystem(named: "Trail.scnp", inDirectory: nil)!
  // 3
  trail.particleColor = color
  // 4
  trail.emitterShape = geometry
  // 5
  return trail
}

Here’s what’s going on above:

  1. This defines createTrail(_: geometry:) which takes in color and geometry parameters to set up the particle system.
  2. This loads the particle system from the file you created earlier.
  3. Here, you modify the particle’s tint color based on the parameter passed in.
  4. This uses the geometry parameter to specify the emitter’s shape.
  5. Finally, this returns the newly created particle system.

This method helps you create instances of SCNParticleSystem, but you still need to add the particle systems to your spawned shape objects.

Note that createTrail(_: geometry:) takes in a color parameter and uses it to tint the particles. You will set the color of the particle system to be the same as the color of the shape.

Find the line in spawnShape(), where you set the shape’s material diffuse contents, and split it up so the random color is stored in a constant like so:

let color = UIColor.random()
geometry.materials.first?.diffuse.contents = color

Next, add the following lines further down in spawnShape(), just after you apply a force to the physics body of geometryNode:

let trailEmitter = createTrail(color: color, geometry: geometry)
geometryNode.addParticleSystem(trailEmitter)

This uses createTrail(_: geometry:) to create a particle system and attach it to geometryNode.

Build and run; take a look at your hard work in action!

BuildAndRun0

Woot! :] It looks great — but what would really make this shine is a heads-up display.

Heads-up Displays

In this short section, you’ll use the Game Utils to quickly add a little heads-up display to show your player’s remaining lives, best score and current score. The code behind the scenes uses a SpriteKit label and uses the output of the label as a texture for a plane. This is a powerful technique!

Add the following new property to GameViewController.swift, right below spawnTime:

var game = GameHelper.sharedInstance

This lets you quickly access the GameHelper shared instance, which contains a set of methods to do the heavy lifting for you.

Add the following method to the bottom of GameViewController, below createTrail():

func setupHUD() {
  game.hudNode.position = SCNVector3(x: 0.0, y: 10.0, z: 0.0)
  scnScene.rootNode.addChildNode(game.hudNode)
}

Here, you make use of game.hudNode from the helper library. You set the HUD node’s position and add it to the scene.

Next, you need to call setupHUD() from somewhere. Add the following line to the bottom of viewDidLoad():

setupHUD()

Now that you have a heads-up display, you need to keep it up to date. Add the following call to game.updateHUD() to the bottom of renderer(_: updateAtTime:)

game.updateHUD()

Build and run; you’ll see your display at the top of the screen as shown below:

BuildAndRun1

Your game now has a nifty little HUD with a life counter, the high score and the current score.

Okay, the heads-up display is nice, but it’s high time to add some interaction to your game.

Touch Handling

As is usually the case, enabling touch in your app isn’t as straightforward as one would hope.

The first step is to understand how touch handling works in 3D. The image below shows a touch point in a side view of your scene and how SceneKit translates that touch point into your 3D scene to determine which object you’re touching:

3DPicking

So what steps do you take to handle the user’s touch event?

  1. Get touch location: First, you need to get the location of the user’s touch on the screen.
  2. Convert to view coordinates: After that, you need to translate that touch location to a location relative to the SCNView instance that’s presenting the scene.
  3. Fire a ray for a hit test: Once you’ve established a touch location relative to the view, SceneKit can perform a hit test for you by firing off a ray (no, not that Ray! :]) into your scene and returning a list of objects that intersect with the ray.

Naming Nodes

Before you can activate the touch ray of death, you need a way to identify each spawned object. The simplest approach is to give them names.

Add the following to spawnShape(), right after you add the particle system to geometryNode:

if color == UIColor.black {
  geometryNode.name = "BAD"
} else {
  geometryNode.name = "GOOD"
}

True to the spirit of the black-hatted villains of old Western movies, you assign the moniker "BAD" to black-colored objects and "GOOD" to all others.

Adding Touch Handling

Next, you need to write a method that you’ll later call when you detect the user has tapped a particular node.

Add the following method to the bottom of GameViewController, below setupHUD():

func handleTouchFor(node: SCNNode) {
  if node.name == "GOOD" {
    game.score += 1
    node.removeFromParentNode()
  } else if node.name == "BAD" {
    game.lives -= 1
    node.removeFromParentNode()
  }
}

This method checks the moniker of the touched node; good nodes increase the score and bad (black) nodes reduce the number of lives by one. In either case, you remove the node from the screen as it’s destroyed.

Using the Touch Handler

To capture the user’s touches, you’ll use touchesBegan(_: withEvent:), which is called every time the player touches the screen.

To implement your own version, add the following to GameViewController, right below handleTouchFor(_:):

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
  // 1
  let touch = touches.first!
  // 2
  let location = touch.location(in: scnView)
  // 3
  let hitResults = scnView.hitTest(location, options: nil)
  // 4
  if let result = hitResults.first {
    // 5
    handleTouchFor(node: result.node)
  }
}

Taking each numbered comment in turn:

  1. Grab the first available touch. There can be more than one if the player uses multiple fingers.
  2. Translate the touch location to a location relative to the coordinates of scnView.
  3. hitTest(_: options:) gives you an array of SCNHitTestResult objects that represent any intersections of the ray starting from the spot inside the view the user touched, and going away from the camera.
  4. Check the first result from the hit test.
  5. Finally, you pass the first result node to your touch handler, which will either increase your score — or cost you a life!

One final step. You don’t need the camera control anymore so change the line in setupView() as follows:

scnView.allowsCameraControl = false

Build and run; get ready to unleash your deadly finger of annihilation! :]

BuildAndRun1

Tap on the spawned objects and make them disintegrate into thin air. Whoo-hoo! :]

Challenge

Time to up the cool factor — and what’s cooler than explosions? Absolutely nothing, right?

That brings you to the challenge of this tutorial, and that is to create another particle system and name it Explode.scnp. See if you can figure out what attributes to modify to make those particles explode.

The effect should look something similar to this:

ExplosionParticleSystem

You can use the following image as a starting point for your particle system:

CreateExplosionParticles0

Shaping Particle Explosions

Now that you’ve created the explosion particle system, you need to add some code to make those nodes explode. You’re going to use some special properties to make the explosion take the same shape as whatever node you touch.

Add the following to the bottom of GameViewController, below touchesBegan(_: withEvent):

// 1
func createExplosion(geometry: SCNGeometry, position: SCNVector3,
  rotation: SCNVector4) {
  // 2
  let explosion =
    SCNParticleSystem(named: "Explode.scnp", inDirectory:
  nil)!
  explosion.emitterShape = geometry
  explosion.birthLocation = .surface
  // 3
  let rotationMatrix =
    SCNMatrix4MakeRotation(rotation.w, rotation.x,
      rotation.y, rotation.z)
  let translationMatrix =
    SCNMatrix4MakeTranslation(position.x, position.y, 
      position.z)
  let transformMatrix =
    SCNMatrix4Mult(rotationMatrix, translationMatrix)
  // 4
  scnScene.addParticleSystem(explosion, transform: transformMatrix)
}

Here’s the play-by-play for the above code:

  1. createExplosion(_: position: rotation:) takes three parameters: geometry defines the shape of the particle effect, while position and rotation help place the explosion into the scene.
  2. This loads Explode.scnp and uses it to create an emitter. The emitter uses geometry as emitterShape so the particles will emit from the surface of the shape.
  3. Enter the Matrix! :] Don’t be scared by these three lines; they simply provide a combined rotation and position (or translation) transformation matrix to addParticleSystem(_: withTransform:).
  4. Finally, you call addParticleSystem(_: wtihTransform) on scnScene to add the explosion to the scene.

You’re so close to replicating those great Hollywood explosions! Add the following line twice inside handleTouchFor(_:) — once to the “good” if block and once to the “bad” else block, right before you remove node from the parent:

createExplosion(geometry: node.geometry!,
  position: node.presentation.position,
    rotation: node.presentation.rotation)

This uses the presentation property to retrieve the position and rotation parameters of node . You then call createExplosion(_: position: rotation:) with those parameters.

Note: You’re using presentation because the physics simulation is currently moving the node.

Build and run; tap away and make those nodes explode!

BuildAndRun2

Note: You can find the project for this challenge under the projects/challenge/GeometryFighter folder.

Adding Juice

Your game is not quite done yet; there’s still plenty room for improvement, right? To push your game to that next level, you absolutely have to add something known as juice. Juice will give your game that little something special, just to make it stand out above the rest.

Here are a few ideas that will definitely juice things up:

  • Game state management: With basic game state management you’ll be able to control certain game mechanics based on a game state like TapToPlay, Playing or GameOver.
  • Splash screens: Use pretty splash screens. They provide the player with visual clues of the current game state.
  • Sound effects: Add cool sound effects to provide the player with crucial audio feedback of good and bad interaction with the game elements.
  • Camera shakes: Really big explosions produce really big shockwaves. Add a shaking camera effect to give your game a little something extra.

Where to Go From Here?

You’ve reached the end of this tutorial series — and you have a great SceneKit game to boot! Feel free to do your happy dance! :]

Here is the finished project from this Scene Kit series with Swift (with the juice applied).

If you’d like to learn more, you should check out our book 3D Apple Games by Tutorials. The book teaches you everything you need to know to make 3D iOS games, by making a series of mini-games like this one, including a games like Breakout, Marble Madness, and even Crossy Road.

In the meantime, if you have any questions or comments about this tutorial, please join the forum discussion below!

Chris Language

Chris is a seasoned coder with 20+ years of experience. He has fond memories of his childhood and his Commodore 64; more recently he started adding more good memories of life with all his iOS devices. At day, he fights for survival in the corporate jungle of Johannesburg, South Africa. At night he fights demons, dragons and angry little potty-mouth kids online. For relaxation he codes.

You can find Chris on Twitter.

Forever Coder, Artist, Musician, Gamer and Dreamer.

Other Items of Interest

Save time.
Learn more with our video courses.

raywenderlich.com Weekly

Sign up to receive the latest tutorials from raywenderlich.com each week, and receive a free epic-length tutorial as a bonus!

Advertise with Us!

PragmaConf 2016 Come check out Alt U

Our Books

Our Team

Video Team

... 20 total!

Swift Team

... 15 total!

iOS Team

... 42 total!

Android Team

... 16 total!

macOS Team

... 11 total!

Unity Team

... 11 total!

Articles Team

... 12 total!

Resident Authors Team

... 15 total!