Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CAEmitterCell does not respect birthRate change

I would like to create a particle effect which is only emitting while the user touches the screen, but I cannot change the CAEmitterCell birthRate property once is set to a non zero value.

I have a subclass of UIView, which sets up my CAEmitterLayer and my CAEmitterCell just the way I want them. I am defining two properties on that class:

@property (strong, nonatomic) CAEmitterLayer *emitterLayer;
@property (strong, nonatomic) CAEmitterCell *emitterCell;

Then, in my view controller, I am tracking touches, setting the position of the emitterLayer, and emitterCell birthrate:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    CGPoint tappedPt = [touch locationInView:touch.view];
    NSLog(@"began x:%f y:%f",tappedPt.x, tappedPt.y);
    emitterView.emitterCell.birthRate = 42;
    emitterView.emitterLayer.emitterPosition = tappedPt;
}

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    CGPoint tappedPt = [touch locationInView:touch.view];
    NSLog(@"moved x:%f y:%f",tappedPt.x, tappedPt.y);
    emitterView.emitterLayer.emitterPosition = tappedPt;
}

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
    NSLog(@"ending %f", emitterView.emitterCell.birthRate);
    emitterView.emitterCell.birthRate = 0.00;
    NSLog(@"ended %f", emitterView.emitterCell.birthRate);
}

The log reports that the emitterView.emitterCell.birthRate changes:

began x:402.000000 y:398.500000
ending 42.000000
ended 0.000000

When I touch the screen, the emitter starts as expected, the layer follows the touch, but when I end the touch, the emitter cell happily emits whatever value was set initially set (the value set in touchesBegan). Whatever I do I cannot seem to be able to change the birthrate value once is set to a non zero value. Log reports that the values are set properly, but the emitter keeps emitting.

However, if I change the touchesEnded method to change the position of the layer, after I set the birthRate on emitterCell then everything works as expected:

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
    UITouch *touch = [touches anyObject];
    CGPoint tappedPt = [touch locationInView:touch.view];
    NSLog(@"began x:%f y:%f",tappedPt.x, tappedPt.y);

    NSLog(@"ending %f", emitterView.emitterCell.birthRate);
    emitterView.emitterCell.birthRate = 0.0;
    NSLog(@"ended %f", emitterView.emitterCell.birthRate);
    emitterView.emitterLayer.emitterPosition = tappedPt;
}

Can someone please explain why?

like image 577
nekiTamoTip Avatar asked May 25 '13 12:05

nekiTamoTip


1 Answers

To stop emitting the particles you have to set birthRate property of CAEmitterLayer instance to 0, although it was initially set on CAEmitterCell instance... Not sure why, but it works.

Swift 3 example:

func emitParticles() {
    let particlesEmitter = CAEmitterLayer()
    particlesEmitter.emitterPosition = center
    particlesEmitter.emitterShape = kCAEmitterLayerCircle
    particlesEmitter.emitterSize = CGSize(width: 50, height: 50)
    particlesEmitter.renderMode = kCAEmitterLayerAdditive

    let cell = CAEmitterCell()
    cell.birthRate = 15
    cell.lifetime = 1.0
    cell.color = bubble.color.cgColor
    cell.velocity = 150
    cell.velocityRange = 50
    cell.emissionRange = .pi
    cell.scale = 0.1
    cell.scaleSpeed = -0.1

    cell.contents = UIImage(named: "particle")?.cgImage
    particlesEmitter.emitterCells = [cell]

    layer.addSublayer(particlesEmitter)

    DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
        particlesEmitter.birthRate = 0
    }
}
like image 77
Antonin Charvat Avatar answered Oct 02 '22 22:10

Antonin Charvat