ゲームエンジンgodot4.0で3Dスマホゲームを作りたいと思いますが、その前にお勉強しています。
Godot_v4.0-rc1_win64.exe.zipを使用しています。
目的
Godotの衝突制御を確認します。
ぶつかって止まる、ぶつかって移動する、もしくは通過について確認します。
Godotの衝突制御をするためのノード
CollisionObject3Dを継承するノード
Godotの衝突制御はCollisionObject3Dを継承するノードで使用できます。
用途によって使い分けます。
(AnimatableBody3DとPhysicalBone3Dは勉強不足でわかりません)
ノード | 用途 |
---|---|
StaticBody3D | 動かないので、主に地形、壁、建物、固定オブジェクトの表現に適しています。 |
CharacterBody3D | 移動やジャンプなどの自由な操作を実装することができますので、主にキャラクターやプレイヤーに適しています。 |
RigidBody3D | 物理エンジンを使用した3Dオブジェクトのシミュレーションにより動きます。 |
Area3D | 衝突せず通過しますが、他ノードの衝突(エリアに入る/出る)を検出することができます。 |
衝突形状
衝突する形状はCollisionShape3Dに設定します。CharacterBody3D等の子ノードとしてぶらさげるとGodotエンジンはノード間の衝突をCollisionShape3Dに設定した形状で逐次チェックします。
見た目はMeshInstance3Dなどで定義
見た目はMeshInstance3Dなどを子ノードとしてぶら下げて定義します。
見た目と衝突形状は別々に設定するということを意識します。
RigidBody3Dを追加する(重力で落ちる)
新規プロジェクトを作成して下記のようになるようにノードを追加しましょう。
ルートノードはNode3Dで、名称をglobalに変更しています。
- Camera3DはRigitBody3D全体が見えるように手前に移動
インスペクタ内のNode3D/Transform/zに5mを設定します。 - MeshInstance3Dに球を設定
インスペクタ内のMeshInstance3D/Meshに新規SphereMeshを設定します。 - CollisionShape3Dに衝突形状を設定
インスタンス内のCollisionShape3D/Shapeに新規SphereShape3Dを設定します。 - DirectionalLight3Dをほかのノードと重ならないように移動
インスペクタ内のNode3D/Transform/xに-10mを設定します。
実行すると球が一瞬で落ちます。
RigidBody3Dは物理エンジンが動かし、重力が働いているので下に落ちていきます。
StaticBody3Dで床を作成
RigidBody3Dの球を受け止めるためにStaticBody3Dで床をつくります。
globalの子としてStaticBody3Dを追加し、StaticBody3Dの子にMeshInstance3DとCollisionShape3Dを追加します。
-
StaticBody3Dを下に移動します
インスペクタ内のNode3D/Transform/yに-3mを設定します。 -
MeshInstance3Dに立方体を設定して床にします
インスペクタ内のMeshInstance3D/Meshに新規BoxMeshを設定します。
インスペクタ内のMeshInstance3D/Meshのアイコンをクリックすると、サイズを設定できます。Sizeのxとzを10mにします。
-
CollisionShape3Dに衝突形状としてMeshInstance3Dと同じサイズの立方体を設定します。
インスタンス内のCollisionShape3D/Shapeに新規BoxShape3Dを設定します。
インスタンス内のCollisionShape3D/ShapeのBoxShape3DをクリックするとSizeを設定できます。
Sizeのxとzを10mにします。
Godot3.5のときはSizeではなくExtentsというパラメータで中心からの長さを設定していました。
MeshInstanceが10mだから半分の5mを設定するというように変換が必要でしたので、4.0になって設定しやすくなりました。
実行します。
RigidBody3Dの球が重力によって落ちますが、StaticBody3Dの床で止まりました。
Godotの物理エンジンが自動的に制御をしています。
少し床を傾けてみましょう。
StaticBody3Dのインスペクタ内のNode3/Transform/rotation/zを5°にします。
実行すると球は落ちた後に転がります。
球が跳ねるようにしましょう。
RigidBody3Dを選択して、インスペクタ内のRigidBody3D/Physics Material Overrideのをクリックして新規PhysicsMaterialをクリックします。
PhysicsMaterialをクリックするとBounceという項目があるので、0.5にします(初期値0.0)
実行すると落ちたボールが跳ね返るようになります。
見た目と衝突形状は異なります。
実行画面を見ると、床の上をボールが転がるように見えていますが、GodotエンジンはCollisionShape3Dの形状のみチェックしていることを意識しましょう。
StaticBody3Dの子のCollisionShape3Dを選択して、インスペクタ内のNode3D/Transform/position/yを2mに設定して実行してください。
MeshInstance3Dは変更していないため見た目の床の位置は変わっていませんが、衝突判定を行うCollisionShape3Dが上に移動したため、ボール落ちずに空中を転がります。
親のStaticBody3Dを移動するべきところを間違って、MeshInstance3Dだけとか、CollisionShape3Dだけ移動すると、見た目と衝突形状がずれて動作がおかしくなることがあるので注意が必要です。
デバッグメニューの「コリジョン形状を表示」をチェックすると衝突形状がワイヤフレームで表示されて、衝突箇所が赤く描画されます。デバッグに活用できると思います。
StaticBody3Dの子のCollisionShape3Dを選択して、インスペクタのNode3D/Transform/position/yを0mに戻しましょう。
CharacterBody3Dを追加
ルートノードglobalの子にCharacterBody3Dを追加します。
CharacterBody3Dの子にMeshInstance3D、CollisionShape3Dを追加しましょう。
- CharacterBody3Dを2m左に移動します
インスペクタのNode3D/Transform/xに-2mを設定します。 - MeshInstance3Dに立方体を設定します
インスペクタの新規BoxMeshを選択 - CollisionShape3Dも立方体を設定します
インスペクタのCollisionShape3D/Shapeを新規BoxShape3Dを選択します。
実行しましょう。
CharacterBody3Dとして追加した立方体は空中で止まっています。
CharacterBody3Dは衝突の応答はありますが、動かすのはGDScriptでプログラミングする必要があります。
シーン内のCharacterBody3Dを右クリックして、スクリプトをアタッチしましょう。
テンプレートに「CharacterBody3D:Basic Movement」を選択すると、重力により落下し、カーソルキーによる移動、スペースキーでジャンプするようなスクリプトが挿入されます。
挿入されたスクリプト(テンプレートのまま)
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の球は落ちた後転がり、立方体にぶつかって押しています。
そのままにしておくと、球に押されて立方体がおちてしまいます。
カーソル右を押下して抵抗すると球の動きが止まります。CharacterBody3Dの衝突がRigidBody3Dの動きに影響を与えるのがわかります。スペースキーの押下でジャンプして球の上にのることもできます。
Area3Dを通過することを確認
ルートノードglobalの子にArea3Dを追加します。
Area3Dの子にMeshInstance3D、CollisionShape3Dを追加しましょう。
- Area3Dを立方体と球の通るところに移動する
インスペクタのNode3D/Transform/xに-4m、yに-2mを設定します。 - MeshInstance3Dに円柱を設定
インスペクタの新規CylinderMeshを選択 - CollisionShape3Dも円柱を設定
インスペクタのCollisionShape3D/Shapeを新規CylinderShape3Dを選択します。
実行します。
立方体と球が、Area3Dの円柱を通り抜けます。
Area3DはPhysicsBody3Dを継承していないため、物理エンジンによる衝突することはありません。
衝突判定はしているので、名前の通り、CharacterBody3D、RigidBody3D、Area3Dがあるエリアに入ったとき出たときをチェックすることができます。
以上