2012-06-07 13 views
9

Tôi đang chơi xung quanh với CAEmitterLayer và tôi phải đối mặt với một số vấn đề hiện nay :(CAEmitterLayer làm thế nào để phát ra trong một thời gian ngắn liên tục

Tôi cần một hiệu ứng hạt ngắn - như một hit hoặc nổ - tại một nơi ví dụ (vì vậy tôi có UIView nhỏ ở nơi này) Làm thế nào tôi nên làm điều đó?

1, tôi đã có một ý tưởng - tạo emitterLayer với các hạt của nó và thiết lập lifeTime thành 0. Và khi tôi cần nó tôi đặt lifeTime thành 1 ví dụ và sau một thời gian tôi có thể thiết lập nó trở lại 0. - NHƯNG nó không làm bất cứ điều gì: (

2, ý tưởng thứ hai là tạo [lớp CAEmitterLayer] mỗi lần tôi cần và thêm lớp đó làm lớp con. Nhưng tôi đang nghĩ điều gì xảy ra khi tôi lặp lại nó ví dụ mười lần ... Tôi có 10 lớp con với một acive và 9 "chết"? Làm thế nào để dừng phát ra nói chung? Tôi có performSelector sau một thời gian để thiết lập tuổi thọ là 0 và các bộ chọn khác với khoảng thời gian dài hơn để removeFromSuperlayer… Nhưng nó không đẹp như tôi muốn có nó: (có cách nào khác "thích hợp" không?

Tôi nghĩ rằng quá nhiều lớp con có liên quan đến vấn đề khác của tôi… Tôi chỉ muốn phát ra một hạt, và khi tôi làm nó hoạt động.Nhưng SOMETIMES nó phát ra ba hạt, đôi khi hai… Và nó làm tôi phát điên vì điều đó. nó đem lại cho mỗi lần con số chính xác của các hạt ...

vì vậy, các câu hỏi ...

làm thế nào để phát ra các hạt trong một thời gian ngắn làm thế nào để làm việc với họ -. như st op, loại bỏ từ lớp cha mẹ, ... làm thế nào để phát ra số chỉ chính xác của các hạt

EDIT:

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]; 

Sau đó, trong phương pháp liên kết với nút Tôi làm điều này:

emitter.lifetime = 1.0; 

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

biên tập và CẬP NHẬT sau khi chuyển sang thái độ @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"]; 
}); 
+0

Bạn đã nhìn Táo mẫu mã ([Fireworks ] (http://developer.apple.com/library/mac/#samplecode/Fireworks/Listings/ReadMe_txt.html)) cho điều này? Về cơ bản họ làm những gì bạn đang cố gắng làm (một vụ nổ hạt ngắn) –

+0

Vâng, tôi có. Nhưng nó trông giống như nó phát thải ngắn. Nói chung có một lớp (với emitter) được thêm vào khung nhìn bên trong awakeFromNib và sau đó nó phát ra "pháo hoa" tròn và tròn. Nhưng hãy tưởng tượng tình hình khi tôi muốn bắt đầu chỉ một pháo hoa khi bạn chạm vào nút. Bạn làm điều đó như thế nào? Đó chính là điều tôi đang chỉ ra. – D33

Trả lời

9

Bạn có thể làm thi s bằng cách cấu hình tất cả mọi thứ một lần (không thêm một tế bào phát mới mỗi lần) và thiết lập các birthRate đến 0 (không có hạt sẽ được tạo ra). Sau đó, khi bạn muốn tạo các hạt của mình, bạn có thể đặt số birthRate thành số hạt trên giây mà bạn muốn tạo. Sau một thời gian nhất định, bạn đặt birthRate quay lại 0 để phát ra dừng. Bạn có thể sử dụng một cái gì đó như dispatch_after() để làm chậm trễ này.


Tôi đã làm điều tương tự một lúc và giải quyết nó như thế này. Sau đây sẽ tạo ra một vụ nổ nhanh chóng của các hạt. Lần sau khi bạn muốn các hạt phát ra, bạn thay đổi tỷ lệ sinh của "đám mây" trở lại 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"]; 
}); 
+0

Tôi hơi tuyệt vọng về nó. Tôi đã thử những gì bạn nói. Nhưng không thành công. Nhưng với một bước tiến nhỏ. Tôi đang tạo ra bộ phát trong viewDidLoad giống như trong đoạn mã tôi đã thêm vào câu hỏi ban đầu của mình. Nó hoạt động khá tốt NHƯNG với sự chậm trễ: (Khi tôi nhấn nút, không có gì xảy ra.Và sau một giây có thể nó mang lại cho tôi hạt đó. Điều gì xảy ra khi tôi nhấn nút hai lần là cho một câu chuyện khác: ( Tôi đã thử làm việc Nhưng khi tôi đặt nó thành 1 để bắt đầu phát xạ, nó đã cho tôi nhiều hơn 10 hạt thay vì một ... – D33

+0

Tôi không biết, nếu dispatch_after hoạt động lạ bạn có thể có lớp phát ra phát ra một ô birthRate = 1 lần lượt phát ra các tế bào thực của bạn, bằng cách này bạn có thể phát ra một phần không có tốc độ và thời gian sống tùy chỉnh để phát ra các hạt của bạn và sau đó thiết lập birthRate thành 0 sau 0.5 giây. hạt –

+0

Hm có lẽ tôi không hiểu câu trả lời của bạn một cách rõ ràng Nhưng tôi không gặp vấn đề gì với công văn (tôi đoán) Nó chỉ bắt đầu phát ra sau một số dela y và tôi không biết tại sao. Tôi nghĩ rằng nó giống như khi tôi tạo ra emitterLayer, nó bắt đầu chạy. Và khi tôi thay đổi tuổi thọ theo thời gian, nó vẫn đang làm việc với bộ hẹn giờ đang chạy. Vì vậy nó không phát ra ngay lập tức mà đúng lúc ... Và nó cho tôi sự chậm trễ. Hm. Bất kỳ ý tưởng làm thế nào để tránh điều này? – D33

0

OK, cuối cùng, sau nhiều giờ thử nghiệm và cố gắng phong cách rất khác nhau (khởi tạo, loại bỏ, phát thải cấu hình) Tôi đã đưa ra kết quả cuối cùng ... Và thực sự nó làm tôi rất khó chịu ...

--- Không thể!---

Ngay cả khi tôi tạo ra emitter và các hạt của nó mỗi khi tôi cần nó, nếu tôi chỉ đặt một hạt để phát ra, nó mang lại cho tôi hầu hết thời gian một hạt ... NHƯNG nó không phải là 100% và đôi khi nó chỉ phát ra ba hạt, hoặc hai ... Đó là ngẫu nhiên. Và điều đó rất tệ. Bởi vì nó là hiệu ứng hình ảnh ... :(

Dù bằng cách nào nếu ai đó có một mẹo làm thế nào để giải quyết việc này, xin vui lòng cho tôi biết ...

5

tôi đang tìm kiếm một giải pháp quá và thấy article này. .

nhìn vào Gist này cho hạt confetti, và phương pháp Stop Emitting

gì nó làm là:

  1. Thêm chế độ xem hạt vào chế độ xem hiển thị của bạn.
  2. Để hạt chạy nhanh đến mức bạn muốn.
  3. dừng phát xạ hạt mới với confettiEmitter.birthRate = 0.0;.
  4. đợi vài giây.
  5. xóa chế độ xem hạt.

Hy vọng điều đó có thể hữu ích.

6

Nhờ câu trả lời xuất sắc của foundrys over here Tôi giải quyết được một vấn đề rất giống với vấn đề này. Nó không liên quan đến việc ẩn bất kỳ khung nhìn nào. Tóm lại, nó đi như thế này:

Thiết lập phát của bạn như bạn sẽ bình thường, đặt tên cho các tế bào phát và cung cấp cho họ một giá trị tỷ lệ sinh của zero:

-(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]; 
} 

Sau đó, tạo ra một phương pháp bắt đầu có thể tự động tắt phát thải sau một thời gian ngắn và phương pháp dừng:

-(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"]; 
} 

Trong ví dụ này, tôi tắt chế độ phát ra sau 0,2 giây nhưng sử dụng bất kỳ thứ gì bạn thấy phù hợp.

Giải pháp tối ưu sẽ là nếu Apple đã triển khai phương pháp bắt đầu/dừng, nhưng ngắn gọn là tôi thấy đây là giải pháp thỏa đáng.

+1

Đó là câu trả lời duy nhất có hiệu quả đối với tôi. Cảm ơn –

+0

cảm ơn vì câu trả lời đơn giản và thanh lịch. Tôi vẫn còn bối rối về cách tôi có thể đặt emittercell1 và emittercell2 ở hai vị trí khác nhau trên emitterlayer. cảm ơn –

0

Bạn đã xem xét lợi dụng các thuộc tính có thể hoạt hình của 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. Thuộc tính đại biểu cũng có thể là con số không nếu bạn không muốn làm bất cứ điều gì trên hoàn thành, như trong animationDidStop: hoàn thành:

  2. Các hằng kBirthRateDecayAnimationKey & sử dụng kStandardAnimationDuration quy ước của tôi, không phải của Apple.

1

CAEmitter.birthRate có thể hoạt hình.Giả sử bạn đã thêm một vài CAEmitterLayer s để xem, bạn có thể làm điều này để animate sự phân rã của các tỷ lệ sinh và sau đó khởi động lại sau một vài giây:

- (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]; 
    } 
    } 
}