【Godot 4.0】スマホ３Dゲームを作るための勉強　その５　Godotエンジンによる衝突の制御

 ゲームエンジンgodot4.0で3Dスマホゲームを作りたいと思いますが、その前にお勉強しています。
 [Godot_v4.0-rc1_win64.exe.zip](https://downloads.tuxfamily.org/godotengine/4.0/rc1/Godot_v4.0-rc1_win64.exe.zip)を使用しています。

# 目的
　Godotの衝突制御を確認します。
　ぶつかって止まる、ぶつかって移動する、もしくは通過について確認します。

# Godotの衝突制御をするためのノード

### CollisionObject3Dを継承するノード
　Godotの衝突制御はCollisionObject3Dを継承するノードで使用できます。

![(_) node_3d.tscn - 05_collision002 - Godot Engine 2023_02_23 17_00_51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/c7a145e5-600f-3c18-2621-5210ffe375a8.png)

　用途によって使い分けます。
　（AnimatableBody3DとPhysicalBone3Dは勉強不足でわかりません）

| ノード | 用途 |
|:-----------|:------------|
| StaticBody3D       |動かないので、主に地形、壁、建物、固定オブジェクトの表現に適しています。 |
| CharacterBody3D       | 移動やジャンプなどの自由な操作を実装することができますので、主にキャラクターやプレイヤーに適しています。 |
| RigidBody3D       | 物理エンジンを使用した3Dオブジェクトのシミュレーションにより動きます。      |
| Area3D       | 衝突せず通過しますが、他ノードの衝突（エリアに入る／出る）を検出することができます。        |

### 衝突形状
　衝突する形状はCollisionShape3Dに設定します。CharacterBody3D等の子ノードとしてぶらさげるとGodotエンジンはノード間の衝突をCollisionShape3Dに設定した形状で逐次チェックします。
![スクリーンショット (15).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/c4250fa4-adc9-4025-586e-ffa33b376a73.png)

### 見た目はMeshInstance3Dなどで定義
　見た目はMeshInstance3Dなどを子ノードとしてぶら下げて定義します。
　見た目と衝突形状は別々に設定するということを意識します。

# RigidBody3Dを追加する（重力で落ちる）
　新規プロジェクトを作成して下記のようになるようにノードを追加しましょう。
　ルートノードはNode3Dで、名称をglobalに変更しています。
![global.tscn - 05_collision003 - Godot Engine 2023_02_23 22_33_07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/2eca1144-4b91-ea8c-b60b-c7ade134af72.png)

1. Camera3DはRigitBody3D全体が見えるように手前に移動
　インスペクタ内のNode3D/Transform/zに5mを設定します。
1. MeshInstance3Dに球を設定
　インスペクタ内のMeshInstance3D/Meshに新規SphereMeshを設定します。
1. CollisionShape3Dに衝突形状を設定
　インスタンス内のCollisionShape3D/Shapeに新規SphereShape3Dを設定します。
1. DirectionalLight3Dをほかのノードと重ならないように移動
　インスペクタ内のNode3D/Transform/xに-10mを設定します。

![(_) global.tscn - 05_collision003 - Godot Engine 2023_02_23 22_40_15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/1be2968b-c727-1243-0b5d-0c221175871f.png)

　実行すると球が一瞬で落ちます。
　RigidBody3Dは物理エンジンが動かし、重力が働いているので下に落ちていきます。

# StaticBody3Dで床を作成
　RigidBody3Dの球を受け止めるためにStaticBody3Dで床をつくります。
　globalの子としてStaticBody3Dを追加し、StaticBody3Dの子にMeshInstance3DとCollisionShape3Dを追加します。
![(_) global.tscn - 05_collision003 - Godot Engine 2023_02_23 22_46_24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/7ca7ea83-ac67-7ecf-aeb7-140831b7f8cb.png)

1. StaticBody3Dを下に移動します
　インスペクタ内のNode3D/Transform/yに-3mを設定します。
1. MeshInstance3Dに立方体を設定して床にします
　インスペクタ内のMeshInstance3D/Meshに新規BoxMeshを設定します。
　インスペクタ内のMeshInstance3D/Meshのアイコンをクリックすると、サイズを設定できます。Sizeのxとｚを10mにします。
![フォト 2023_02_23 22_52_48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/593fe1cc-50b3-4271-ed72-0a0736d39c09.png)

1. CollisionShape3Dに衝突形状としてMeshInstance3Dと同じサイズの立方体を設定します。
　インスタンス内のCollisionShape3D/Shapeに新規BoxShape3Dを設定します。
　インスタンス内のCollisionShape3D/ShapeのBoxShape3DをクリックするとSizeを設定できます。
　Sizeのxとzを10mにします。

![global.tscn - 05_collision003 - Godot Engine 2023_02_23 23_05_15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/f6560d73-e21c-75b6-830f-ca7e3f169f19.png)

::: note
Godot3.5のときはSizeではなくExtentsというパラメータで中心からの長さを設定していました。
MeshInstanceが10mだから半分の5mを設定するというように変換が必要でしたので、4.0になって設定しやすくなりました。
:::

　実行します。
　RigidBody3Dの球が重力によって落ちますが、StaticBody3Dの床で止まりました。
　Godotの物理エンジンが自動的に制御をしています。
![C__Users_hathr_Videos_Captures 2023_02_23 23_14_43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/13b3b6db-ff59-f6f7-9b76-c9aaa6c8dffd.png)

　少し床を傾けてみましょう。
　StaticBody3Dのインスペクタ内のNode3/Transform/rotation/zを５°にします。

　実行すると球は落ちた後に転がります。

![global.tscn - 05_collision003 - Godot Engine 2023_02_23 23_19_36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/9cd10a5f-7338-99db-fbb1-bca76e671a2d.png)

　球が跳ねるようにしましょう。
　RigidBody3Dを選択して、インスペクタ内のRigidBody3D/Physics Material Overrideの<empty>をクリックして新規PhysicsMaterialをクリックします。
![スクリーンショット (16).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/a3d81817-e187-4563-243d-d32b0391c7fb.png)

　PhysicsMaterialをクリックするとBounceという項目があるので、0.5にします（初期値0.0)
![スクリーンショット (17).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/cc1f63c7-3bfc-4880-74e7-074328319065.png)

　実行すると落ちたボールが跳ね返るようになります。

# 見た目と衝突形状は異なります。
　実行画面を見ると、床の上をボールが転がるように見えていますが、GodotエンジンはCollisionShape3Dの形状のみチェックしていることを意識しましょう。

　StaticBody3Dの子のCollisionShape3Dを選択して、インスペクタ内のNode3D/Transform/position/yを2mに設定して実行してください。
　MeshInstance3Dは変更していないため見た目の床の位置は変わっていませんが、衝突判定を行うCollisionShape3Dが上に移動したため、ボール落ちずに空中を転がります。

　親のStaticBody3Dを移動するべきところを間違って、MeshInstance3Dだけとか、CollisionShape3Dだけ移動すると、見た目と衝突形状がずれて動作がおかしくなることがあるので注意が必要です。

　デバッグメニューの「コリジョン形状を表示」をチェックすると衝突形状がワイヤフレームで表示されて、衝突箇所が赤く描画されます。デバッグに活用できると思います。

![「【Godot 4.0】スマホ３Dゲームを作るための勉強　その５　衝突の制御」を編集 - Qiita - Google Chrome 2023_02_23 23_35_24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/5725b0fe-458a-f2ce-eab6-364925028500.png)

　
　StaticBody3Dの子のCollisionShape3Dを選択して、インスペクタのNode3D/Transform/position/yを0mに戻しましょう。

# CharacterBody3Dを追加
　ルートノードglobalの子にCharacterBody3Dを追加します。
　CharacterBody3Dの子にMeshInstance3D、CollisionShape3Dを追加しましょう。
![(_) global.tscn - 05_collision003 - Godot Engine 2023_02_24 8_49_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/747bd814-3c8f-9fff-326b-bfb1b5eac990.png)

1. CharacterBody3Dを２ｍ左に移動します
　インスペクタのNode3D/Transform/xに-2mを設定します。
1. MeshInstance3Dに立方体を設定します
　インスペクタの新規BoxMeshを選択
1. CollisionShape3Dも立方体を設定します
 　インスペクタのCollisionShape3D/Shapeを新規BoxShape3Dを選択します。

　実行しましょう。
　CharacterBody3Dとして追加した立方体は空中で止まっています。

![Screenshot 2023_02_24 8_52_56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/02edffb5-397f-7f1b-8e6e-d203e5f617ca.png)

　CharacterBody3Dは衝突の応答はありますが、動かすのはGDScriptでプログラミングする必要があります。
　シーン内のCharacterBody3Dを右クリックして、スクリプトをアタッチしましょう。

　テンプレートに「CharacterBody3D:Basic Movement」を選択すると、重力により落下し、カーソルキーによる移動、スペースキーでジャンプするようなスクリプトが挿入されます。
![「【Godot 4.0】スマホ３Dゲームを作るための勉強　その５　衝突の制御」を編集 - Qiita - Google Chrome 2023_02_24 8_54_56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/7724a7c0-a202-8412-a6b7-da4a993ff8db.png)

　挿入されたスクリプト（テンプレートのまま）

``` GDScript
extends CharacterBody3D


const SPEED = 5.0
const JUMP_VELOCITY = 4.5

# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")


func _physics_process(delta):
	# Add the gravity.
	if not is_on_floor():
		velocity.y -= gravity * delta

	# Handle Jump.
	if Input.is_action_just_pressed("ui_accept") and is_on_floor():
		velocity.y = JUMP_VELOCITY

	# Get the input direction and handle the movement/deceleration.
	# As good practice, you should replace UI actions with custom gameplay actions.
	var input_dir = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
	var direction = (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
	if direction:
		velocity.x = direction.x * SPEED
		velocity.z = direction.z * SPEED
	else:
		velocity.x = move_toward(velocity.x, 0, SPEED)
		velocity.z = move_toward(velocity.z, 0, SPEED)

	move_and_slide()

```

　ここではあまり詳しく説明はしませんが、CharacterBody3Dの動作を制御する場合、_processではなく_physics_processメソッドの中で制御します。
　Vector3型のvelocityに1秒あたりの移動量を設定して、move_and_slide()メソッドを実行することで移動します。

　実行しましょう。
　GDScriptによりCharacterBody3Dの立方体が重力によって落ちるようになりました。
　そして、RigidBody3Dの球は落ちた後転がり、立方体にぶつかって押しています。
　
![global.tscn - 05_collision003 - Godot Engine 2023_02_24 9_45_09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/54600d44-2b28-db8a-1c83-4925bc6b58a8.png)

　そのままにしておくと、球に押されて立方体がおちてしまいます。
　カーソル右を押下して抵抗すると球の動きが止まります。CharacterBody3Dの衝突がRigidBody3Dの動きに影響を与えるのがわかります。スペースキーの押下でジャンプして球の上にのることもできます。
　

# Area3Dを通過することを確認
　ルートノードglobalの子にArea3Dを追加します。
　Area3Dの子にMeshInstance3D、CollisionShape3Dを追加しましょう。
![ダウンロード 2023_02_24 10_19_52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/a0004127-6746-0cd8-7d54-c1d4c6ab2faa.png)


1. Area3Dを立方体と球の通るところに移動する
　インスペクタのNode3D/Transform/xに-4m、yに-2mを設定します。
1. MeshInstance3Dに円柱を設定
　インスペクタの新規CylinderMeshを選択
1. CollisionShape3Dも円柱を設定
 　インスペクタのCollisionShape3D/Shapeを新規CylinderShape3Dを選択します。
![C__Users_hathr_Videos_Captures 2023_02_24 10_24_00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/d4579b2c-8c0e-dc93-f203-9ae3f1670d73.png)

　実行します。
　立方体と球が、Area3Dの円柱を通り抜けます。
![Screenshot 2023_02_24 10_25_09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/aae933b9-0e7f-0cdd-6835-1d7ce1241333.png)

　Area3DはPhysicsBody3Dを継承していないため、物理エンジンによる衝突することはありません。
　衝突判定はしているので、名前の通り、CharacterBody3D、RigidBody3D、Area3Dがあるエリアに入ったとき出たときをチェックすることができます。


以上