Living Without Story Boards: Using SKEmitter Nodes to Make the Fire Pendulum.

Screenshot 2014-05-29 06.21.55In this last lesson in building the clock, I’ll introduce emitter nodes. I’ll turn our pendulum into a  ball of fire and make it snow in the app.

Emitter nodes are special nodes that emit particles which follow their own physics. They cannot be assigned physics properties like collisions but make for many great special effects like fire, rain, magic and snow. Emitter nodes can be created programmatically. Unlike other nodes we have discussed they do have a special editor which creates a file with the node, which allows for more experimentation.

Create an Emitter Node File

Start in Xcode by going to File>New>File… in the drop-down menu. In the dialog box select  Resources on the left column under iOS and select Sprite Kit Particle File as the template.

Screenshot 2014-05-29 05.36.23

Click Next, and Xcode will ask you to select a  Particle template.  There are several selections, but for our first emitter, pick the Fire template.

Screenshot 2014-05-29 05.37.03
The different templates for the particle emitter

Click Next and  the emitter editor will appear.

Emitter Editor
The emitter editor with our fire template
The properties for the emitter
The properties for the emitter

The properties box has many properties you can play with to get exactly the effect you like. For our purposes, we will keep the flame in its default setting. You can check Apple’s documentation, the Particle Emitter Editor Guide for more information on each of these properties.

The template added two files: a particle emitter file and a particle texture file.  By adding another .png file to your project you can change the texture to anything you want — and the fire will take on some interesting effects.

Add the Emitter Node to the Scene

Now go back to the scene file in Xcode. Add the following code just under [self addChild:bouncingBall]; adding the bouncing ball to the scene in initWithSize:.

  //add fire to the ball using emitters
        NSString *firePath = [[NSBundle mainBundle] pathForResource:@"MyFireParticle" ofType:@"sks"];
        SKEmitterNode *fireEmitter = [NSKeyedUnarchiver unarchiveObjectWithFile:firePath];
        CGVector emitterVector = CGVectorMake(bouncingBall.frame.size.width * 1.1, 0);
        fireEmitter.particlePositionRange = emitterVector;
        [bouncingBall addChild:fireEmitter];
  

Line 2 of this code gets the path of the emitter file. Line 3 makes the emitter. Lines 4 and 5 are not mandatory, but they change the particle position range of the emitter to match the width of the node I’m adding the emitter to. I add the fire emitter to the bouncing ball.

Build and run:

The fire ball
The fire ball

Adding Some Snow

Using the same process, let’s add snow to the application. Start by selecting File>New>File… from the drop down menu. Select the Particle Emitter Template. Click Next, then select the particle template Snow. Click Next. you will see the editor screen with snow. Go back to the scene and near the top of initWithSize: add the following code:

//add snow the the top of the screen using emitters
        NSString *snowPath = [[NSBundle mainBundle] pathForResource:@"MySnowParticle" ofType:@"sks"];
        SKEmitterNode *snowEmitter = [NSKeyedUnarchiver unarchiveObjectWithFile:snowPath];
        snowEmitter.position = [self gridPointX:2.5 pointY:5.0];
        [self addChild:snowEmitter];

Lines 1 and 2 again get the emitter node file and create the emitter. Line 3 I set the  position property of the emitter node centered on the top of the scene using the gridding method. Line 4 adds the snow. Notice where we placed this code segment in the method. Sprite Kit loads nodes on top of each other and this is the easiest way to keep the snow in the back ground. If you wanted to snow in the foreground, move this code to the last node created in initWithSize:

Build and Run:

The finished app. Dont stare too much.
The finished app. Don’t stare too much.

A Queasy Caution about Emitters

As you will see shortly after running this code it has a small problem. Apple found this out when introducing iOS7 and the parallax backgrounds. In real programming, I would never make a UI like this, but for demonstration purposes, I left it this way. It is very easy to get people, including me, queasy and bring on a headache with the emitter animations used here. It stimulates motion sickness. Be as subtle with your use of snow or rain as you can, such as using a low alpha value or a small particle birth rate, and do not have something like the pendulum having fast  motion at a different angle to the snow or rain. That will keep things a bit more sane.

That is everything I was going to cover for the clock. I’ve covered most of the basic ingredients of user interfaces in sprite kit. If you have any other questions or suggestions, send me a message below or leave me a comment

The Whole Code

I’ve posted the code for the project on github.  where the graphics and the emitters already exist. Here is the code for the scene for those wanting to look at everything in context:

//
//  MPViewController.m
//  SpriteTimeClock
//
//  Created by Steven Lipton on 5/6/14.
//  Copyright (c) 2014 Steven Lipton. All rights reserved.
//

#import "MPViewController.h"
#import "MPMyScene.h"

@implementation MPViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
/*
    // Configure the view.
    SKView * skView = (SKView *)self.view;
    skView.showsFPS = YES;
    skView.showsNodeCount = YES;

    // Create and configure the scene.
    SKScene * scene = [MPMyScene sceneWithSize:skView.bounds.size];
    scene.scaleMode = SKSceneScaleModeAspectFill;

    // Present the scene.
    [skView presentScene:scene];
 */
}

-(void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    // Configure the view.
    SKView * skView = (SKView *)self.view;
    skView.showsFPS = YES;
    skView.showsNodeCount = YES;

    // Create and configure the scene.
    SKScene * scene = [MPMyScene sceneWithSize:skView.bounds.size];
    scene.scaleMode = SKSceneScaleModeAspectFill;

    // Present the scene.
    [skView presentScene:scene];
}

- (BOOL)shouldAutorotate
{
    return YES;
}

- (NSUInteger)supportedInterfaceOrientations
{
    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
        return UIInterfaceOrientationMaskAllButUpsideDown;
    } else {
        return UIInterfaceOrientationMaskAll;
    }
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Release any cached data, images, etc that aren't in use.
}

@end

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s