[PLATEAU] CityGMLで簡易的なナビゲーションシステムを作った！

# はじめに
どうも、べちおです。

山奥の大学で学生エンジニアをしています。
よろしくお願いいたします。

# 今回の内容


先日、[**PLATEAU Hack Challenge 2022 in ヒーローズ・リーグ**](https://asciistartup.connpass.com/event/246677/)に「**下山者**」というチーム名で、同じ研究室のメンバーと、当日知りあった方一人の3人で参加をしました。
その際のプロダクトとして、国土交通省が提供している[**PLATEAU**](https://www.mlit.go.jp/plateau/)の3D都市モデルと **Unity**を用いた[「避難経路シミュレータ(水害)」](https://protopedia.net/prototype/3207)を作成しました。

避難経路を作成するのに、Unityの**NavMesh**という機能を使用したのですが、PLATEAUモデルならでは(?)の問題にぶちあたり、**二日間**しかない作業時間の大部分を消費してしまいました。(Unityほとんど触ったことなかったからっていう言い訳をさせてほしい)

今回は簡易的なナビゲーションシステムを作りながら、実際に起きた問題と解決方法を共有したいと思います！

※この記事に載っている画像は2022/08/27時点のものになります。


## 記事の対象者
 - PLATEAUの3D都市モデルに興味がある方
 - Unityを触ったことがある方


## この記事でできること
 - CityGMLの変換
 - 3D都市モデルを用いたナビゲーションシステム(簡易)の作成

完成予想図
![スクリーンショット 2022-08-30 22.53.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2544613/2802b1a7-279e-9dd5-da5c-94ee5a88eb4b.png)



## 動作環境
 - Macbook (M1 Pro)
   - macOS  Monterey 12.5 
   - メモリ:16GB
 - Unity
   - version 2019.4.16f1 Personal
   


# いざ実装！
## 3D都市モデルを用意する
### PLATEAU公式ページからG空間情報センターに辿り着こう！
初めに、3Dモデルを用意するために**G空間情報センター**のサイトに行く必要があります。
ここは王道に沿ってPLATEAUの公式ページから行くことにします。

 + まず[PLATEAU公式ページ](https://www.mlit.go.jp/plateau/)に行きます。

https://www.mlit.go.jp/plateau/

 + 上のメニューバーから[「Open Data」](https://www.mlit.go.jp/plateau/opendata/)を選択します。
 + 画面下に「PLATEAUオープンデータポータルサイト（G空間情報センター）」のリンクがあるので、そこから[G空間情報センター](https://www.geospatial.jp/ckan/dataset/plateau)のサイトへ行きます。

https://www.geospatial.jp/ckan/dataset/plateau

そこには3D都市データが提供されているエリアが載っているので、ほしい3D都市データのリンクをクリックしてください。

また、以下の記事にもダウンロード方法や、ファイルの中身、モデルの使用について簡単にまとめたものがありますので、よかったらご覧ください。

https://qiita.com/betio/items/9b54251cb246c696d7bd



### 3D都市モデルの仕様について
ここではPLATEAUの3D都市モデルの仕様について簡単にご説明します。

CityGMLは「**LOD**（Level of Detail）」と呼ばれる概念を持っており、LOD1、LOD2のように付与されている情報に違いがあります。

具体的には、以下のような違いがあります。
 - LOD1 - 箱モデル
 - LOD2 - LOD1 + 屋根形状
 - LOD3 - LOD2 + 窓やドア
 - LOD4 - LOD3 + 室内

CityGMLの詳細については[コチラ](https://www.mlit.go.jp/plateau/learning/)が参考になると思います。

https://www.mlit.go.jp/plateau/learning/

今回は**LOD2**のモデルを使用していきます。



## CityGML形式をFBX or OBJ形式に変換

CityGML形式は通常のCGソフトでは読み込むことができません。(UnityとかBlenderとか・・・)
そのため、一度形式を読み込める形で変換してあげる必要があります。

詳しい変換の方法はPLATEAU公式の[**Libraries**](https://www.mlit.go.jp/plateau/libraries/)の**3D都市モデルのデータ変換マニュアル**に記載されていますので、一読することをお勧めします。

また以下の記事にCityGML形式を別の形式に変換する方法を記載しましたので、そちらをお読みください。

https://qiita.com/betio/items/681f4bc6a507029eca13





## Unityに読み込み
今回はUnityへの読み込みに**FBX形式**のモデルを使用します。
また今回は**NavMesh**というUnityのデフォルトのナビゲーションシステムを使用するので、以下のモデルをヒエラルキーに追加してください。

 - 床のモデル(3D object → Planeなど)
 - ナビゲーションのゴール地点
 - ナビゲートされるエージェント(3D都市モデルの間を進んでいくモデル、3D object → Capsuleなど)

3D都市モデルの方(FBX)は一度Assetsフォルダに置いてからヒエラルキーに追加してください。

3D都市モデルのスケールは「1」としています。そのため3D都市モデルの間を進んでいくオブジェクトのスケールは「0.1」としてます。

一通り追加するとこのような画面になると思います。(ヒエラルキーで非表示にしているものがいくつかありますがそれは無視してください)

![スクリーンショット 2022-09-02 16.03.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2544613/d34c39de-904c-063b-34a1-cf325086378e.png)




## NavMeshで実際にエージェントを動かしてみる
NavMeshの基本的な使い方は、こちらの記事がとても参考になりました。
また、以下の記事に沿って作業を進めていただければ、動作しますので、ここでの詳しい説明は省かせていただきます。

https://www.midnightunity.net/unity-navmesh/


さて、実行してみましょう！


## 問題発生！

さぁここでいざ実行してみると、
 - 想定していなかった挙動をする
 - 3D都市モデルを飛び越えて目的の場所まで行ってしまう
 - 全く動かない

といったことが起きるかもしれません。

正しい挙動をされた方はおめでとうございます。以下の「目次：ついでに最短経路をlineで表示してみる」に移っていただいて問題ありません。

しかし、以上のような問題に直面した方は「目次：解決方法」をご覧ください。

ちなみに、正しい挙動にするためには以下の画像のように、3D都市モデルの間を進んでいくオブジェクトが通れる箇所は青く塗りつぶされるはずです。

(Bakeをした直後)
![スクリーンショット 2022-09-02 16.20.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2544613/d0206dea-2cc2-c151-50ce-4380df1c4da3.png)



## 解決方法

解決するために参考にした資料を紹介します。

https://docs.unity3d.com/ja/2019.1/Manual/nav-AdvancedSettings.html

![スクリーンショット 2022-09-02 16.39.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2544613/ccddd210-4ea8-4f83-8173-31a64ac69313.png)

都市モデルの、

**Agent Radius**

こちらの値を調整することで問題を解決することができました。

起きていた問題を具体的に解説すると、

「3D都市モデルの建物一つ一つに適用されていたAgent Radiusが干渉しあっていたために、3D都市モデルの間を進んでいくモデルの通れる道がなかった(道がAgent Radiusで埋まっていた)」

ということでした。そのため、「Agent Radius」の値をデフォルトの値から小さくしてあげることで、Bakeした際に青く塗り潰される箇所が多くなったと思います。

そのほかのパラメータは参考にした資料に詳しく載っていますのでぜひ参考にしてみてください。



## ついでに最短経路をlineで表示してみる


ここではNavMeshによるエージェントの行動の最短距離を表示したいと思います。

まずヒエラルキーウィンドウに、「Line」を追加してください。(ヒエラルキーウィンドウを右クリック　→ Effects/Line)
サンプルとして以下の画像を参考に「Line」の設定を行なってみてください。

![スクリーンショット 2022-09-29 14.06.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2544613/4597bfa9-f224-aa27-04cd-d860c288c7fd.png)


以下のコードを参考に、経路を描画するためのファイルを作成してください。
NavMeshPathTest.cs
```
using UnityEngine;
using UnityEngine.AI;


public class NavMeshPathTest : MonoBehaviour
{

    [SerializeField]
    Transform target;     // 目標地点
    [SerializeField]
    NavMeshAgent agent;
    [SerializeField]
    LineRenderer line;

    NavMeshPath path;

    void Start()
    {
        // NavMeshAgent に目的地を設定する
        agent.SetDestination(target.position);

        // 経路取得用のインスタンス作成
        path = new NavMeshPath();
        // 明示的な経路計算実行
        agent.CalculatePath(target.position, path);

        // LineRendererで経路描画！
        line.SetVertexCount(path.corners.Length);
        line.SetPositions(path.corners);
    }

    void Update()
    {
    }
}
```
このファイルは、3D都市モデルの間を進んでいくオブジェクト(ここでは3D object → Capsule)にアタッチしてください。また、アタッチ後には、「Target, Agent, Line」に該当するオブジェクトを Hierarchy からドラッグ＆ドロップしてください。
その後このオブジェクトは以下のような構成になっていると思います。

![スクリーンショット 2022-09-29 14.08.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2544613/7a53da9e-4444-0505-b6a1-9fdb1fca3eee.png)

これで実行ボタンを押していただくと、冒頭でご紹介したような経路が表示されたかと思います。



# おわりに
ここまでお読みいただきありがとうございます。


私自身初めてのハッカソンということもあり、限られた時間の中での効率的な立ち回りができませんでしたが、無事走り切れてよかったです！
また、同じチームになってくれた二人にはとても感謝しています。


この記事が新たなプロダクトのお役に立てれば幸いです。