ヤフー株式会社は、2023年10月1日にLINEヤフー株式会社になりました。LINEヤフー株式会社の新しいブログはこちらです。LINEヤフー Tech Blog

テクノロジー

ARKitでキャラクターをナビ役にした実装例 〜 Yahoo! MAPアプリのAR新機能

Yahoo! JAPAN Advent Calendar 2022の19日目の記事です。

こんにちは。Yahoo! MAPでiOSアプリの開発を担当している徳元二宮です。
こちらでアナウンスさせていただいた通り、2022年11月15日にYahoo! MAPにおけるARモードのアップデートを行いました。
スマートフォンを空に向けると、目的地の方向の上空にキャラクターがARで浮かぶようになりました。本稿では、このアップデートを行った際にどのような技術を使ったのかを紹介します。

Yahoo! MAPについて

Yahoo! MAPはヤフーで提供している地図アプリです。
本稿では、iOS版で提供しているARモードという機能に対して行ったアップデートについてご紹介します。
また、Android版では現在、ARモードを提供していませんが今後実装することを検討中ですので、Androidユーザーの方も良かったら使ってみてくださいね。

ARモードについて

今回、ご紹介するARを使ったARモードはiOSのフレームワークであるARKitを使って実装した歩行者向けのナビゲーションを支援する機能です。
2018年3月にこちらで紹介しているような内容で最初のバージョンとしてリリースを行いました。

ナビゲーション精度の向上方法

ARGeoTrackingConfiguration

アップデート前のARモードでは、ARWorldTrackingConfigurationを使いGPSの情報から各ルートラインの緯度経度の位置をAR空間上にマッピングしていました。
今回のアップデートを行った際に、一部のエリアではARGeoTrackingConfigurationを使うように変更しました。
ARGeoTrackingConfigurationを設定し、ARのセッションを開始するとGPSから取得できる現在地を示す緯度経度とデバイスのカメラから得られる風景画像からAR空間を構築します。
これはVPS(Visual Positioning System)と呼ばれる技術がベースとなっており、デバイスから取得できる現在地と風景と、その付近の風景画像のデータを突合し自己位置推定を行っています。
GPSによる緯度経度や方位と比べて、非常に精度の高い情報を得ることができます。
以下のように実装することで、ARGeoTrackingConfigurationを使ってAR空間を構築できます。

    let configuration = ARGeoTrackingConfiguration()
    session.run(configuration)

また、ARGeoTrackingConfigurationはiOS 14以上かつA12以上のバージョンのチップが搭載されたCellularモデルの端末のみで利用できます。
端末が対応されている場合はARGeoTrackingConfiguration.isSupportedtrueになるため、セッションを開始する前に確認をする必要があります。
さらに対応エリアも限定されており、日本では2022年12月時点で福岡市、広島市、京都市、名古屋市、大阪府、東京都、横浜市のエリアに対応しています。
対応エリア内でも、建物の中などでは利用することができないためこれについても、セッションを開始する前に以下のようにして確認する必要があります。

    ARGeoTrackingConfiguration.checkAvailability { isAvailable, error in
        // 通信に失敗した場合などはerrorにその情報が入っています
        if isAvailable {
            // 利用できる範囲である
        } else {
            // 利用できない範囲である
        }
    }
}

セッションを開始した後に、前述の通り周辺の風景画像から空間を構築するLocalizationという処理を行います。
Localizationの過程で現在の状態を取得できます。
これは、ARSessionObserverプロトコルで定義されている、session(_:didChange:)メソッドの引数を参照することで取得可能です。
Yahoo! MAPアプリでは、ARSCNViewを使っていますがこのビューが持つdelegateプロパティに対して状態が更新された場合に上述のメソッドが呼ばれます。
session(_:didChange:)の引数の内、geoTrackingStatusを参照することで変更後の状態とその理由を得ることができます。
Yahoo! MAPではsession(_:cameraDidChangeTrackingState:)より取得できるAR自体のトラッキング精度がnormalになり尚且この状態が、initializingからlocalizedに変更されたタイミングでARのナビゲーションに必要なオブジェクトを配置するようにしています。

ARGeoAnchor

ARGeoAnchorARWorldTrackingConfigurationを指定してセッションを開始している場合のみ利用可能なARAnchorです。このARGeoAnchorは他のARAnchorと違い、緯度経度と高度(altitude)を指定できます。また、高度(altitude)を指定しない場合は指定した緯度経度に相当する地面の高さを自動的に計算し、それに対応するY座標が指定されます。

ルックアップ機能の実現方法

ナビゲーション精度の向上に加えて、今回はヤフーの公式キャラクターであるえんじんが上空で手を振りながら目的地の方向に誘導する機能を追加しました。ルートを見ずとも上空にかざすことで目的地への方向を定期的に確認できます。

ルックアップ機能

BillBoard

えんじんが常にこちらを向くようにBillBoardという仕組みを使っています。
重力方向であるY軸に対し、カメラのほうを常に向くように制約をつけています。
ARモードではSceneKitが提供するSCNBillboardConstraintを使っています。
プログラム上で設定することも可能ですが、対象のscnファイルをXcodeで開きそこで設定することも可能です。
SceneGraphから対象のNodeを選択し、Show the Node InspectorのConstraintsにBillboardを追加し、y軸にチェックを入れるだけで設定することが可能です。

BillBoard

SCNAction

えんじんが風船で浮いているような表現をするためにアニメーションを付与しています。手をふるような複雑なアニメーションは、デザイナーによってデザイナーが3Dオブジェクトに直接設定していますが、上下に動くだけの単純なアニメーションであれば実装側でカバーすることも可能です。えんじんが浮いているように表現するためにSCNActionを使って、以下のように実装をしています。

    let moveUp = SCNAction.moveBy(x: 0, y: 1, z: 0, duration: 1)
    moveUp.timingMode = .easeInEaseOut;
    let moveDown = SCNAction.moveBy(x: 0, y: -1, z: 0, duration: 1)
    moveDown.timingMode = .easeInEaseOut;
    let moveSequence = SCNAction.sequence([moveUp,moveDown])
    let moveLoop = SCNAction.repeatForever(moveSequence)
    // nodeはえんじんのscnファイルから生成したSCNNodeのインスタンスです
    node.runAction(moveLoop, forKey: "floating")

これで上下に1mずつ繰り返し動くようなアニメーションを表現することが可能です。
ARモードでは、timingMode.easeInEaseOutを設定しアニメーションの動きを調整していますが他にもいくつか設定できるパラメータが用意されています。
SCNAction.sequenceは指定したアニメーションを順番に実行するものであり、SCNAction.repeatForeverでこれを実行することで繰り返し実行することが可能です。

SCNConstraint

えんじんの距離についてはあまりに離れすぎても見えないので、一定以上離れないようにしておく必要があります。
また、えんじんの高さについても、検証を重ね目的地付近では数メートル程度にしておくと見やすく離れた位置では数十メートル確保しないと見づらいと考えました。
そのため、SCNTransformConstraint.positionConstraintを使ってえんじんとカメラの距離に応じて、以下のようにしてえんじんとの距離と高さを変更しています。

let positionConstraint = SCNTransformConstraint.positionConstraint(inWorldSpace: true) { [weak self] node, vector in
    guard let self = self, let camera = self.pointOfView else {
        return vector
    }
    let enjinPosition = self.enjin.worldPosition
    var position = enjinPosition
    let maxDistance: CLLocationDistance = 30
    let maxHeight: CLLocationDistance = 25
    let minHeight: CLLocationDistance = 4
    // X-Z平面上での距離を計算しています
    let distance = camera.worldPosition.length(between: goal)
    guard distance > 0 else {
      position.y = minHeight
      return position
    }
    // 30m以上離れないようにした上で、現在地と目的地との延長線上にえんじんを配置しています
    let ratio = min(1.0, maxDistance / distance)
    position = camera.worldPosition.position(between: goal, ratio: ratio)
    // 総距離に対する残りの距離の割合をみてえんじんの高さを4m以上25m以下にするよう調整しています
    let height = max(minHeight, min(1.0, distance / totalDistance) * maxHeight)
    position.y = height
    return position
}

さいごに

今回はYahoo! MAPアプリでのARモードのアップデート内容と、実装の概要をご紹介しました。
今後、Androidへの対応やARKitを使った便利な機能を追加していくことも検討中です。
また、ARモード以外にもたくさんの便利な機能があるのでYahoo! MAPアプリをぜひ一度使ってみていただけたらと思います!

こちらの記事のご感想を聞かせください。

  • 学びがある
  • わかりやすい
  • 新しい視点

ご感想ありがとうございました


徳元 健太
iOSエンジニア
iOS版のYahoo! MAPアプリの開発を担当しています。アプリの主要な機能に加えてARを使った機能の開発にも携わっています。
二宮 一浩
プロダクトマネージャー
ヤフーのアプリ統括部でスーパーなエンジニアたちと一緒に刺激的なお仕事をしています。最近のマイブームはSpatial webとLLMです。

このページの先頭へ