GodotのArrayMeshで三角形を作ってMaterialと衝突領域を設定する

# はじめに
　GODOT4.0でGDScriptから自由な形の三角形を作りたくなったのでArrayMeshについて調査しました。
　Materialと衝突領域も設定したいと思います。

# やりたいこと
　・GDScriptで3D空間の3点を指定して三角形を作る。
　・Materialを設定する
　・衝突領域を設定する

# GITHUB
　本記事を最後まで実行したプロジェクトです。

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

# 事前準備：クリックしたところの3次元座標をprintする
　ArrayMeshを確認する事前準備として、地面を作成して。地面にタッチ（クリック）したところの３Ｄ空間座標をprintするまで実装します。

## 新規シーンを作成して必要なノードを追加
　Godotで新規プロジェクトを作成して、下記のようにノードを構成します。
![スクリーンショット (600).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/353b9581-a1d9-19c5-b742-eccd6cc949eb.png)
World : Node3D
　┝━Camera3D
　┝━DirectionalLight3D
　┝━StaticBody3D　→地面にする
　┃　　┝━MeshInstance3D　
　┃　　┗━CollisionShape3D
　┝━ArrayMeshTriangle  : MeshInstance3D　→三角形の描画用
　┗━ArrayMeshLineStrip : MeshInstance3D　→クリックした点を結ぶ線の描画用

* **Camera3D**
地面を見下ろすように設定します。
 Node3D/Transform/Position : (x,y,z)=(0m, 4m, 7m)
 Node3D/Transform/Rotation : (x,y,z)=(-45°, 0°, 0°)
* **DirectionalLight3D**
左斜め前方に影が伸びるように設定します。
 Light3D/Light/Shadow/Enable     : オン
 Node3D/Transform/Position : (x,y,z)=(-10m, 0m, 0m)　※DirectionalLight3Dのpositionはどこでもよい
 Node3D/Transform/Rotation : (x,y,z)=(-10°, 45°, 0°)
* **StaticBody3D**
XZ平面に10m×10mの地面を作成。y=0が地面になるようにします。
Node3D/Transform/Position : (x,y,z)=(0m, -0.5m, 0m)
* **StaticBody3D/MeshInstance3D**
MeshInstance3D/Mesh       : 新規BoxMesh
MeshInstance3D/Mesh/Size  : (x,y,z)=(10m, 1m, 10m)
* **StaticBody3D/CollisionShape3D**
CollisionShape3D/Shape       : 新規BoxShape3D
CollisionShape3D/Shape/Size  : (x,y,z)=(10m, 1m, 10m)

![スクリーンショット (603).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/77e5b709-eec4-7ffe-475a-93faec5ccc4d.png)

## 「マウスでタッチ操作をエミュレート」を有効する
　プロジェクトメニューのプロジェクト設定の「入力デバイス/マウスでタッチ操作をエミュレート」をオンにします。
![スクリーンショット (604).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/7892bef3-ce68-245d-df5e-e5c2f9d8c93a.png)

## タッチしたところの３D座標を表示するスクリプトを実装
　ルートノードのWorldを右クリックして、「スクリプトにアタッチ」して下記のようにスクリプトを修正します。
　仕組みなど知りたい場合はこの記事を参考にしてください。

https://qiita.com/FootInGlow/items/564dfe87dc7fc5135c67

```GDScript
extends Node3D

@export var m_ray_length_m = 1000		# [m]十分な長さにする
	
var m_touch_pos = Vector2.ZERO
var m_f_is_screen_touch = false
var m_f_pre_is_screen_touch = false

func _input(event):
	if ( event is InputEventScreenDrag ) or ( event is InputEventScreenTouch ):
		m_touch_pos = event.position
		# スマホタッチ中フラグ
		m_f_is_screen_touch = event.is_pressed() or ( event is InputEventScreenDrag )
		
func _physics_process(delta):
	# 前周期はタッチしていなくて、今周期でタッチした場合を検出したい
	if m_f_is_screen_touch and !m_f_pre_is_screen_touch:
		var camera = get_viewport().get_camera_3d()

		# カメラを利用して3D空間のカメラ位置とタッチしたピクセルに対応する方向の1000m先の位置を計算する
		var from3d = camera.project_ray_origin(m_touch_pos)
		var to3d = from3d + camera.project_ray_normal(m_touch_pos) * m_ray_length_m

		# 3D ray physics queryの作成
		var query = PhysicsRayQueryParameters3D.create(from3d, to3d)
		query.collide_with_areas = true		# Area3Dを検知できるようにする
		
		# Godotの3Dの物理とコリジョンを保存しているspaceという情報を使用してオブジェクト検出
		var space_state = get_world_3d().direct_space_state
		var result = space_state.intersect_ray(query)
		if result:
			print("result.position:", result.position)

	# 今週機のタッチ/タッチしていない状態を保存する
	m_f_pre_is_screen_touch = m_f_is_screen_touch	
```

　実行します。
　地面をタッチ（クリック）すると、タッチ（クリック）した点の3D空間座標を出力します。
　地面表面がy=0になるようにStaticBody3Dを配置したので、y=0で、xとz座標に値が設定されています。
![スクリーンショット (606).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/57c50d22-79da-4704-7f3c-448541622981.png)
# ArrayMeshでクリックした点を線でつなぐ
　三角形を描画する前に、クリックしたところを見える化します。
　せっかくなのでArrayMeshのLINESTRIPを使用して描画します。

　Vector3Dの配列をArrayMeshインスタンスにセットして、シーンの内のArrayMeshLineStrip(MeshInstance3D)に設定します。

　スクリプト全体を示し、そのあとに修正点について説明します。

```GDScript
extends Node3D

@export var m_ray_length_m = 1000		# [m]十分な長さにする
	
var m_touch_pos = Vector2.ZERO
var m_f_is_screen_touch = false
var m_f_pre_is_screen_touch = false

func _input(event):
	if ( event is InputEventScreenDrag ) or ( event is InputEventScreenTouch ):
		m_touch_pos = event.position
		# スマホタッチ中フラグ
		m_f_is_screen_touch = event.is_pressed() or ( event is InputEventScreenDrag )
		
func _physics_process(delta):
	# 前周期はタッチしていなくて、今周期でタッチした場合を検出したい
	if m_f_is_screen_touch and !m_f_pre_is_screen_touch:
		var camera = get_viewport().get_camera_3d()

		# カメラを利用して3D空間のカメラ位置とタッチしたピクセルに対応する方向の1000m先の位置を計算する
		var from3d = camera.project_ray_origin(m_touch_pos)
		var to3d = from3d + camera.project_ray_normal(m_touch_pos) * m_ray_length_m

		# 3D ray physics queryの作成
		var query = PhysicsRayQueryParameters3D.create(from3d, to3d)
		query.collide_with_areas = true		# Area3Dを検知できるようにする
		
		# Godotの3Dの物理とコリジョンを保存しているspaceという情報を使用してオブジェクト検出
		var space_state = get_world_3d().direct_space_state
		var result = space_state.intersect_ray(query)
		if result:
			print("result.position:", result.position)
			add_vertex_position(result.position)

	# 今週機のタッチ/タッチしていない状態を保存する
	m_f_pre_is_screen_touch = m_f_is_screen_touch	

@onready var m_vertex_list = Array()
func add_vertex_position(v3_pos):
	# Line Strip
	m_vertex_list.append(v3_pos)
	create_linestrip_array_mesh(m_vertex_list)

func create_linestrip_array_mesh(vertex_list):
	var vertices = PackedVector3Array()
	
	for v3 in vertex_list:
		vertices.push_back(v3)

	var arrays = Array()
	arrays.resize(ArrayMesh.ARRAY_MAX)
	arrays[ArrayMesh.ARRAY_VERTEX] = vertices
	var tmpMesh = ArrayMesh.new()
	tmpMesh.add_surface_from_arrays(Mesh.PRIMITIVE_LINE_STRIP, arrays)
	$ArrayMeshLineStrip.mesh = tmpMesh
```

* **_physics_processの修正**
 タッチした点の3D空間座標のprintの次の行に「add_vertex_position(result.position)」を追加しました。

* **@onready var m_vertex_list = Array()**
　タッチした点の3D空間座標をすべて記憶するための配列を用意します。
　@onreadyアノテーションを使用しているので、本シーンがアクティブになったタイミングで設定されます。

* **add_vertex_positionメソッド追加**
 タッチした点の3D空間座標(Vector3)をm_vertex_list配列に追加して、create_linestrip_array_meshメソッドを呼び出します。
```GDScript
func add_vertex_position(v3_pos):
	# Line Strip
	m_vertex_list.append(v3_pos)
	create_linestrip_array_mesh(m_vertex_list)
```

* **func create_linestrip_array_mesh(vertex_list):**
　ArrayMeshに渡す3D空間の点を頂点(Vertex)と呼びます。OpenGLと同じです。
　頂点配列は PackedVector3Arrayを使用します。
　「Packed」とついているのでGPUに転送するための条件を保証したメモリ領域を保証するのだと思います（先頭アドレスが～byteの倍数、連続した領域、隙間がないなど）。
　3D空間座標のVector3配列をPackedVector3Array()にコピーします。
```GDScript
	var vertices = PackedVector3Array()
	
	for v3 in vertex_list:
		vertices.push_back(v3)
```
　ArrayMesh.ARRAY_MAX(13）の配列を用意して、ArrayMesh.ARRAY_VERTEX番目(0番目）に頂点配列を設定します。
　１～１２には必要に応じて法線情報（後述）、テクスチャ情報など設定しますが今は設定しません。
```GDScript
	var arrays = Array()
	arrays.resize(ArrayMesh.ARRAY_MAX)
	arrays[ArrayMesh.ARRAY_VERTEX] = vertices
```
　ArrayMeshインスタンスを生成して、add_surface_from_arraysメソッドで頂点配列を元にMeshを生成します。
```GDScript
	var tmpMesh = ArrayMesh.new()
	tmpMesh.add_surface_from_arrays(Mesh.PRIMITIVE_LINE_STRIP, arrays)
```
　生成したMeshをシーンのMeshInstance3Dに設定します。
```GDScript
	$ArrayMeshLineStrip.mesh = tmpMesh
```

　実行します。
　クリックするごとに線が描画されます。
![スクリーンショット (607).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/a357e6ea-038e-5c50-937e-3e7009bd5613.png)

# 3点クリックしたところで三角形を描画する
　3点クリックするたびにArrayMeshで三角形を描画します。
　スクリプト全体を示し、そのあとに修正点について説明します。

```GDScript
extends Node3D

@export var m_ray_length_m = 1000		# [m]十分な長さにする
	
var m_touch_pos = Vector2.ZERO
var m_f_is_screen_touch = false
var m_f_pre_is_screen_touch = false

func _input(event):
	if ( event is InputEventScreenDrag ) or ( event is InputEventScreenTouch ):
		m_touch_pos = event.position
		# スマホタッチ中フラグ
		m_f_is_screen_touch = event.is_pressed() or ( event is InputEventScreenDrag )
		
func _physics_process(delta):
	# 前周期はタッチしていなくて、今周期でタッチした場合を検出したい
	if m_f_is_screen_touch and !m_f_pre_is_screen_touch:
		var camera = get_viewport().get_camera_3d()

		# カメラを利用して3D空間のカメラ位置とタッチしたピクセルに対応する方向の1000m先の位置を計算する
		var from3d = camera.project_ray_origin(m_touch_pos)
		var to3d = from3d + camera.project_ray_normal(m_touch_pos) * m_ray_length_m

		# 3D ray physics queryの作成
		var query = PhysicsRayQueryParameters3D.create(from3d, to3d)
		query.collide_with_areas = true		# Area3Dを検知できるようにする
		
		# Godotの3Dの物理とコリジョンを保存しているspaceという情報を使用してオブジェクト検出
		var space_state = get_world_3d().direct_space_state
		var result = space_state.intersect_ray(query)
		if result:
			print("result.position:", result.position)
			add_vertex_position(result.position)

	# 今週機のタッチ/タッチしていない状態を保存する
	m_f_pre_is_screen_touch = m_f_is_screen_touch	

@onready var m_vertex_list = Array()
@onready var m_triangle_vertex_list = Array()

func add_vertex_position(v3_pos):
	# Line Strip
	m_vertex_list.append(v3_pos)
	create_linestrip_array_mesh(m_vertex_list)

	# Triangle
	m_triangle_vertex_list.append(v3_pos)
	if m_triangle_vertex_list.size() >= 3:
		create_triangle_array_mesh(m_triangle_vertex_list)
		m_triangle_vertex_list.clear()

func create_linestrip_array_mesh(vertex_list):
	var vertices = PackedVector3Array()
	
	for v3 in vertex_list:
		vertices.push_back(v3)

	var arrays = Array()
	arrays.resize(ArrayMesh.ARRAY_MAX)
	arrays[ArrayMesh.ARRAY_VERTEX] = vertices
	var tmpMesh = ArrayMesh.new()
	tmpMesh.add_surface_from_arrays(Mesh.PRIMITIVE_LINE_STRIP, arrays)
	$ArrayMeshLineStrip.mesh = tmpMesh

func create_triangle_array_mesh(vertex_list):
	var vertices = PackedVector3Array()
		
	for v3 in vertex_list:
		vertices.push_back(v3)

	var arrays = Array()
	arrays.resize(ArrayMesh.ARRAY_MAX)
	arrays[ArrayMesh.ARRAY_VERTEX] = vertices
	var tmpMesh = ArrayMesh.new()
	tmpMesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
	$ArrayMeshTriangle.mesh = tmpMesh
```

* **@onready var m_triangle_vertex_list = Array()**
　三角形の3点を保存するための配列を宣言します。

* **func add_vertex_position(v3_pos):**
 タッチした点の3D空間座標をm_triangle_vertex_listに追加します。
　3点（以上）になった場合はcreate_triangle_array_meshメソッドに3点の配列を引数として呼び出します。
　呼び出した後はm_triangle_vertex_listをクリアします。
```GDScript
	# Triangle
	m_triangle_vertex_list.append(v3_pos)
	if m_triangle_vertex_list.size() >= 3:
		create_triangle_array_mesh(m_triangle_vertex_list)
		m_triangle_vertex_list.clear()
```

* **func create_triangle_array_mesh(vertex_list):**
 LINEStripの処理とほとんど同じですが、add_surface_from_arrays()メソッドの第1引数をMesh.PRIMITIVE_TRIANGLESに変更しました。

```GDScript
func create_triangle_array_mesh(vertex_list):
	var vertices = PackedVector3Array()
		
	for v3 in vertex_list:
		vertices.push_back(v3)

	var arrays = Array()
	arrays.resize(ArrayMesh.ARRAY_MAX)
	arrays[ArrayMesh.ARRAY_VERTEX] = vertices
	var tmpMesh = ArrayMesh.new()
	tmpMesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
	$ArrayMeshTriangle.mesh = tmpMesh
```

　実行します。
　3点は画面を見て時計回りにクリックしてください。

:::note
　三角形メッシュの場合頂点を格納する順番はとても重要です。3点を順番にたどったときに時計回りに見える面を表面、反対側を裏面と判断します。裏面は特に指定がなければ描画されません。本記事では実装しませんが3点を時計回りに並べなおす必要がありそうです。

https://docs.godotengine.org/en/latest/classes/class_arraymesh.html
 Note: Note: Godot uses clockwise winding order for front faces of triangle primitive modes.
:::
　3点毎に三角形が表示されるようになりました。
![スクリーンショット (633).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/5f7e19b6-d8eb-b911-b4df-6345caa9fba9.png)

# 法線を追加する
　面が暗いのは法線を指定していないためです。
　法線とは面に対して垂直なベクトル（下図の緑の矢印）で、ライティング処理に使用されます。
![スクリーンショット (609).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/592a2b65-c83a-6f9f-84f2-76c37ea6dcca.png)


## func create_triangle_array_mesh(vertex_list)に法線を設定する処理を追加する
今回追加している面は上方向（Y軸のプラス方向）を向いています。
create_triangle_array_mesh()に法線として上方向(Vector3.UP=(0,1,0)を設定する処理を追加しました。

```GDScript
func create_triangle_array_mesh(vertex_list):
	var vertices = PackedVector3Array()
	var normals = PackedVector3Array()
	
	for v3 in vertex_list:
		v3.y = 0.1
		vertices.push_back(v3)
		normals.push_back(Vector3.UP)
		
	var arrays = Array()
	arrays.resize(ArrayMesh.ARRAY_MAX)
	arrays[ArrayMesh.ARRAY_VERTEX] = vertices
	arrays[ArrayMesh.ARRAY_NORMAL] = normals
	var tmpMesh = ArrayMesh.new()
	tmpMesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
	$ArrayMeshTriangle.mesh = tmpMesh
```

* **var normals = PackedVector3Array()**
　法線はPackedVector3Array配列を使用します。

* **normals.push_back(Vector3.UP)**
　上方向のベクトルを法線として登録します。
　描画した三角形が見えるように、高さy=0.1mを設定します。

```GDScript
	for v3 in vertex_list:
		v3.y = 0.1
		vertices.push_back(v3)
		normals.push_back(Vector3.UP)
```

* **arrays[ArrayMesh.ARRAY_NORMAL] = normals**
　配列のArrayMesh.ARRAY_NORMAL番目(1番目）に法線配列を設定します。

　実行します。地面の上面と同じ色になりました。
　
![スクリーンショット (610).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/dba5ded0-233b-79df-e95e-16d5679b3e32.png)

## （試しに）頂点ごとに法線の向きを変える
　法線はライティング処理に使用されるのを確認します。

　先ほど追加した「normals.push_back(Vector3.UP)」を削除して、下記のように3点分の法線として、Vector3.UP、Vector3.BACK、Vector3.DOWNを追加してみます。

```GDScript
func create_triangle_array_mesh(vertex_list):
	var vertices = PackedVector3Array()
	var normals = PackedVector3Array()
	
	for v3 in vertex_list:
		v3.y = 0.1
		vertices.push_back(v3)

	normals.push_back(Vector3.UP)
	normals.push_back(Vector3.BACK)
	normals.push_back(Vector3.DOWN)

	var arrays = Array()
	arrays.resize(ArrayMesh.ARRAY_MAX)
	arrays[ArrayMesh.ARRAY_VERTEX] = vertices
	arrays[ArrayMesh.ARRAY_NORMAL] = normals
	var tmpMesh = ArrayMesh.new()
	tmpMesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
	$ArrayMeshTriangle.mesh = tmpMesh
```

　実行します。
　描画しているのは3点の平面の三角形ですが、立体的に見えるようになりました。
　1クリック目は三角形の右側の点で、法線はVector3.UPを指定しているので、地面の上面と同じ色（同じライティング）になります。
　2クリック目は三角形の下側の点で、法線はVector3.BACK（手前側）を指定しているので、地面の手前と明るい色（同じライティング）になります。
　1クリック目は三角形の左上の点で、法線はVector3.DOWN（地面の裏側）を指定しているので、真っ暗です。

　3点の中間は自動で計算しているのが、頂点があるように見えますね。
　本来は球を疑似した三角形メッシュでもライティングで球に見えるようにするためのものだと思います。

![スクリーンショット (615).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/1f298f23-ea71-ab63-affe-5d0871c68850.png)


# Materialを設定して緑色にする
　Materialを追加して、緑色に変更します。
　法線はVector3.UPに戻しています。

```GDScript
func create_triangle_array_mesh(vertex_list):
	var vertices = PackedVector3Array()
	var normals = PackedVector3Array()
	
	for v3 in vertex_list:
		v3.y = 0.1
		vertices.push_back(v3)
		normals.push_back(Vector3.UP)

	var arrays = Array()
	arrays.resize(ArrayMesh.ARRAY_MAX)
	arrays[ArrayMesh.ARRAY_VERTEX] = vertices
	arrays[ArrayMesh.ARRAY_NORMAL] = normals
	var tmpMesh = ArrayMesh.new()
	tmpMesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
	$ArrayMeshTriangle.mesh = tmpMesh

	# Materialを設定
	var material = StandardMaterial3D.new()
	material.albedo_color = Color(0, 1, 0)
	$ArrayMeshTriangle.mesh.surface_set_material(0, material)
```

　最後に追加した3行でMaterialを作成し、albedo_colorに緑をにして、Meshに設定します。

![スクリーンショット (617).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/562c9a90-7532-0999-e448-7624afa3977a.png)

# Materialファイルを作成してArrayMeshに適用する
　Materialをコードからすべて設定するのは大変なので、Materialリソースファイルを作成します。

　ファイルシステムの「res」を右クリックして、新規からリソースを実行します。
![スクリーンショット (623).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/21075800-28bf-c2ab-d1ee-907fb4acf3a2.png)

　StandardMaterial3Dを選択します。
![スクリーンショット (619).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/8c1dec85-7693-ea0d-62fa-2e1c426e20cd.png)
　ファイル名を「triangle_material.tres」に変更して保存します。
![スクリーンショット (620).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/f52b6c6b-751f-bd4e-09e9-439b15b86a38.png)
　作成されました。
![スクリーンショット (624).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/faeb71d3-665f-7eb2-7252-46852b47408e.png)

　作成した「triangle_material.tres」をダブルクリックして、インスペクタのBaseMaterial3Dのalbedoを開いて、Colorを赤に変更します。
![スクリーンショット (625).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/b93c02a8-3650-8d4b-17e6-7a5b5fe916fc.png)

　スクリプトから「triangle_material.tres」を読み込んでArrayMeshに設定します。

```GDScript
var m_material = preload("res://triangle_material.tres")

func create_triangle_array_mesh(vertex_list):
	var vertices = PackedVector3Array()
	var normals = PackedVector3Array()
	
	for v3 in vertex_list:
		v3.y = 0.1
		vertices.push_back(v3)
		normals.push_back(Vector3.UP)

	var arrays = Array()
	arrays.resize(ArrayMesh.ARRAY_MAX)
	arrays[ArrayMesh.ARRAY_VERTEX] = vertices
	arrays[ArrayMesh.ARRAY_NORMAL] = normals
	var tmpMesh = ArrayMesh.new()
	tmpMesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
	$ArrayMeshTriangle.mesh = tmpMesh

	# Materialを設定
	$ArrayMeshTriangle.mesh.surface_set_material(0, m_material)
```

　実行します。赤くなりました。
![スクリーンショット (627).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/14ccf36c-4c1f-87f7-51c8-a14590e2e73b.png)

# Collisionを設定する
　Meshから衝突形状を生成して設定することができます。

　シーンのルートノードのWorldを右クリックして子ノードを追加からArea3Dを選択します。
　Area3Dを右クリックして子ノードを追加からCollisionShape3Dを追加します。

　衝突形状を見やすいようにCollisionShape3DのインスペクタのNode3D/Transform/Positionのyを1.0mにしておきます。

　![スクリーンショット (628).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/b9fa8d6d-c0e8-48fc-30c8-3fd9281f93d6.png)

　create_triangle_array_mesh関数の最後に以下を追加します。
　create_trimesh_shapeメソッドはMesh形状からConcavePolygonShape3Dを生成します。
```GDScript
	# MeshからCollision形状(ConcavePolygonShape3D)生成
	$Area3D/CollisionShape3D.shape = tmpMesh.create_trimesh_shape()
```
　目視で確認できるように、デバッグメニューから「コリジョン形状を表示」をチェックします。
　![スクリーンショット (631).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/647aa26a-1648-f530-50c8-c39aa98b930c.png)

　実行します。
　3点をクリックして赤い三角形を表示すると、その1m上に衝突形状が表示されているのがわかります。
![スクリーンショット (632).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/b06a7920-6730-e1fd-96b6-6dd3e6a4b561.png)

　以上。