iOS の ParticleSystem を試してみた

iOS にも ParticleSystem があるのを知っていますか？
`SceneKit` の `SCNParticleSystem` を使えば、簡単にリッチな視覚効果を再現することができます。
一般的に、パーティクルシステムは煙や火花、雲などをシミュレーションするために用いられます。

## ベースコード

パーティクルシステムの実装に必要な最低限のコードは以下になります。
やっていることは、サイズが4cmのピンク色のパーティクルを1秒間に1個ずつ生成しているだけです。
この状態では、画面にピンク色の点が見えるだけになります。

今回は ARKit と組み合わせ、パーティクルシステムを付与したノードを [ARSCNView](https://developer.apple.com/documentation/arkit/arscnview) の rootNode に追加することで3次元空間上にパーティクルを配置しています。

```swift
let node = SCNNode()
node.position = .init(0, 0, -0.4)

let ps = SCNParticleSystem()
ps.birthRate = 1
ps.particleSize = 0.04
ps.particleColor = .systemRed

node.addParticleSystem(ps)
sceneView.scene.rootNode.addChildNode(node)
```

![output1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190870/2f1e6bd1-c373-ac37-d80f-6876bc579aca.gif)

## 生成範囲の調整

[emitterShape](https://developer.apple.com/documentation/scenekit/scnparticlesystem/1522737-emittershape) を設定することで、パーティクルの生成範囲を指定することができます。
ベースコードのように何も設定しない場合、すべてのパーティクルは一点から生成されます。

```swift
ps.emitterShape = SCNSphere(radius: 0.04)
```

| SCNPlane | SCNSphere | SCNBox | SCNPyramid | SCNTorus |
| --- | --- | --- | --- | --- |
| ![output7.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190870/2d99a6fb-fa0d-7a4f-899d-5f42050301d6.gif) | ![output3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190870/4db9f29e-005a-9f1c-3f29-d0a779496616.gif) | ![output4.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190870/db350e31-6701-6dc2-6dbd-b65bddad741b.gif) | ![output5.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190870/15386500-1306-9bd2-5ec1-1e78bb00745b.gif) | ![output6.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190870/f63ecfcb-0955-1ff6-0649-12fd8ee4b388.gif) |



## サイズ

各パーティクルのサイズは [particleSize](https://developer.apple.com/documentation/scenekit/scnparticlesystem/1523508-particlesize) で調整可能です。
また、[particleSizeVariation](https://developer.apple.com/documentation/scenekit/scnparticlesystem/1522716-particlesizevariation) を設定すれば各パーティクルのサイズを特定の範囲内でランダムに決定することができます。particleSize が 1.0、particleSizeVariation が 1.0 の場合、サイズの範囲は 0.5 ~ 1.5 となります。

```swift
ps.particleSize = 0.004
ps.particleSizeVariation = 0.004
```

![output8.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190870/937ecfcf-ac19-33aa-33a7-7f06c472b012.gif)


## 色

各パーティクルの色は [particleColor](https://developer.apple.com/documentation/scenekit/scnparticlesystem/1523248-particlecolor) で調整可能です。
また、[particleColorVariation](https://developer.apple.com/documentation/scenekit/scnparticlesystem/1523639-particlecolorvariation) を設定することでサイズと同様に値のランダム化ができます。particleColorVariation の型は `SCNVector4` となっており、**色相**、**彩度**、**明度**、**アルファ**の順に4つの値が必要です。

```swift
ps.particleColor = .systemRed
ps.particleColorVariation = .init(x: 1.0, y: 1.0, z: 1.0, w: 1.0)
```

| 色相のみ変化 { 1, 0, 0, 0 } | 彩度のみ変化 { 0, 1, 0, 0 } | すべて変化 { 1, 1, 1, 1 } |
| --- | --- | --- |
|![output9.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190870/e0701cd9-7b0b-61cd-bcad-9737c836c594.gif) | ![output10.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190870/9dc135e6-555c-5c03-993d-46b4d3233fd0.gif) | ![output11.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190870/32de1371-ade5-0766-50a5-797567c42fdc.gif) |


## プロパティのアニメーション

Animatable となっている属性はアニメーションを行うことができます。最も簡単にアニメーションを設定する方法は、[propertyControllers](https://developer.apple.com/documentation/scenekit/scnparticlesystem/1522775-propertycontrollers) を設定する方法だと思われます。
propertyControllers は `[SCNParticleSystem.ParticleProperty : SCNParticlePropertyController]?` 型で、[特定の属性](https://developer.apple.com/documentation/scenekit/scnparticlesystem/particleproperty)に [SCNParticlePropertyController](https://developer.apple.com/documentation/scenekit/scnparticlepropertycontroller) を紐付ける形になります。

以下の例では、**白 -> ピンク -> 青 -> 緑** の順に各パーティクルの色が変化するようなアニメーションを付与しています。

```swift
let colorAnimation = CAKeyframeAnimation()
colorAnimation.values = [
    UIColor.white,
    UIColor.systemPink,
    UIColor.systemBlue,
    UIColor.systemGreen
]
colorAnimation.keyTimes = [0, 1, 2, 3]
colorAnimation.duration = 3

ps.propertyControllers = [.color: .init(animation: colorAnimation)]
```

![output12.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190870/2fee77a8-2d53-0377-cae5-57e90b8284b2.gif)


## 最後に

今回は iOS のパーティクルシステムについての簡単な紹介でしたが、SCNParticleSystem には他にも多くのプロパティが用意されています。パーティクルに動きをつけたり、パーティクルから新たなパーティクルを生成することで様々な表現が可能です。
今回紹介した基本的なプロパティを操作するだけでも、アーティスティックな作品をつくることが可能かと思います。

![output2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190870/de3ef111-765d-444c-3c60-eeacba7b42fe.gif)