CAEmitterLayer如何反复短时间发射

时间:2012-06-07 09:40:26

标签: ios uiview core-animation calayer caemitterlayer

我正在玩CAEmitterLayer,现在我遇到了一些问题:(

我需要一个短粒子效果 - 比如一个击球或爆炸 - 例如在一个地方(所以我在这个地方有小UIView)。我该怎么办?

1,我有一个想法 - 使用它的粒子创建emitterLayer并将lifeTime设置为0.当我需要它时,我将lifeTime设置为1,并且在一段时间之后我可以将其设置为0。   - 但它没有做任何事情:(

2,第二个想法是每次需要时创建[CAEmitterLayer图层]并将其添加为图层子图层。但是我在想,当我重复十次时会发生什么......我有10个子层,一个有效,9个“死”?  如何停止一般发射?我有一段时间后执行performSelector将生命周期设置为0,并将其他选择器设置为更长的间隔来删除.FromSuperlayer ...但它不是那么漂亮我想拥有它:(还有另一种“正确”方式吗?

我认为太多的子层与我的另一个问题有关...我只想发射一个粒子。当我这样做时,它有效。但是有时候它发出三个粒子,有时两个......这让我很生气。当我不停止发射器时,它每次给出正确数量的粒子......

所以问题......

如何在短时间内发射粒子。  如何使用它们 - 如停止,从父图层中删除,...  如何发出确切数量的粒子

编辑:

emitter = [CAEmitterLayer layer];
emitter.emitterPosition = CGPointMake(self.view.bounds.size.width/2, self.view.bounds.size.height/2);
emitter.emitterMode = kCAEmitterLayerPoints;
emitter.emitterShape    = kCAEmitterLayerPoint;
emitter.renderMode      = kCAEmitterLayerOldestFirst;
emitter.lifetime = 0;


particle = [CAEmitterCell emitterCell];
[particle setName:@"hit"];
particle.birthRate      = 1;
particle.emissionLongitude  = 3*M_PI_2;//270deg
particle.lifetime       = 0.75;
particle.lifetimeRange      = 0;
particle.velocity       = 110;
particle.velocityRange      = 20;
particle.emissionRange      = M_PI_2;//PI/2 = 90degrees
particle.yAcceleration      = 200;
particle.contents       = (id) [[UIImage imageNamed:@"50"] CGImage];
particle.scale          = 1.0;
particle.scaleSpeed     = -0.5;
particle.alphaSpeed     = -1.0;

emitter.emitterCells = [NSArray arrayWithObject:particle];
[(CAEmitterLayer *)self.view.layer addSublayer: emitter];

然后在与按钮链接的方法中,我这样做:

emitter.lifetime = 1.0;

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.9 * NSEC_PER_SEC), dispatch_get_current_queue(), ^{
    emitter.lifetime = 0;
});

改为@DavidRönnqvist态度后改编和更新

CAEmitterCell *dustCell = [CAEmitterCell emitterCell];
[dustCell setBirthRate:1];
[dustCell setLifetime:1.5];
[dustCell setName:@"dust"];
[dustCell setContents:(id) [[UIImage imageNamed:@"smoke"] CGImage]];
[dustCell setVelocity:50];
[dustCell setEmissionRange:M_PI];
// Various configurations for the appearance...
// This is the only cell with configured scale, 
// color, content, emissionLongitude, etc...

[emitter setEmitterCells:[NSArray arrayWithObject:dustCell]];
[(CAEmitterLayer *)self.view.layer addSublayer:emitter];

// After one burst, change the birth rate of the cloud to 0
// so that there is only one burst per side.
double delayInSeconds = 0.5; // One cloud will have been created by now, but not two
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void) {

    [emitter setLifetime:0.0];
    [emitter setValue:[NSNumber numberWithFloat:0.0] 
               forKeyPath:@"emitterCells.dust.birthRate"];
});

6 个答案:

答案 0 :(得分:10)

您可以通过配置一次(不要每次都添加新的发射器单元格)并将birthRate设置为0(不会创建任何粒子)来完成此操作。然后,当您想要创建粒子时,可以将birthRate设置为您想要创建的每秒粒子数。经过一段时间后,将birthRate设置为0,以便停止发射。 您可以使用类似dispatch_after()的内容来执行此延迟。


我做了类似的事情,并且像这样解决了。以下将创建一个快速爆发的粒子。下次想要粒子发射时,将“云”的birthRate更改回1.

CAEmitterCell *dustCell = [[CAEmitterCell alloc] init];
[dustCell setBirthRate:7000];
[dustCell setLifetime:3.5];
// Various configurations for the appearance...
// This is the only cell with configured scale, 
// color, content, emissionLongitude, etc...

CAEmitterCell *dustCloud = [CAEmitterCell emitterCell];
[dustCloud setBirthRate:1.0]; // Create one cloud every second
[dustCloud setLifetime:0.06]; // Emit dustCells for 0.06 seconds
[dustCloud setEmitterCells:[NSArray arrayWithObject:dustCell]];
[dustCloud setName:@"cloud"]; // Use this name to change the birthRate later

[dustEmitter setEmitterPosition:myPositionForDustEmitter];
[rightDustEmitter setEmitterCells:[NSArray arrayWithObject:dustCloud]];

// After one burst, change the birth rate of the cloud to 0
// so that there is only one burst per side.
double delayInSeconds = 0.5; // One cloud will have been created by now, but not two
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void) {

    // For some reason, setting the birthRate of the "cloud" to 0
    // has a strange side effect that when you set it back to 1 all
    // the missed emissions seems to happen at once during the first
    // emission and then it goes back to only emitting once per
    // second. (Thanks D33 for pointing this out).
    // By instead changing the birthRate of the "dust" particle
    // to 0 and then back to (in my case) 7000 gives the visual
    // effect that I'm expecting. I'm not sure why it works
    // this way but at least this works for me...
    // NOTE: This is only relevant in case you want to re-use
    // the emitters for a second emission later on by setting
    // the birthRate up to a non-zero value.
    [dustEmitter setValue:[NSNumber numberWithFloat:0.0] 
               forKeyPath:@"emitterCells.cloud.emitterCells.dust.birthRate"];
});

答案 1 :(得分:6)

感谢foundrys出色的回答over here我解决了一个与此非常相似的问题。它不涉及隐藏任何视图。简而言之,它是这样的:

像往常一样设置发射器,命名发射器单元并给它们一个出生值为零:

-(void) setUpEmission {
  # ...
  # snip lots of config
  # ...
  [emitterCell1 setBirthrate:0];
  [emitterCell1 setName:@"emitter1"];
  [emitterCell2 setBirthrate:0];
  [emitterCell2 setName:@"emitter2"];
  emitterLayer.emitterCells = @[emitterCell1, emitterCell2];
  [self.view.layer addSublayer:emitterLayer];
}

然后创建一个启动方法,在短时间后自动关闭发射,并使用停止方法:

-(void) startEmission {
  [emitterLayer setValue:@600 forKeyPath:@"emitterCells.emitter1.birthRate"];
  [emitterLayer setValue:@250 forKeyPath:@"emitterCells.emitter2.birthRate"];
  [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(stopEmission) userInfo:nil repeats:NO];
}

-(void) stopEmission {
  [emitterLayer setValue:@0 forKeyPath:@"emitterCells.emitter1.birthRate"];
  [emitterLayer setValue:@0 forKeyPath:@"emitterCells.emitter2.birthRate"];
}

在这个例子中,我将出生率设置为600和250.定时器在0.2秒后关闭发射,但使用你认为合适的任何东西。

如果Apple实施了启动/停止方法,那么最佳解决方案就是如此,但我发现这是一个令人满意的解决方案。

答案 2 :(得分:5)

我也在寻找解决方案并找到了article

查看此Gist的五彩纸屑粒子及其Stop Emitting方法。

它的作用是:

  1. 将粒子视图添加到显示视图。
  2. 让粒子尽可能长时间运行。
  3. 使用confettiEmitter.birthRate = 0.0;停止发出新粒子。
  4. 等几秒钟。
  5. 删除粒子视图。
  6. 希望它可以提供帮助。

答案 3 :(得分:1)

CAEmitter.birthRate是可动画的。假设您已在视图中添加了一些CAEmitterLayer s,您可以执行此操作来设置出生率的衰减动画,然后在几秒钟后重新启动:

- (void) startConfetti{
  for (CALayer *emitterLayer in self.layer.sublayers) {
    if ([emitterLayer isKindOfClass: [CAEmitterLayer class]]) {
      ((CAEmitterLayer *)emitterLayer).beginTime = CACurrentMediaTime();
      ((CAEmitterLayer *)emitterLayer).birthRate = 6;

      // Decay over time
      [((CAEmitterLayer *)emitterLayer) removeAllAnimations];

      [CATransaction begin];
      CABasicAnimation *birthRateAnim = [CABasicAnimation animationWithKeyPath:@"birthRate"];
      birthRateAnim.duration = 5.0f;
      birthRateAnim.fromValue = [NSNumber numberWithFloat:((CAEmitterLayer *)emitterLayer).birthRate];
      birthRateAnim.toValue = [NSNumber numberWithFloat:0.0f];
      birthRateAnim.repeatCount = 0;
      birthRateAnim.autoreverses = NO;
      birthRateAnim.fillMode = kCAFillModeForwards;
      [((CAEmitterLayer *)emitterLayer) addAnimation:birthRateAnim forKey:@"finishOff"];
      [CATransaction setCompletionBlock:^{
        ((CAEmitterLayer *)emitterLayer).birthRate = 0.f;
        [self performSelector:@selector(startConfetti) withObject:nil afterDelay:10];
      }];
      [CATransaction commit];
    }
  }
}

答案 4 :(得分:0)

好的,最后,经过数小时的测试并尝试了不同的风格(初始化,删除,配置发射器),我想出了最终结果...... 实际上它让我非常沮丧...

---这是不可能的!---

即使我每次需要它时都会创建发射器及其粒子,如果我只设置一个粒子来发射,它会给我大部分时间一个粒子......但它不是100%,有时它只发出三个粒子,或者两个......这是随机的。这非常糟糕。因为它是视觉效果...... :(

无论哪种方式,如果有人提示如何解决这个问题,请告诉我......

答案 5 :(得分:0)

您是否考虑过利用CALayers的动画属性?

  func setEmitterProperties() {

    backgroundColor = UIColor.clearColor().CGColor
    birthRate = kStandardBirthRate
    emitterShape = kCAEmitterLayerLine
    emitterCells = [typeOneCell, typeTwoCell, typeOneCell]
    preservesDepth = false

    let birthRateDecayAnimation = CABasicAnimation()
    birthRateDecayAnimation.removedOnCompletion = true
    birthRateDecayAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
    birthRateDecayAnimation.duration = CFTimeInterval(kStandardAnimationDuration)
    birthRateDecayAnimation.fromValue = NSNumber(float: birthRate)
    birthRateDecayAnimation.toValue = NSNumber(float: 0)
    birthRateDecayAnimation.keyPath = kBirthRateDecayAnimationKey
    birthRateDecayAnimation.delegate = self

  }
  1. 如果您不想这样做,委托属性也可能是零 完成的任何事情,如 animationDidStop:finished:

  2. 常数kBirthRateDecayAnimationKey& kStandardAnimationDuration使用我的约定,而不是Apple的。