How to add particle effects to an iOS App that is not a game using iOS 7 SpriteKit Particle?
Asked Answered
T

7

39

I need to add a rain particle effect to my app, I have been having a tough time finding ways to actually execute this idea.

I tried following this CALayer approach tutorial : Link but I am not quite sure if this is the best approach, considering the new iOS 7 SpriteKit Particle Emitter available in Xcode 5.

I have already created the .sks file and it's in my Hierarchy, but I am still unable to add it to my storyboard / project.

With that being said, How exactly do I add a SpriteKit Particle (sks) to my view? I am not at all familiar with scenes, layering , etc in the SpriteKit framework as I am not a game developer. I need the most details and sample code possible so that I can figure this out please

UPDATE: I have followed the direction provided in an answer by fellow SO member: AyatollahAndy, please see his answer below. Although I was able to display the SKScene in my view the app crashes when any touch event is received. I get the following: enter image description here

Thanks

Triple answered 27/10, 2013 at 17:9 Comment(5)
I am glad to hear that it is possible to achieve this without having "The Whole Package" of SpriteKit in my app, with that being said -> How do can I add said Particle to a normal View? O have already generated the .sks file.Triple
Erm, yes you do have to link with the SpriteKit.framework meaning "the whole package". Not that it matters because this library is built into iOS and doesn't increase the app's size. Nevertheless, to render the particle effect you have to create an SKView with an SKScene and put the particle effect on it. All other views are either on top or below, unless everything else is also made of Sprite Kit nodes.Cottrell
why not just add a "partially transparent raindrop overlay" and have an [UIView animationXXX:] applied to it?Intact
@Intact because that would not look nearly as good as particles generated from an emitter randomly.Triple
anyone coming to this very old question, I would urge you to scroll down to my answer ("2017") or any of the answers newer than my answer. Fortunately it is now very easy these days.....Bounded
Z
49

Create a SKScene in your UIView to add a SKEmitterNode particle effect.

One way of doing this:

1.In storyboard (or programatically if you prefer) add a View object on top of the existing View and resize it to your needs.
2.Change the class of the new view to SKView
3.In your view controller .h file create a property for the SKView:

@property IBOutlet SKView *skView;

4.Link the SKView on your storyboard to the skView property.
5.Create a new class, subclassing SKScene. MyScene.h will look like:

#import <SpriteKit/SpriteKit.h>
@interface MyScene : SKScene
@end

MyScene.m below contains code to create a particle effect whenever and wherever the SKView is touched.

#import "MyScene.h"

@implementation MyScene

-(id)initWithSize:(CGSize)size {    
    if (self = [super initWithSize:size]) {
        /* Setup your scene here */

        self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0];
        SKLabelNode *myLabel = [SKLabelNode labelNodeWithFontNamed:@"Chalkduster"];
        myLabel.text = @"Hello, World!";
        myLabel.fontSize = 30;
        myLabel.position = CGPointMake(CGRectGetMidX(self.frame),
                                   CGRectGetMidY(self.frame));

        [self addChild:myLabel];
    }
    return self;
}

//particle explosion - uses MyParticle.sks
- (SKEmitterNode *) newExplosion: (float)posX : (float) posy
{
    SKEmitterNode *emitter =  [NSKeyedUnarchiver unarchiveObjectWithFile:[[NSBundle mainBundle] pathForResource:@"MyParticle" ofType:@"sks"]];
    emitter.position = CGPointMake(posX,posy);
    emitter.name = @"explosion";
    emitter.targetNode = self.scene;
    emitter.numParticlesToEmit = 1000;
    emitter.zPosition=2.0;
    return emitter;
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    /* Called when a touch begins */

    for (UITouch *touch in touches) {
        CGPoint location = [touch locationInNode:self];
        //add effect at touch location
        [self addChild:[self newExplosion:location.x : location.y]];
    }
}

-(void)update:(CFTimeInterval)currentTime {
    /* Called before each frame is rendered */
}

@end

6.In your main view controller, include your scene class:

#import "MyScene.h"

and add code to viewDidLoad to initialise the SKView:

- (void)viewDidLoad
{
    [super viewDidLoad];

    // Configure the SKView
    SKView * skView = _skView;
    skView.showsFPS = YES;
    skView.showsNodeCount = YES;

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

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

You should then have a working SKScene within your main UIView.

Zed answered 4/11, 2013 at 15:47 Comment(8)
I have given this a try, although the app does initially launch with the "Hello World" scene in my view working fine, when I "touch" the app crashes and throws an error, please see updated question with details.Triple
#19780715Triple
How do I get rid of the black background? I made the skView & scene backgroundColor property clearColor but I still get a black background.Cuyler
Sprite Kit views cannot be made transparent. If you want to integrate particle effects with a UIKit view hierarchy, CAEmitterLayer is your best bet -- otherwise you'll be reimplementing your own UI within Sprite Kit, which is probably not the best use of your time.Imposing
I wish all StackOverflow answers were this clear and easy to follow. Many thanks!!Crispa
You Sir Are Simply A Genius. Im Freeking Out!Metalware
this 10 yr old answer is totally awesome, I sent a bounty, BUT these days it is much different and fortunately much easier! I put in an answerBounded
@Imposing SKViews and SKScenes can have a .clear background, but you must also set 'allowsTransparency' on the SKView.Prytaneum
B
45

In modern Xcode:

This is now very easy.

1. In Xcode, click to create a new

"SpriteKit Particle File"

it will be a single .sks file.

(NOTE: Do NOT choose "SceneKit Particle System File". Choose "SpriteKit Particle File".)

Click once on the .sks file. Notice the many controls on the right.

enter image description here

The particles will actually be moving, it is a living preview. Anything that can be done with particles, you can do it. It is like using particles in a game engine, except performance is 18 billion times better.

2. Have any ordinary UIView, anywhere you want:

@IBOutlet weak var teste: UIView! // totally ordinary UIView

3. Just use the following code to link:

The following slab of code will put your new particle system, inside, the ordinary UIView "teste":

import SpriteKit ...


let sk: SKView = SKView()
sk.frame = teste.bounds
sk.backgroundColor = .clear
teste.addSubview(sk)

let scene: SKScene = SKScene(size: teste.bounds.size)
scene.scaleMode = .aspectFit
scene.backgroundColor = .clear

let en = SKEmitterNode(fileNamed: "SimpleSpark.sks")
en?.position = sk.center

scene.addChild(en!)
sk.presentScene(scene)

Add this to anything you want.

If you want a sparkling button, add it to a button.

If you want the whole screen to shower rainbows, add it to a full-screen view.

It's that easy.


Example of how to use the SpriteKit Particle File controls:

Say you want a burst of sparks, which ends.

Set the max to 50...

enter image description here

Tip - if your effect "finishes" (ie, it is not a loop), it seems you can simply get rid of the SKScene when finished. Like this:

    ...
    scene.addChild(en!)
    sk.presentScene(scene)
    
    delay(1.5) { sk.removeFromSuperview() }

That one line of code at the end seems to clean-up everything.


BTW if you want fantastic ideas for particle systems, a great idea is click to the Unity "asset store", where various particle artists buy and sell particle systems. Their work will give you great ideas.

enter image description here

Just click "particles" in the list on the right; watch the videos. (Innovative examples .)


Note! Apple are going to make it so that you can very simply make a SKView in storyboard, and select the .sks scene. However ..

enter image description here

... it does not work yet! It's still broken as of the last edit to this post (2020). So you need the code fragment above.

Bounded answered 28/3, 2017 at 17:10 Comment(8)
You just made my day. Works perfectly!Medulla
@Bounded Absolutely.! Also, Where did you learn this. I couldn't find a single reference to learn this. Highly appreciable, that you provided this :)Medulla
I have adjusted the speed and added colors. Everything looks good for the sks file. I want what I can see to be displayed in the UIView where I added the SKView. I used the exact code and have tried many things but I haven't been able to figure out why it's not being displayed. I do everything else in code, I erase my main Storyboard.Anecdotist
That was one of the first things I did. I put a constraint to the safeAreaLayout, changed the color from white to clear to blue and then put a timer seeing it remove and the UIView is perfect. It has to be something to do with the UIView displaying the SKView with UIView.addSubview(SKView) or just adding the SKview to be displayedAnecdotist
@Bounded thanks for the help. I still don't know why it didn't work for me but after spending all day on this I did get it working. I added your code as it is in a function and added it right below the UIView function inside a DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(0), execute: {self.'your code()'} and everything is working now. Thanks 👍Anecdotist
@Anecdotist how interesting, so the app was on another thread? Wild guess, does your app fetch something from the internet ? Glad it works cheers!Bounded
It was very strange because that app isn't connected to anything yet. I was just testing the particle emitter on it's own VC before I transferred it to where I needed it. I forgot to upvote you, I'll do it right now ✌🏻Anecdotist
Good answer. For this to work properly, you must also set 'allowsTransparency' to true on the SKView. Also, ensure the SKView's backgroundColor is .clear, not 'nil'.Prytaneum
P
7

You can add SKView as a subview within your UIKit hierarchy. A function like the following would work, allowing you to create a UIImageView with the effect as a subview, and then you can add this to your main view. Be sure to link against SpriteKit.

UIImageView *NewEffectUIImageViewWithFrame(CGRect frame)
{
    UIImageView *tempView = [[UIImageView alloc] initWithFrame:frame];

    SKView *skView = [[SKView alloc] initWithFrame:CGRectMake(0.0, 0.0, frame.size.width, frame.size.height)];
    [tempView addSubview:skView];

    SKScene *skScene = [SKScene sceneWithSize:skView.frame.size];
    skScene.scaleMode = SKSceneScaleModeAspectFill;
    skScene.backgroundColor = [UIColor clearColor];

    SKEmitterNode *emitter =  [NSKeyedUnarchiver unarchiveObjectWithFile:[[NSBundle mainBundle] pathForResource:@"SparkParticle" ofType:@"sks"]];
    emitter.position = CGPointMake(frame.size.width*0.5,0.0);

    [skScene addChild:emitter];
    [skView presentScene:skScene];

    return tempView;
}

In the end, if all you need is an emitter, it may be easier to create a CAEmitterLayer and add that as a sublayer to your UIView instead. Of course, that means you have to programmatically create the CAEmitterLayer and can't use the cool Xcode particle editor...

Paracelsus answered 10/2, 2014 at 1:53 Comment(1)
the background of the imageview is black. is there a way to make it clear so that it can be merge with the superview flawlessly?Mortification
T
3

Actually there is a way to add particles without SpriteKit - CoreAnimation's CAEmitterCells.

This way you can add particles in your UIView easily. If you want to play around with the parameters and get the code easily, get this app (Particle X).

It also supports SpriteKit so if you want to play around or design particles on the go and immediately get the code for it, this app is the solution.

PS. If you haven't noticed it, I am the developer of the app - made it to use it myself when designing app and games. :)

Tumbledown answered 18/8, 2015 at 8:4 Comment(1)
Unfortunately, CAEmitterLayer lacks a lot of SpriteKit's functionality. For example, you can't create particles that fade in and then fade out. It's all or nothing with CAEmitterLayer. Nor can you pre-bake a scene's particles with the equivalent of SpriteKit's 'advanceSimulationTime'. But otherwise, CAEmitterLayer is a much better solution than SpriteKit for "instant" effects like a burst of particles.Prytaneum
B
3

Here's approach totally different approach to try. My buddy gave me this cool way to go. Using CAEmitterCell. All in code! Looks like you need a spark.png image.

extension UIView {
    final public func ignite() {
        let emitter = CAEmitterLayer()
        emitter.frame = self.bounds
        emitter.renderMode = kCAEmitterLayerAdditive
        emitter.emitterPosition = self.center
        self.layer.addSublayer(emitter)

        let cell = CAEmitterCell()
        let bundle = Bundle.init(for: UIColor.self)
        let image = UIImage(named: "spark", in: bundle, compatibleWith: traitCollection)

        cell.contents = image?.cgImage
        cell.birthRate = 1500
        cell.lifetime = 5.0
        cell.color = UIColor(red: 1.0, green: 0.5, blue: 0.1, alpha: 1).cgColor
        cell.alphaSpeed = -0.4
        cell.velocity = 50
        cell.velocityRange = 250
        cell.emissionRange = CGFloat.pi * 2.0

        emitter.emitterCells = [cell]
    }
}

Enjoy.

Bounded answered 3/4, 2017 at 2:5 Comment(0)
P
2

Putting this here for visibility reasons.

The answers regarding the user of a .clear backgroundColor are correct, except that you must also set the allowsTransparency property on SKView to 'true'.

skView.allowsTransparency = true
skView.backgroundColor = .clear  // (not nil)
scene.backgroundColor = .clear

If you don't set allowsTransparency to true, and you layout your SKView over, say, a UIImageView, the composition engine will have a fit, and will send your GPU red-lining, even if only a single particle is drawn. (In the Simulator, the CPU will spike instead.)

Prytaneum answered 5/7, 2020 at 7:12 Comment(0)
K
-1

You cannot use particle effects within UIView directly.

SKEmitterNode must be in a node tree defined with a node scene (SKScene). The scene node runs an animation loop that renders the contents of the node tree for display. UIView is static, won't work for it.

However, you probably able to create a scene inside your UIView, but I've never tried to do that.

Killie answered 1/11, 2013 at 11:17 Comment(1)
You can very easily add a particle view, right inside an ordinary UIView! see my answer belowBounded

© 2022 - 2024 — McMap. All rights reserved.