LoginSignup
39
38

More than 5 years have passed since last update.

UIKit上でパーティクルエフェクトを表示する

Last updated at Posted at 2013-03-14

iOS5より、Core Animationでパーティクルシステムがサポートされ、UIKitで実装されたUI上でパーティクル表現を簡単に行えるようになりました。

ここでは CAEmitterLayer と CAEmmiterCell を用いたパーティクルエフェクトの基本的な実装方法を説明し、入れ子にして花火のような段階的なエフェクトを実現する方法や、動的にパラメータを変更する方法を紹介します。

基本的な実装方法

1. パーティクル画像をプロジェクトに追加する

パーティクルシステムは、1つの画像を大量に描画することで多様な表現を行うものなので、その素となる画像が必要になります。ここでは、わかりやすいように次のようなシンプルな円形のpng画像を使います。

パーティクル画像

(※視認しやすいよう背景を黒にして載せています)

プロパティから色を変えられるので、白ベースの画像を用いることが多いですが、あらかじめ着色した画像を用いてもOKです。

2. QuartzCore.frameworkをプロジェクトに追加し、ヘッダをインポート

#import <QuartzCore/QuartzCore.h>

3. CAEmitterLayerオブジェクトを生成

パーティクルを発生させるレイヤーであるCAEmitterLayerオブジェクトを1つ生成します。

CAEmitterLayer *emitterLayer = [CAEmitterLayer layer];
CGSize size = self.view.bounds.size;
emitterLayer.emitterPosition = CGPointMake(size.width / 2, size.height / 2);
emitterLayer.renderMode = kCAEmitterLayerAdditive;
[self.view.layer addSublayer:emitterLayer];

renderModeプロパティは各パーティクルを重ね合わせる際の描画モードを指定できます。ここで使用しているkCAEmitterLayerAdditiveは加算による重ね合わせが行われます。

emitterPositionプロパティにはパーティクルを発生させる座標を指定します。

4. CAEmitterCellオブジェクトを生成

パーティクルの発生源となるCAEmitterCellオブジェクトを生成します。

CAEmitterCell *emitterCell = [CAEmitterCell emitterCell];
UIImage *image = [UIImage imageNamed:@"particle.png"];
emitterCell.contents = (__bridge id)(image.CGImage);
emitterCell.emissionLongitude = M_PI * 2;
emitterCell.emissionRange = M_PI * 2;
emitterCell.birthRate = 800;
emitterCell.lifetimeRange = 1.2;
emitterCell.velocity = 240;
emitterCell.color = [UIColor colorWithRed:0.89
                                    green:0.56
                                     blue:0.36
                                    alpha:0.5].CGColor;

contentsプロパティには、手順1で用意したパーティクル画像をCGImageRef型でセットします。

他にもプロパティがたくさんありますが、パーティクルの発生具合を調整するためのものです。それぞれの具体的な意味は後述します。

5. CAEmitterCellsプロパティを設定

CAEmitterLayerはemitterCellsというプロパティを持ち、NSArray型で複数のCAEmitterCellオブジェクトを登録できます。

self.emitterLayer.emitterCells = @[emitterCell];

ここでは手順4で生成したCAEmitterCellオブジェクトを登録しています。

以上で次のようなパーティクルエフェクトを表示できます。

particle2

CAEmitterCellを入れ子にして用いる

先ほどの手順ではCAEmitterLayerのemitterCellsプロパティにCAEmitterCellオブジェクトの配列をセットしましたが、CAEmitterCell自体もemitterCellsプロパティを持っており、(CAEmitterLayerと同様に)CAEmitterCellオブジェクトの配列を持つことができます。

これはどういうことかというと、CAEmitterCellから発されたパーティクル自身がまたパーティクルの発生源となれる、ということを意味しています。

例えば次のように、ひとつのCAEmitterCellオブジェクトから、

  • 上昇中のパーティクルの発生源となるCAEmitterCellオブジェクト
  • 破裂後に飛散するパーティクルの発生源となるCAEmitterCellオブジェクト

の2種類のCAEmitterCellオブジェクトを発生させるようにすると、花火のようなパーティクルエフェクトを実現することができます。

// パーティクル画像
UIImage *particleImage = [UIImage imageNamed:@"particle.png"];

// 花火自体の発生源
CAEmitterCell *baseCell = [CAEmitterCell emitterCell];
baseCell.emissionLongitude = -M_PI / 2;
baseCell.emissionLatitude = 0;
baseCell.emissionRange = M_PI / 5;
baseCell.lifetime = 2.0;
baseCell.birthRate = 1;
baseCell.velocity = 400;
baseCell.velocityRange = 50;
baseCell.yAcceleration = 300;
baseCell.color = CGColorCreateCopy([UIColor colorWithRed:0.5
                                                   green:0.5
                                                    blue:0.5
                                                   alpha:0.5].CGColor);
baseCell.redRange   = 0.5;
baseCell.greenRange = 0.5;
baseCell.blueRange  = 0.5;
baseCell.alphaRange = 0.5;

// 上昇中のパーティクルの発生源
CAEmitterCell *risingCell = [CAEmitterCell emitterCell];
risingCell.contents = (__bridge id)particleImage.CGImage;
risingCell.emissionLongitude = (4 * M_PI) / 2;
risingCell.emissionRange = M_PI / 7;
risingCell.scale = 0.4;
risingCell.velocity = 100;
risingCell.birthRate = 50;
risingCell.lifetime = 1.5;
risingCell.yAcceleration = 350;
risingCell.alphaSpeed = -0.7;
risingCell.scaleSpeed = -0.1;
risingCell.scaleRange = 0.1;
risingCell.beginTime = 0.01;
risingCell.duration = 0.7;

// 破裂後に飛散するパーティクルの発生源
CAEmitterCell *sparkCell = [CAEmitterCell emitterCell];
sparkCell.contents = (__bridge id)particleImage.CGImage;
sparkCell.emissionRange = 2 * M_PI;
sparkCell.birthRate = 8000;
sparkCell.scale = 0.5;
sparkCell.velocity = 130;
sparkCell.lifetime = 3.0;
sparkCell.yAcceleration = 80;
sparkCell.beginTime = risingCell.lifetime;
sparkCell.duration = 0.1;
sparkCell.alphaSpeed = -0.1;
sparkCell.scaleSpeed = -0.1;

// baseCellからrisingCellとsparkCellを発生させる
baseCell.emitterCells = [NSArray arrayWithObjects:risingCell, sparkCell, nil];

// baseCellはemitterLayerから発生させる
self.emitterLayer.emitterCells = [NSArray arrayWithObjects:baseCell, nil];

花火の「上昇後に破裂する」という2段階のプロセスを表現するため、sparkCellのbeginTimeプロパティに、risingCellのlifeTimeプロパティと同じ値をセットすることで、risingCellによる放射が完了した後にsparkCellによる放射が開始するようにしています。

particle3

動的にパラメータを変更する

CAEmitterCellのnameプロパティに値をセットしておけば、パラメータを動的変更することができます。

花火のサンプルを例にとると、baseCellのnameプロパティに下記のように名前をセットしておけば、

baseCell.name = @"fireworks";

setValue:forKeyPath:メソッドを用いて次のようにパラメータを変更することができます。

[self.emitterLayer setValue:@0
                 forKeyPath:@"emitterCells.fireworks.birthRate"];

emitterCellsプロパティが保持する配列内にある "fireworks" という名前の CAEmitterCell オブジェクトの birthRate プロパティに値 0 をセットしています。

birthRateに 0 をセットすることは、パーティクルの発生を停止することを意味するので、上記コードは動的にパーティクルエフェクトを停止していることになります。

CAEmitterCellの各プロパティの効果

種類が多く、言葉で定義を説明するよりも実際に動作させながら「変更してみると何が起きるか」を確認した方が理解が早いので、ここでは必要最小限のものについて説明します。

  • birthRate:1秒間に生成するパーティクルの数。
  • lifetime:パーティクルが発生してから消えるまでの時間。単位は秒。
  • color:パーティクルの色。
  • velocity:パーティクルの秒速。
  • emissionRange:パーティクルを発生する角度の範囲。単位はラジアン。

lifetimeRangevelocityRangeのように、xxxxRangeという名前のプロパティは、それぞれ各プロパティにランダム性をもたせるために、幅を設定するものです。たとえば、velocityRangeは、velocityに設定した値に幅を持たせるためのプロパティです。

サンプルコード

Githubにアップしてあります。

CAEmitterSample

39
38
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
39
38