ARKitで位置情報を共有するためのモデルをCodableで作成する


昨日のARKitで空間共有して端末間通信する方法22選で、端末間でデータを送受信するのに使うのはCodableがベストだと書きました。

今日は具体的にそのCodableの使い方を紹介したいと思います。

Codableとは

Swift4から使えるJSONのエンコードとデコードが簡単にできるSwift標準ライブラリです。めちゃ簡単に使えます。

以下はJSONパーサーのパフォーマンス比較表です。DecodableのJSONをデコードするのにかかる時間は比較的短いことが分かります。これで標準なんだから使わない手はないですね。(Codableの型はDecodable & Encodable)


1

基本Entity

それではARKitの位置共有で使うための基本データをCodableに準拠させてどう作っていくのか紹介します。

Vector3Entity

位置座標x, y, zやオイラーアングルはSCNVector3で表されるのでそれに対応するEntityを作ります。

struct Vector3Entity: Codable {
    let x: Float
    let y: Float
    let z: Float
}

Vector4Entity

4*4行列の要素や、rotationのSCNVector4に対応するために、対応するEntityを作ります。

struct Vector4Entity: Codable {
    let x: Float
    let y: Float
    let z: Float
    let w: Float
}

実践的なEntity

PlayerEntity

例えばデバイス間で各自のデバイス位置を共有したいとき、以下のようなPlayerEntityを作って座標(position)と向き(eulerAngles)を共有します。

struct PlayerEntity: Codable {
    let position: Vector3Entity
    let eulerAngles: Vector3Entity
}

TransformEntity

4*4行列を送受信するためのEntityを作ります。このtransformはデバイス間で座標合わせをするときに必要なことが多いです。(参考: ARKitで空間共有して端末間通信する方法22選)

struct TransformEntity: Codable {
    let column0: Vector4Entity
    let column1: Vector4Entity
    let column2: Vector4Entity
    let column3: Vector4Entity
}

ARKitにおける位置情報共有の例

送信する側

例えばARSCNViewDelegateのrenderer(:updateAtTimeは毎フレームごとに呼ばれるので、ここでカメラの位置を取得して毎フレームごとに相手に送信することができます。(毎フレームごとに送るべきかは置いといて)

// MARK: - <#ARSCNViewDelegate#>
extension CameraViewController: ARSCNViewDelegate {
    func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
        guard let camera = sceneView.pointOfView else {
            return
        }
        // TODO: PlayerEntityのイニシャライザ作っとく
        let entity = PlayerEntity(position: camera.position,
                                  eulerAngles: camera.eulerAngles)
        let data: Data = try! JSONEncoder().encode(entity)

        // TODO: dataを送る

    }

受信する側

repositoryというクラスに相手からデータを受け取ったときに発火するonEventというクラスを用意したとして、以下のようにそのobjectをparseすることができます。

repository.onEvent { object in
    guard let data = object as? Data else {
        return
    }

    do {
        let decoder: JSONDecoder = JSONDecoder()
        let entity: PlayerEntity = try decoder.decode(PlayerEntity.self, from: data)
        // これを使って相手のデバイスを空間上で再現する
        let position = entity.position
        let eulerAngles = entity.eulerAngles
    } catch {

    }
}

まとめ

以上のようなパターンでデバイス間で位置情報を共有することができます。位置情報アプリを作りたい方の参考になれば幸いです。