Unityにリアルワールドを簡単に導入する！

# はじめに
こんにちは．Life is Tech Advent Calendar 2023の24日目を担当しますえふじといいます！

今回はUnityで現実世界の3Dマップを簡単に導入する方法を紹介したいと思います．

# Cesium for Unityについて
まずはじめにCesiumとは何かというと，
「Cesium（セシウム）は、オープンソースとして提供される3D地理空間可視化プラットフォームです。」

ここにGoogleの地図データを読み込ませてあげればこのように地球の地図データの全てをUnityに持ってくることができるのです！
![スクリーンショット 2023-12-24 15.37.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2500127/f75fe26b-6ae9-bdda-7142-6110a2c77df6.png)
拡大してみると．．．
宮下パーク
![スクリーンショット 2023-12-24 15.40.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2500127/deec3150-ea16-cbda-4f16-e2b035d21eb7.png)
結構綺麗

# 導入方法
### GoogleのMap tiles APIを取得
https://developers.google.com/maps/documentation/embed/get-api-key?hl=ja
ここのURLから認証情報ページに飛ぶ
![スクリーンショット 2023-12-24 15.45.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2500127/4625510b-bdb5-75c3-fc2f-01b9ba158acc.png)
プロジェクトを作成すると，APIKeyが表示されるのでどこかにメモっておく．（1度しか表示されないよ！）
上部の検索欄から`Map tiles API`と検索し，Map tiles APIを有効にしておく
![スクリーンショット 2023-12-24 15.47.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2500127/2fcdedb9-766c-e08d-1a36-61f7be2df346.png)

:::note warn
注意
Google API keyの取得にはクレカ情報が必要になります．デバッグ程度であれば十分無料枠の中で使用できると思いますが，適切に上限等の設定を行なってください
:::

### UnityにCesiumを導入
ここからはUnityにCesiumを導入する作業です．ちなみにバージョンが古すぎるとできないです！私の場合は2022.3.0を使用しています．

まずはProjectを3D(URP)or3D(HDRP)で作成します．
Edit > Project Setting > Package ManagerのScoped RegistriesにCesiumのパッケージを設定します．
![スクリーンショット 2023-12-24 15.55.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2500127/a37cd462-28c5-9f14-f896-2fd2cafdd6a9.png)

```
名称：Cesium
URL: https://unity.pkg.cesium.com
スコープ: com.cesium.unity
```

その後Package ManagerからCesiumをインストールするとメニューバーにCesiumが表示されると思うので，Cesiumを選択してCesiumウィンドウを開いてください！
![スクリーンショット 2023-12-24 16.01.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2500127/e551a704-435c-3d9a-e4f4-faff384bb9f1.png)

Blank 3D Tiles Tilesetのプラスアイコンをクリックすると，ヒエラルキーにCesium3DTilesetというオブジェクトが生成されるのでInspectorから Tileset SorceをFrom Url に設定する．
URLは`https://tile.googleapis.com/v1/3dtiles/root.json?key=<自分のAPIKey>`を入れておく

するとSceneウィンドウに地球が表示されるはず！されなかったら再起動
これだけの設定で現実世界のマップを導入することができました！

CesiumGeoreferenceの子オブジェクトであるDynamicCameraのCesiumGlobeAnchorをいじると色々できます．

![スクリーンショット 2023-12-24 16.08.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2500127/69ccd8c2-df06-8243-c579-ab2ee72605bc.png)

例えば，Input fieldに入力した場所の緯度経度を取得してその場所に移動するサンプルコードは以下のようになります．

```C#:LocationFetcher
using UnityEngine;
using UnityEngine.Networking;
using System.Threading.Tasks;
using Cysharp.Threading.Tasks;
using TMPro;
using UnityEngine.UI;
using CesiumForUnity;

public class LocationFetcher : MonoBehaviour
{
    [System.Serializable]
    public class Location
    {
        public float lat;
        public float lng;
    }

    [System.Serializable]
    public class Geometry
    {
        public Location location;
    }

    [System.Serializable]
    public class Result
    {
        public Geometry geometry;
    }

    [System.Serializable]
    public class Root
    {
        public Result[] results;
    }

    [SerializeField]
    TMP_InputField inputField;
    [SerializeField]
    Button fetchButton;

    private CesiumGlobeAnchor globeAnchor;
    [SerializeField]
    TMP_InputField inputText;
    TextMeshProUGUI placeholderText;

    private async void Start()
    {
        inputText = GameObject.Find("PromptField").GetComponent<TMP_InputField>();
        placeholderText = inputText.placeholder as TextMeshProUGUI;
        fetchButton.onClick.AddListener(async () => await GetLocation(inputField.text));
    }

    public async Task GetLocation(string address)
    {
        string apiKey = "自分のAPIKey";
        string url = $"https://maps.googleapis.com/maps/api/geocode/json?address={address}&key={apiKey}";

        using (UnityWebRequest request = UnityWebRequest.Get(url))
        {
            await request.SendWebRequest();

            if (request.result == UnityWebRequest.Result.Success)
            {
                string json = request.downloadHandler.text;
                Root root = JsonUtility.FromJson<Root>(json);

                if (root.results.Length > 0)
                {
                    float latitude = root.results[0].geometry.location.lat;
                    float longitude = root.results[0].geometry.location.lng;

                    Debug.Log("Latitude: " + latitude);
                    Debug.Log("Longitude: " + longitude);
                    GameObject dynamicCamera = GameObject.Find("DynamicCamera");
                    globeAnchor = dynamicCamera.GetComponent<CesiumGlobeAnchor>();
                    globeAnchor.SetPositionLongitudeLatitudeHeight(longitude, latitude, 200);
                }
                else
                {
                    Debug.LogError("No results found");
                }
            }
            else
            {
                Debug.LogError("Request failed: " + request.error);
            }
        }

        inputText.text = "";
    }
}
```
:::note warn
注意
このサンプルコードではコード内にAPIKeyをベタ書きしているのでpublic repoなど他人の目にふれるところには公開しないようにしてください
:::
# おわり
アイデア次第で面白いゲームが色々作れそうな気がします．皆さんもぜひ試してください！
# 参考
https://lilea.net/lab/how-to-setup-photorealistic-3d-tiles-for-cesium-for-unity/

https://qiita.com/tatsuya1970/items/828dff5ad116b210c74d