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 を開き、この辺りを確認します。
ここでは、自分のノードと衝突したノードをccで取得し、if文でbulletノードであるかを確認しているようです。(ここでは、弾が敵に当たった時の処理を書いているため)
今回はここの詳細は調べていないので割愛します。
2. Area3Dノードをつかう
公式ドキュメントや2Dゲームの方法を色々みた結果、この方法で作りました。
まず、衝突を検知するノードの型をArea3Dとしてシーンに追加します。ここではCoin000という名前にします。このCoin000と3Dキャラクタの衝突を検知する処理を説明します。
Coin000 (型はArea3D) の子として、MeshInstance3D、Collision3Dを追加します。MeshInstance3DはSphereの形にします。
次にシグナルを接続します。
シグナルとはGodot Engine固有?の仕組みです。 Area3Dノードを選択した状態で画面右側のインスペクターの隣のタブにある「シグナル」というメニューを表示させると選べます。
このようにArea3Dや、子にしたCollision3Dなどのイベントハンドラを色々と選べます。
今回はbody_enteredというシグナル関数を選択します。「接続」を選択すると、選択したスクリプトに自動的に関数の雛形が入ります。ここではPlayerノードにつけているPlayer.gdと接続しているので、Player.gdにbody_entered関数の雛形が追加されます。
ここでは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という型で表示しています。
次の行は、Coin000のノードをシーン全体から探しています。Unityを知っている方はGameObject.Findと同等と考えるとわかりやすいと思います。
この辺に少し書いておきました。
最後のCoin000.hide()を実行すると、Coin000ノードを非表示にできます。
実行した結果です。
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モデルを扱う情報があまりないので、何かの参考になれば幸いです。