ARチキン（Swift）

![Xcode-14.1](https://img.shields.io/badge/Xcode-14.1-brightgreen) ![iOS-16.0](https://img.shields.io/badge/iOS-16.0-brightgreen) 

## はじめに
先日かわいいチキンを作ったのですがどうせなら AR で表示したいという欲が出てきました:smiling_imp:

<iframe width="300" height="400" src="https://www.youtube.com/embed/kdzrXBCjF7w" title="SpriteKitで2D横スクロールゲームをつくろう" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

[SpriteKitで2D横スクロールゲームをつくろう（その１）](https://qiita.com/am10/items/df42c1f0091e6c4705e2)

調べていると [ARSKView](https://developer.apple.com/documentation/arkit/arskview) というまさに AR でチキンを表示するために用意されたような View を発見しました。

これはもうやるしかない！ということで AR でチキンを表示します。

完成形はこんな感じです:chicken:

<img width="200" alt="chicken" src="https://user-images.githubusercontent.com/34936885/208313488-cade0fa6-2b9e-40fd-a6ab-668ddab0eca4.gif">

## やりたいこと
やりたいことは下記。

1. 画像マーカーを認識する
2. 認識した画像マーカーにチキンを表示する

## 画像マーカー設定
下記記事を参考に画像マーカーを設定します。

* [ARKitで作るAR名刺](https://qiita.com/kboy/items/d5c7e0f4174da4b8d5f0)
* [ARKit画像マーカーに適した画像適さない画像](https://qiita.com/kboy/items/988af3a1663e84f2f328)

1. Assets > AR and Textures > New AR Resource Group で Group 追加
2. マーカー用の画像を追加
3. マーカーのサイズ指定（今回はてきとーに 15cm * 15cm で設定しました）

<img width="500" alt="marker" src="https://user-images.githubusercontent.com/34936885/207549147-e191af8a-0b8b-431a-97e2-b79d6fbda7fc.png">

マーカーにはちょうど手元にあったオレイカルコスの結界を使います（なんか警告出てますが気にしません）。

これでマーカーの設定は終わりです。

## ARSKViewを表示する
とりあえず `ARSKView` を表示してみます。

1. info.plist に「Privacy - Camera Usage Description」を追加
2. Storyboard で `ViewController` に `ARSKView` を追加
3. `ViewController` に `ARSKView` を接続
4. `ViewController` に下記追加
    ```swift
    import UIKit
    import ARKit

    final class ViewController: UIViewController {

        @IBOutlet private weak var arskView: ARSKView!

        override func viewDidLoad() {
            super.viewDidLoad()
            arskView.presentScene(.init(size: arskView.frame.size))
            let configuration = ARWorldTrackingConfiguration()
            arskView.session.run(configuration)
        }
    }
    ```

これで動きます！（カメラ映るだけですが）

## マーカーを認識させる
次にマーカーを認識できるようにしていきます。

以下のように `ARWorldTrackingConfiguration` から　`ARImageTrackingConfiguration` に変更し `ARSKViewDelegate` を追加します。

```swift
arskView.delegate = self

let configuration = ARImageTrackingConfiguration()
let images = ARReferenceImage.referenceImages(inGroupNamed: "AR Resources", bundle: nil)
configuration.trackingImages = images!
configuration.maximumNumberOfTrackedImages = 1
arskView.session.run(configuration)

extension ViewController: ARSKViewDelegate {

    func view(_ view: ARSKView, nodeFor anchor: ARAnchor) -> SKNode? {
        print("マーカーみっけ！！！")
        return nil
    }
}
```

これでマーカーを認識するとコンソールに「マーカーみっけ！！！」と表示されるようになりました。

## チキンを表示する
いよいよチキンを表示します。

`ARSKViewDelegate` の部分を下記のように修正します。

```swift
extension ViewController: ARSKViewDelegate {

    func view(_ view: ARSKView, nodeFor anchor: ARAnchor) -> SKNode? {
        let parentNode = SKNode()
        let chicken = SKSpriteNode(texture: .init(imageNamed: "c1"), size: .init(width: 32, height: 32))
        let jumpTextures: [SKTexture] = [
            .init(imageNamed: "j1"),
            .init(imageNamed: "j2")
        ]
        let action = SKAction.group([
            .animate(with: jumpTextures, timePerFrame: 0.2),
            .sequence([
                .wait(forDuration: 0.2),
                .moveBy(x: 0, y: 10, duration: 0.2),
                .moveBy(x: 0, y: -10, duration: 0.2)
            ])
        ])
        chicken.run(.repeatForever(action))
        parentNode.addChild(chicken)
        return parentNode
    }
}
```

`view(_ view: ARSKView, nodeFor anchor: ARAnchor)` で `SKNode` を返すと指定の `ARAnchor` にノードが追加されるのでこれでチキンが表示できます。

`parentNode` に `chicken` を add しているのはそのまま `chicken` を返すと `ARAnchor` で固定されてしまいアニメーションで動かせないためです。

参考：[ARKitでつくったAR空間上に置いたSpriteKitのSKNodeをアニメーションで動かす方法](https://qiita.com/toshi586014/items/f3226f806eeba0eb3d22)

## おわりに
意外とかんたんに AR でかわいいチキンの表示ができました:chicken:

50 行いかないくらいでサクッと AR が試せます！いい時代ですね:kissing_closed_eyes:

<details><summary>ソース全体</summary><div>

```swift
import UIKit
import ARKit

final class ViewController: UIViewController {

    @IBOutlet private weak var arskView: ARSKView!
    private let jumpTextures: [SKTexture] = [
        .init(imageNamed: "j1"),
        .init(imageNamed: "j2"),
    ]

    override func viewDidLoad() {
        super.viewDidLoad()

        arskView.delegate = self
        arskView.presentScene(.init(size: arskView.frame.size))
        let configuration = ARImageTrackingConfiguration()
        let images = ARReferenceImage.referenceImages(inGroupNamed: "AR Resources", bundle: nil)
        configuration.trackingImages = images!
        configuration.maximumNumberOfTrackedImages = 2
        arskView.session.run(configuration)
    }
}

extension ViewController: ARSKViewDelegate {

    func view(_ view: ARSKView, nodeFor anchor: ARAnchor) -> SKNode? {
        let parentNode = SKNode()
        let player = SKSpriteNode(texture: .init(imageNamed: "c1"), size: .init(width: 32, height: 32))
        let action = SKAction.group([
            .animate(with: jumpTextures, timePerFrame: 0.2),
            .sequence([
                .wait(forDuration: 0.2),
                .moveBy(x: 0, y: 10, duration: 0.2),
                .moveBy(x: 0, y: -10, duration: 0.2)
            ])
        ])
        player.run(.repeatForever(action))
        parentNode.addChild(player)
        return parentNode
    }
}
```
</div></details>

## 参考
* [ARKitで作るAR名刺](https://qiita.com/kboy/items/d5c7e0f4174da4b8d5f0)
* [ARKit画像マーカーに適した画像適さない画像](https://qiita.com/kboy/items/988af3a1663e84f2f328)
* [ARKitでつくったAR空間上に置いたSpriteKitのSKNodeをアニメーションで動かす方法](https://qiita.com/toshi586014/items/f3226f806eeba0eb3d22)