CAEmitterLayer, как излучать в течение короткого времени повторно
Я играю с CAEmitterLayer, и теперь я сталкиваюсь с некоторыми проблемами:(
Мне нужен эффект короткой частицы - например, удар или взрыв - например, в одном месте (поэтому у меня есть небольшой UIView в этом месте). Как мне это сделать?
1, у меня была идея - создать emitterLayer с его частицами и установить lifeTime на 0. И когда мне это нужно, я устанавливаю для lifeTime, например, 1, и через некоторое время я могу установить его обратно на 0. - НО он ничего не делает:(
2. Вторая идея состояла в том, чтобы каждый раз создавать мне слой [CAEmitterLayer] и добавлять его в качестве подслоя слоев. Но я думаю, что произойдет, когда я повторю это, например, десять раз... У меня есть 10 подслоев с одним активным и 9 "мертвыми"? Как вообще перестать излучать? Через некоторое время у меня есть executeSelector, чтобы установить время жизни равным 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;
});
ИЗМЕНЕНО и ОБНОВЛЕНО после изменения отношения @David Rö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 ответов
Вы можете сделать это, настроив все один раз (не добавляйте новую ячейку эмиттера каждый раз) и установив birthRate
до 0 (частицы не будут созданы). Затем, когда вы хотите создать свои частицы, вы можете установить birthRate
на количество частиц в секунду, которое вы хотите создать. Через некоторое время вы установите birthRate
вернуться к 0, чтобы выброс прекратился. Вы могли бы использовать что-то вроде dispatch_after()
сделать эту задержку.
Некоторое время назад я сделал нечто подобное и решил это следующим образом. Следующее создаст один быстрый взрыв частиц. В следующий раз, когда вы захотите испускать частицы, вы измените частоту рождения "облака" обратно на 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"];
});
Благодаря отличному ответу Foundrys здесь я решил проблему, очень похожую на эту. Это не подразумевает сокрытие каких-либо взглядов. Вкратце, это выглядит так:
Настройте свой излучатель, как обычно, назовите ячейки излучателя и дайте им значение рождаемости, равное нулю:
-(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];
}
Затем создайте метод start, который автоматически отключает излучение через некоторое время, и метод stop:
-(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 внедрила методы запуска / остановки, но если не считать этого, я считаю это удовлетворительным решением.
Я тоже искал решение и нашел эту статью.
посмотрите на эту суть частицы конфетти, и ее Stop Emitting
метод.
Что это делает:
- Добавьте вид частиц к вашему виду дисплея.
- Пусть частицы бегут столько, сколько вы хотите.
- остановить выброс новых частиц с
confettiEmitter.birthRate = 0.0;
, - подожди несколько секунд.
- удалить вид частиц.
Надеюсь, это поможет.
CAEmitter.birthRate
является анимируемым. Предполагая, что вы добавили несколько CAEmitterLayer
С точки зрения, вы можете сделать это, чтобы оживить спад рождаемости, а затем перезапустить через несколько секунд:
- (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];
}
}
}
ОК, наконец, после нескольких часов тестирования и проб разных стилей (инициализация, удаление, настройка эмиттеров) я пришел к окончательному результату... И на самом деле это меня очень огорчает...
---Это невозможно!---
Даже когда я создаю излучатель и его частицы каждый раз, когда мне это нужно, если я настраиваю только одну частицу на излучение, это дает мне большую часть времени одну частицу... НО это не 100%, а иногда он просто излучает три частицы или две... Это случайно. И это очень плохо. Потому что это визуальный эффект...:(
В любом случае, если у кого-то есть совет, как решить эту проблему, пожалуйста, дайте мне знать...
Вы рассматривали возможность использования анимируемых свойств 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
}
Свойство делегата также может быть равно нулю, если вы не хотите ничего делать по завершении, как в animationDidStop: finish:
Константы kBirthRateDecayAnimationKey & kStandardAnimationDuration используют мое соглашение, а не Apple.