【Unity+ARCore】位置情報から飲食店に空席状況を表示させるには | 前編

# 作ったサービス

<iframe width="560" height="315" src="https://www.youtube.com/embed/sV6yb6Basd4?si=8TCXWXPMQ761pIc3" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

**目次**
[1.本記事でできること](#本記事でできること)
[2.動作環境](#動作環境)
[3.システム構成図](#システム構成図)
[4.Unityプロジェクトの作成](#Unityプロジェクトの作成)
[5.ARCore Extensionsインストール](#ARCoreExtensionsインストール)
[6.ARCoreAPIの取得方法](#ARCoreAPIの取得方法)
[7.特定の位置にオブジェクトを表示させる](#特定の位置にオブジェクトを表示させる)
[8.まとめ](#まとめ)

# 本記事でできること
こんにちは。位置情報から飲食店に空席状況をAR空間に表示させるサービスを作ってます。前編ではUnityとARCore Geospatial APIを使い特定の位置情報のもとにARコンテンツを表示する方法を紹介します。
ARCore Geospatial APIのインストールやAPIの発行について困っている方の参考になればと思います。

# 動作環境
##### AR環境
・Unity 2022.3.49f1
・ARFoundation 5.1.5
・Google ARCore XR Plugin
・ARCore Extensions: 1.46.0

##### 実行デバイス
Galaxy S22 SC-51C

##### サーバー環境
Flask　フレームワーク
ngrok　ローカルサーバー

# システム構成図
![構成図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3929428/f5fd4d28-0b8f-3a1e-446f-bd175988d3c7.png)


# Unityプロジェクトの作成
上記の環境のUnityバージョンをダウンロードします。
https://unity.com/ja/releases/editor/archive

Unity Hubで`AR Moible`テンプレートを使用してプロジェクトを作成します。

![スクリーンショット 2024-11-08 101617.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3929428/54cc2ffd-160b-d006-ef98-8c5c4560b7bc.png)

プロジェクトを開けたらそのまま実行します。Game ViewでAR空間をシミュレーションすることができます。実行できたらOKです。

:::note warn
AR Mobileテンプレートを使ってない方は最新バージョンのARFoundationとGoogle ARCore XR Pluginの導入を済ませてください。
:::


# ARCore Extensionsインストール


![スクリーンショット 2024-11-08 105127.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3929428/69706a90-a6db-dec3-744d-893cae091e1f.png)

ARCore Extensions: 1.46.0を導入するには`Window` > `Package Manager`を開き左上の+ボタンを押し`Add pacake from git URL...`を押します。![スクリーンショット 2024-11-08 110255.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3929428/6c22613e-1460-3ae1-c5c0-1b3604179bb7.png)
以下のGit URLをコピーして貼り付けます。
https://github.com/google-ar/arcore-unity-extensions.git#arf5

:::note alert
**XROriginが割り当てられない方**
最新のバージョンだと`XROrigin`がスクリプトに参照できないため上記のURLでインストールしてください。
`XROrigin`を参照できない方はサンプルシーンをAssetsフォルダーから直接削除してインポートし直すと参照できるようになります。
:::

![sampleシーン.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3929428/c3f9fe8a-0569-d681-fea8-bafc45c5f806.png)

最新バージョンがインストールされてることを確認したら`Geospatial Sample`をインポートします。
![スクリーンショット 2024-11-08 114452.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3929428/4de96197-1bd7-ac6a-83e0-df292ae57a44.png)
`Assets`から`ARCore Extensions`のScenesから`GeospatialArf5`シーンを開きます。

![Xrorigin.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3929428/d1919344-abec-99af-591c-2f8868a81b76.png)

AR Core ExtensionsにXR Originが入っているか確認してください。入っていれば次はARCore APIの取得に移ります。実行してもまだエラーが出る状態なので実行できないはずです。

# ARCore APIの取得方法
##### 必要なもの
クレジットカード、Googleアカウント
ARCore Geospatial APIを機能させるためのAPIをGCP (Google Cloud Platform) で 発行し取得します。登録にはクレジットカード情報が必要ですが無料の範囲でも使うことができます。
無料で使える範囲を超えるリクエスト数はそれに応じて料金が発生します。
※個人の責任でお願いします。

##### Google Cloudに登録
URLにアクセス
https://console.cloud.google.com/apis/library/arcore
![スクリーンショット 2024-11-08 141103.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3929428/e1c18d33-4e60-6218-4a42-651d65cf8edf.png)
`無料で利用開始`>`Googleアカウントにログイン`>`クレジットカード情報を入力`
確認コードをメールで受け取り入力します。Google CloudへようこそというメールがきたらGoogle Cloudを利用できます。
再度上記のURLへアクセスしARCore APIを有効にします。
プロジェクトは適宜作ってください。

##### ARCore APIの発行
![スクリーンショット 2024-11-08 143605.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3929428/1f8ec7be-8b57-b1ea-08a1-2a7a9f12b8be.png)
有効にしたら管理画面が表示されるので`APIとサービスの認証画面`という文字をクリックしてください。![スクリーンショット 2024-11-08 143616.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3929428/78dfede3-9995-96b9-2e27-37fc847b8e3d.png)
認証情報を作成をクリックしAPIキーを選択します。しばらく待つとAPIキーが作成できるのでAPIキーをコピーしてください。

:::note warn
APIキーの取り扱いには注意してください。
:::


# デモをビルド
![スクリーンショット 2024-11-08 144552.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3929428/65c73d60-519c-8605-c50f-e880c07d6f77.png)

##### 取得したAPIキーをUnityに渡す
`Edit`>`Project Settings`>`XR Plug-in Management`>`ARCore Extensions`から上記のような設定にしてください。※iOSでビルドしたい方はiOSサポートにチェックをいれて同様にAPIキーをいれてください。

APIキーを入れまだ`Project Settings`閉じずに
![スクリーンショット 2024-11-08 145308.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3929428/640052a7-04c6-9c6e-46dc-48d5a51b6356.png)
`Player`からビルドするプラットフォームの`Active Input Handling`を`Both`にします。
（AndroidだとBothにすることを求められる。なぜかはわからない。）
BothにしたらUnityが再起動するので待ちます。

![スクリーンショット 2024-11-08 155414.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3929428/8995bcc7-3507-7db5-73f1-a96ad71e8ba0.png)


**ビルドする前にGeospatial ModeをEnableにします。**

![スクリーンショット 2024-11-08 150054.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3929428/8200e96b-6bf6-1632-870d-df4d1f61ef25.png)
では実際にシーンを`Scene In Build`に追加しビルドしてみます。

# デモを実行
![Screenshot_20241108_150650_TEST.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3929428/83c05ed5-50b8-8508-7d65-9573c3de5fa3.jpeg)

いろいろな数値がでてきた。一番上から緯度と経度、高度もあります。Accuracyに関してはこの次で重要なものになります。


# 特定の位置にオブジェクトを表示させる
特定の位置にオブジェクトを表示するために、ARCore Geospatial APIを使う際の簡単な手順について説明します。

**1. Geospatial APIと屋外での使用について**
まず、ARCore Geospatial APIはGPSやVPS（Visual Positioning System）を利用して正確な位置情報を取得しますが、屋内ではうまく機能しません。そのため、基本的に屋外での使用をおすすめします。

**2. ローカライズのチェック**
実際にオブジェクトを表示する前に、位置情報がどれだけ正確かを確認する「ローカライズチェック」を行います。このチェックでは「Accuracy（精度）」の値を確認します。Accuracyの値が低いほど、位置情報が正確であることを意味します。

**3. スクリプトの概要**
スクリプトを作成し、Accuracyの値が一定のしきい値以下になるまで待機します。しきい値を下回った場合、位置情報の精度が十分に確保されたと判断します。

```c#
using Google.XR.ARCoreExtensions;
using System;
using UnityEngine;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;


public class LocalizeCheck : MonoBehaviour
{
    // しきい値を設定する（方位角の許容誤差）
    public double orientationYawAccuracyThreshold = 16;
    // しきい値を設定する（水平精度の許容誤差）
    public double horizontalAccuracyThreshold = 14;

    // ローカライズが完了したときに実行するアクション
    public Action OnFinishedLocalization { get; set; }
    // ローカライズが失われたときに実行するアクション
    public Action OnLostLocalization { get; set; }

    [SerializeField] private AREarthManager earthManager;

    private bool isLocalizing;

    private bool hasLostLocalization = false;

    void Update()
    {

        bool currentlyLocalizing = IsLocalizing();

        if (currentlyLocalizing && !hasLostLocalization)
        {
            hasLostLocalization = true;
            OnLostLocalization?.Invoke(); // イベントを呼び出す
        }

        else if (!currentlyLocalizing && hasLostLocalization)
        {
            hasLostLocalization = false; 
            OnFinishedLocalization?.Invoke(); // イベントを呼び出す
        }
    }

    // ローカライズ状態を判定するメソッド
    bool IsLocalizing()
    {
        // ARセッションと位置情報サービスが正常に動作しているか確認
        bool isSessionReady = ARSession.state == ARSessionState.SessionTracking &&
                              Input.location.status == LocationServiceStatus.Running;
        // 現在のARトラッキング状態を取得
        var earthTrackingState = earthManager.EarthTrackingState;

        // 地理的なポーズを取得（トラッキング中の場合のみ）
        var pose = earthTrackingState == TrackingState.Tracking ?
            earthManager.CameraGeospatialPose : new GeospatialPose();

        // ローカリゼーションの状態を判定し、しきい値を超えている場合はローカリゼーションが不安定と判断
        return !isSessionReady ||
               earthTrackingState != TrackingState.Tracking ||
               pose.OrientationYawAccuracy > orientationYawAccuracyThreshold ||
               pose.HorizontalAccuracy > horizontalAccuracyThreshold;
    }
}
```

![スクリーンショット 2024-11-08 162600.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3929428/2e941fdd-a9ce-d6a1-4dcf-b056e6df0c18.png)

```c#
using Google.XR.ARCoreExtensions;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARFoundation;

public class SampleSpawnObject : MonoBehaviour
{
    public double TestLatitude; //出現させたい緯度を入力
    public double TestLongitud; //出現させたい経度を入力
    private LocationInfo currentLocation;
    private LocationServiceStatus Status;
    [SerializeField] LocalizeCheck localizeCheck;
    public GameObject Prefab;//出現させたいプレファブ
    public ARAnchorManager anchorManager;
    public AREarthManager earthManager;
    GameObject instance;
    ARGeospatialAnchor anchor;

    void Start()
    {
        localizeCheck.OnFinishedLocalization += SpawnObject;
        localizeCheck.OnLostLocalization += DestroyObject;
    }

    private void SpawnObject()
    {
        var pose = earthManager.CameraGeospatialPose;
        double targetAltitude = pose.Altitude + 1.5;
        var anchor = anchorManager.AddAnchor(
            TestLatitude,
            TestLongitud,
            targetAltitude,
            Quaternion.identity
        );

        instance = Instantiate(Prefab, anchor.transform);
    }

    private void DestroyObject()
    {
        if (instance != null)
        {
            Destroy(instance);
            instance = null;
        }
        if (anchor != null)
        {
            Destroy(anchor);
            anchor = null;
        }
    }
}
```
![スクリーンショット 2024-11-08 165015.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3929428/675e4096-18d3-3325-5bf7-2b35c2dbb286.png)
オブジェクトを表示させたい場所の緯度と経度をGoogle Mapからとってきてインスペクター上で貼り付けます。先ほどと同様のシーンをBuild&Runで実行。

# 実行
![Screenshot_20241108_173458_TEST.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3929428/ff2257f7-9f48-93cd-1ed5-bca61a3ef724.jpeg)


# まとめ
VPSの中でGeospatial APIはかなり使いやすいと思います。スキャンしなくてもいいというのが最大の利点です。後編ではサーバーから飲食店の空席状況を取得して飲食店の位置に表示させたいと思います。
お読み頂き誠に有難うございました。

# 参考
https://tech.fusic.co.jp/posts/2024-03-01-unity-arcore-geospatial-api/