CrossRoad

XRを中心とした技術ブログ。 Check also "English" category.

Godot Engine4.2.1で3Dキャラクタと3Dモデルの衝突判定をつける方法

Godot Engineの使い方を調べると2Dゲームの情報は色々出るのですが、3Dゲームの情報はあまり出てきません。

実際は他にもあるかもしれませんが、3Dモデル同士の衝突判定をする方法を2つ見つけたのでまとめました。

使用した環境
Godot Engine 4.2.1 (Standard)

1. state.get_contact_collider_object関数を使う

公式から提供されているサンプルプロジェクトにありました。

https://godotengine.org/asset-library/asset/125

このページからplatformer.zipをダウンロードして解凍し、Godot Engineで開きます。

Enemyノードのスクリプト enemy.gd を開き、この辺りを確認します。

gist.github.com

ここでは、自分のノードと衝突したノードをccで取得し、if文でbulletノードであるかを確認しているようです。(ここでは、弾が敵に当たった時の処理を書いているため)

今回はここの詳細は調べていないので割愛します。

2. Area3Dノードをつかう

公式ドキュメントや2Dゲームの方法を色々みた結果、この方法で作りました。

まず、衝突を検知するノードの型をArea3Dとしてシーンに追加します。ここではCoin000という名前にします。このCoin000と3Dキャラクタの衝突を検知する処理を説明します。

Coin000 (型はArea3D) の子として、MeshInstance3D、Collision3Dを追加します。MeshInstance3DはSphereの形にします。

An example of area3D on Godot Engine4.2.1

次にシグナルを接続します。

シグナルとはGodot Engine固有?の仕組みです。 Area3Dノードを選択した状態で画面右側のインスペクターの隣のタブにある「シグナル」というメニューを表示させると選べます。

Connecting signal on Godot Engine 4.2.1

このようにArea3Dや、子にしたCollision3Dなどのイベントハンドラを色々と選べます。

今回はbody_enteredというシグナル関数を選択します。「接続」を選択すると、選択したスクリプトに自動的に関数の雛形が入ります。ここではPlayerノードにつけているPlayer.gdと接続しているので、Player.gdにbody_entered関数の雛形が追加されます。

An example of added script on Godot Engine

ここではL76が追加された行です。それ以下は私が自分で書いています。

シグナルを接続したので、「76」という行の左に矢印のようなアイコンがでます。先ほどのシグナル選択画面で「切断」を選択すると、スクリプトを再表示することでこの矢印も消えます。

あとはこの関数に何と衝突したらどうしたいか、を書いていきます。
今回は3Dキャラクターと衝突したらCoin000ノードが非表示になるとしました。このように書きます。

func _on_coin_000_body_entered(body):
    if body is CharacterBody3D:
        var Coin000 = WorldNode.get_node("Coin000")
        Coin000.hide()

以下、解説です。

最初のif文で、衝突したノードの型がCharacterBody3Dだったときにif文の処理が実行されます。移動できるキャラクターはCharacterBody3Dという型で表示しています。

CharacterBody3D node on Godot Engine

次の行は、Coin000のノードをシーン全体から探しています。Unityを知っている方はGameObject.Findと同等と考えるとわかりやすいと思います。

この辺に少し書いておきました。

www.crossroad-tech.com

最後のCoin000.hide()を実行すると、Coin000ノードを非表示にできます。

実行した結果です。

An example of character locomotion on Godot Engine4.2

3. 補足

Area3Dを使う方法のところでWorldNodeという変数が出ましたが、ここについて少し補足します。

ここではCoin000ノードを取得する処理を書いていますが、WorldNode変数自体が事前に以下の形式で取得したものを使っています。

func _ready():
    WorldNode = get_tree().get_root().get_child(1)

_reday()はUnityのStart()関数相当です。シーン実行時に1回だけ呼ばれます。

上記の例では、プロジェクト設定で「自動読み込み(Autoloading)」を使っているので、シーン実行時にget_child(0)が自動読み込みの値になり、get_child(1)が、シーン全体を意味します。

自動読み込み機能を使っていない場合、get_tree().get_root().get_child(0)とします。

4. おわりに

Godot Engineの3Dモデルを扱う情報があまりないので、何かの参考になれば幸いです。