Godot4でBlenderファイルからMeshLibraryを作成して、GridMapを使ってみた

　Godot4.1.1でBlenderファイルからMeshLibraryを作成して、GridMapを使う方法を調査しました。

# GridMapについて
　GridMapを使用すると、下図のようにパレットにあるタイルを選択して、エディターウインドウに左クリックでタイルを配置、右クリックでタイルを削除と簡単かつ効率的に配置することができます。高さ方向はフロアで指定可能です。
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/96ca8aba-7630-902d-e849-b24d4e4c596f.png)

　パレットにあるタイルは、Blenderファイル（やglTF2.0ファイルなど）をGodotでMeshLibraryに変換して使用します。

# Blenderでタイルにする３Dオブジェクトを作成する
　本稿ではblenderのファイル(.blendファイル）を直接Godotにインポートします。3.0以上のBlenderが対応していますが、glTF exporterのバグ対応済みの3.5以上を推奨しています。今回はBlender3.6.5を使用しました。

https://docs.godotengine.org/en/4.1/tutorials/assets_pipeline/importing_scenes.html#importing-blend-files-directly-within-godot

　Blenderで壁、草原、水面の３つの３Dオブジェクトを用意し、アウトライナー上で、それぞれWall、Grass、Waterという名前にしました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/6809d491-cac2-b57b-5185-c5ce42c54a7a.png)

:::note
アウトライナー内のオブジェクト1個が、パレット内の1個のタイルに変換されます。もし一つのタイルを複数のオブジェクトで構成して作成した場合はGodotにインポートする前にひとつのオブジェクトに統合する必要があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/8947af2a-2b9f-3943-c16f-d95cf3df54db.png)
　　　　　　↓
![2023-10-28 (20).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/631a5d06-766b-9562-d63f-0155d656235e.png)

:::

# GodotがBlenderファイルを読み込めるようにする
　GodotはBlenderのglTF exporter機能を利用して、blendファイルを読み込むため、Blenderの実行ファイル「blender.exe」が置いてあるパスを設定する必要があります。
　Godotのエディターメニューの「エディター設定...」を実行します。
　ファイルシステムのインポートを選択し、「Blender 3 Path」に「blender.exe」が置いてあるパスを設定します。
![2023-10-28 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/c86e631e-768d-ab79-aef3-5b4b9494b72e.png)

# Godotプロジェクトを作成し、blenderファイルを配置する
　Godotで新規プロジェクトを作成し、assetsフォルダを作成しました。
　先ほどBlenderで作成した３Dオブジェクトは「SampleParts.blend」というファイル名で、assetsフォルダに保存しました。
　Godotをアクティブにするとblendファイルが自動的にインポートされます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/321af7e0-18ec-2598-fbe8-8227bba48a72.png)

# blendファイルから「新しい継承シーン」を作成する
　インポートしたSampleParts.blendを右クリックして、「新しい継承シーン」を実行すると、ルートノードがNode3Dで、子ノードが３つのMeshInstance3Dの新規シーンが作成されます。
　「sample_parts.tscn」という名前で保存します。
![2023-10-28 (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/1ea7d9eb-7f09-a44f-1394-e84de5a2edf8.png)
　シーンの３つのMeshInstance3Dノードの名称は、Blenderのアウトライナーのオブジェクト名と同じです。
![2023-10-28 (4).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/095df084-e568-642b-2739-efcc27e2bf0c.png)

# ３DオブジェクトにCollision3Dを設定する
　３DオブジェクトをStaticBody3DにしてCollisionを設定します。今回はシンプルな形なのでCollisionをBoxShape3Dなどで設定可能ですが、メッシュ形状から自動で生成します。
　シーン内でMeshInstance3D（Wallなど）を選択すると、「〼メッシュ」メニューが表示されるので、クリックして、「三角形メッシュコリジョンの兄弟を作成」を実行します。
　![2023-10-28 (6).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/0a0402cb-f723-3c07-8228-cd10d97cf20a.png)
　実行すると、選択したメッシュの形でCollisionShape3Dノードが生成されます。
![2023-10-28 (8).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/0c02752e-80b9-2a0e-3ee6-20af0aa44581.png)
MeshInstance3Dノードの子ノードにStaticBody3Dを追加して、孫の位置にCollisionShape3Dを移動します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/73ae62e0-8250-c375-78c2-14a8582cf2f2.png)
他のMeshInstance3D(GrassとWater）に対しても同様の操作を実行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/7bf474e3-0472-fcfc-5947-79351c43a780.png)

:::note
〼メッシュメニューで、４種類のコリジョンシェイプ作成方法を選択することができます。
**1. 三角形メッシュコリジョンの兄弟を作成**
　ポリゴンベースのコリジョンシェイプを作成します。
　衝突検出が最も正確な（ただし最も遅い）オプションです。
**2. 単一の凸型コリジョンの兄弟を作成**
　単一の凸型コリジョンシェイプを作成します。
　衝突検出が最速（ただし精度が最も低い）のオプションです。
**3. 簡略化された凸型コリジョンの兄弟を作成**
　簡略化された凸型コリジョンシェイプを作成します。
　単一の凸型コリジョンシェイプと似ていますが、精度を犠牲にして単純なジオメトリとなることがあります。
**4. 複数の凸型コリジョンの兄弟を作成**
　ポリゴンベースのコリジョンシェイプを作成します。
　2の単一の凸型コリジョンシェイプと1のポリゴンベースのコリジョンの中間的なパフォーマンスです。
:::

# シーンからMeshLibraryをエクスポートする
　blendファイルから作成した、sample_parts.tscnを選択した状態で、「シーン」メニューの「エクスポート...」から「メッシュライブラリ...」を実行します。

 ![2023-10-28 (14).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/ec0408db-40ed-df5e-2859-e0e944c59637.png)
 エクスポートするMeshLibraryのファイル名を「sample_parts.tres」のように設定して、保存します。
![2023-10-28 (15).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/d97e9f15-3bcf-2b39-f521-2435a8eb2ea4.png)

「sample_parts.tres」をダブルクリックして中身を見てます。
インスペクターのMeshLibraryのItemをひらくと、０，１，２と３つのItemが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/854bddd5-1b17-dcdf-93d2-549ef0f5de68.png)

:::note
　何度もエクスポートしているとItemがうまくエクスポートされないことがありました。その場合、「sample_parts.tres」ファイルを削除してからエクスポートするとうまく出力されることがあります。
:::

「０」を開くとID#0に登録されたメッシュの情報を表示します。赤枠で囲われたところを見ると、IDが０，名前が「Grass」、Meshが緑の正方形であることが確認できます。
![2023-10-28 (16).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/db0feee6-8520-196a-4529-0a7f3987bb11.png)

:::note
 MeshLibraryでは「Item」という名前ですが、GridMapでは「Tile」や「Gridmap Item」という表現が使われます。同じものを指していると思います。
:::

# GridMapを作成する
　「シーン」メニューの「新規シーン」を実行して、３Dボタンを押下します。ルートノード名を「SampleGridMap」に変更して保存します（sample_grid_map.tscnというファイル名で保存）。
　SampleGridMapの子ノードに「GridMap」を追加します。GridMapを選択した状態で、インスペクターのGridMapの「MeshLidrary」に先ほどエクスポートした「sample_parts.tres」をドラッグして登録します。

![2023-10-28 (18).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/626e2c1f-20d3-86d5-47df-fb52a3846a16.png)

成功するとパレット内に、MeshLibraryに登録した３つのタイルが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/09423943-d2ba-bbde-885f-98ec07abb425.png)

# GridMapのプロパティを設定する
　GridMapを選択した状態でインスペクターを表示し、GridMapのプロパティを設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/ab1b7685-81d1-3d9e-640a-67376226e4dd.png)

| プロパティ名 | 内容 |
|:-|:-|
| Size  | セル（グリッド）のサイズを設定します。今回は３DオブジェクトはBlenderの平面でX=Y=2mで作成したので、セルのサイズは３Dオブジェクトのサイズの合わせて、Godotの平面でX=Z=2mに設定しました。高さは1m単位で設定したいので、Y=1mにします。  |
| Center X/Y/Z | オンにするとタイルの中心がセルの中心になるように配置されるので、X,Zはオンにします。Yはオフにすると、タイルの底面がY=0のところに配置され期待通りになりました（あまり理解できていないです）|
| Scale  | タイルを一律拡大・縮小できますが、今回はタイルにSizeを合わせたので、Scaleは１です。  |

# GridMapにタイルを配置する
　タイルを配置するのはとても直感的です。パレットで配置したいタイルをクリックして選択し、エディターウインドウで左クリックで配置します。右クリックで削除します。別のタイルを選択して配置した場合、上書きになります。
![2023-10-28 (19).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/98cfcd81-8e14-80e8-a665-3f4833268e8f.png)


　![2023-10-28 (19).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/6baf9428-526a-5990-b277-e3f5f303ac57.png)

# 衝突判定の確認
　タイルにはStaticBody3DとCollisionShape3Dを設定したので大丈夫だとは思いますが、一応確認します。
　RigidBody3Dのボールを追加して、Wallの角に落ちるような位置に配置しました。DirectionalLight3DとCamera3Dを配置して実行すると、Wallの角にぶつかって、緑と青の平面上をボールが転がるので、衝突判定を確認できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/51b1caf7-19fc-338c-b766-63ac3c90fa4d.png)


# 床面（フロア）を使う
  タイルを置くときの高さ（Ｙ軸）は「床面」で指定します。Y軸の高さはインスペクターのGridMap/Cell/Size/Yの設定値 × 床面（フロア）になります。
　左から床面を０，１，２，３にしてタイルを設定しました。
　GridMapのインスペクターのCell Size Yは１ｍにしているため、１ｍ単位で高さが変化します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/b51c9d91-a325-1f8f-2762-797cdac45e77.png)

# github

https://github.com/footinglow/Godot4/tree/main/03_study/SampleMeshLibAndGridMap

以上です。