今回は Godot Engine の物理システムの中核となる「RigidBody2D」の基本的な使い方を紹介します。
ここで紹介した内容は、投擲ゲームやバランスゲームなど、 Physics puzzle game (物理パズルゲーム) を作るのに役立つと思います。
目次
RigidBody2Dの基本的な使い方
使用する素材
今回使用する素材としてプロジェクトファイルを添付しておきます。
プロジェクトの説明
このプロジェクトの構成は以下の通りです
- Main.tscn: メインシーン。ここに壁やボールを配置しています (F5で実行されるシーン)
- Wall.tscn: 壁シーン。このシーンを壁として、メインシーンの上下左右に配置しています
- Ball.tscn: ボールシーン。このボールで RigidBody2D の動作テストをします
メインシーンは以下のようになっています。
Wallsノード以下が周囲を囲む壁で「Wall」シーンを複製して配置しています。そしてBallノードが「Ball」シーンを配置したものです。
またグリッドスナップを有効にしていますが、もし無効にしたい場合は
このアイコンをクリックして無効にします。
壁シーンは、RigidBody2Dノードの下に「ColorRect」「CollisionShape2D」をぶら下げています。
特に変わったことはせずに矩形のコリジョン (RectangleShape2D) を ColorRectに重ねています。なお壁は動かないので、RigidBody2D の Mode は「Static」となっています。
Ballシーン(RigidBody2Dノード)の下には、 Sprite (Ball.png) と円形コリジョンをぶら下げています。
そして、RigidBody2Dのパラメータは以下のようにしています。
初期状態だとふんわりした動きになるので、パラメータを以下のようにしました。
- Mass (質量): 8.03
- Weight (重さ): 78.69
- Physics Material
- Friction (摩擦係数): 0.5
- Bounce (弾力性): 0.5
- Gravity Scale (重力係数): 8.93
ずっしりとした重さで程々の弾力性と滑りがある物体です。
動作確認
では、F5 (macOSの場合は Cmd+B) で実行して動きを確認してみます。
開始するとボールが落下します。また「Spaceキー」を押すことで、右方向に力が発生してボールが右に吹っ飛びます。これはスクリプトでボールの動きが記述されているためです。
色々な物理関数を試す
ここからは色々な物理の力を発生させる関数を使用してみます。
Ballシーンのアタッチされているスクリプト「Ball.gd」をクリックして開いてみます。
Ball.gd には以下のように記述されています。
extends RigidBody2D
func _ready() -> void:
pass # Replace with function body.
func _process(delta: float) -> void:
if Input.is_action_just_pressed("ui_accept"):
# 右方向に力を加える
apply_central_impulse(Vector2(10000, 0))
apply_central_impulse() が呼び出されており、これが剛体 (RigidBody2D) に力を加える関数となります。
central (中央) とある通り、apply_central_impulse() はボールの “中心” に力を加えます。
Ball.gd を開いて、以下のように修正します。
extends RigidBody2D
func _ready() -> void:
pass # Replace with function body.
func _process(delta: float) -> void:
if Input.is_action_just_pressed("ui_accept"):
# ボールの上部分に右方向からの力を加える
apply_impulse(Vector2(0, -30), Vector2(5000, 0))
では実行して動作を確認してみます。
先程よりも、ボールが「回転」しやすくなっているように見えます。これは力を加える位置がボールの上部分になっているため、ボールを回転する力が発生しているためです。
apply_impulse() は第1引数に力を加える位置のオフセット座標で、第2引数がその力の値となります。
もちろん、第1引数の値をゼロにすれば中心に力を加えることもできます。
apply_impulse(Vector2.ZERO, Vector2(5000, 0))
続けて、Ball.gd を以下のように修正します
extends RigidBody2D
func _ready() -> void:
pass # Replace with function body.
func _process(delta: float) -> void:
if Input.is_action_just_pressed("ui_accept"):
# ボールに時計回りのトルクを加える
apply_torque_impulse(80000)
実行して動きを確認します。
ボールに力が加わるというよりも「回転」させているように見えます。torque (トルク) というのは、剛体の回転の中心を基準に回転する力を加える、という意味で、apply_torque_impulse() はトルク(回転)を発生させる関数となります。
さらにコードを修正します。
extends RigidBody2D
func _ready() -> void:
pass # Replace with function body.
func _process(delta: float) -> void:
if Input.is_action_just_pressed("ui_right"):
# 右方向に力を加え続ける
add_central_force(Vector2(5000, 0))
if Input.is_action_just_pressed("ui_left"):
# 左方向に力を加え続ける
add_central_force(Vector2(-5000, 0))
「左右キー」を押したときに add_central_force() を呼び出すようにしました。
左右キーの押した方にボールが動き続けます。apply_central_impulse() との違いは「力が加え続けられる」ということです。押した方向に重力が働き続けるかのように動き続けるのが add_central_force() となります。
なお、add_force() という力が加わる位置を指定する関数も用意されています。
# ボールの上の部分に力を加え続ける
add_force(Vector2(-30, 0), Vector2(10000, 0))
まとめ
ここまで使用した関数の種類をまとめてみます
- apply_impulse: 衝撃を加える (そのタイミングだけ力を発生させる)
- central: 中心に力を発生させる
- torque: 回転を発生させる
- add_force: 力を発生し続ける
このあたりの単語の違いが理解できれば、物理の力を発生させる関数はほぼ理解できたこととなります。
Area2D を使用した力の発生
少し応用となりますが、Area2D と組み合わせることで、指定の領域範囲内のみ力を発生させるという使い方もできます。
Area2Dとコリジョンを設定する
Mainシーン (Main.tscn) を開いて、Area2D ノードを追加してその下に CollisionShape2D を配置します。
そして CollisionShape2D のインスペクターから、Shape を「CircleShape」にして、それをクリックして表示されるパラメータの「Radius」を「240」にします。
するとスクリーン座標の原点 (0, 0) にコリジョンの円が表示されるので、シーンビューから「Area2D」「CollsionShape2D」を Shiftキーを押しながら複数選択します。
そしてArea2Dのコリジョンをキャンバス中央あたりに移動させておきます。
ただ、このまま実行しても Area2D の領域は視認できません。
そこで、メニューから「デバッグ > コリジョン形状を表示」にチェックを入れます。
そうすると、実行時でもコリジョンの大きさが視認できるようになります。
Area2Dの物理パラメータを設定する
それでは、Area2Dの物理パラメータを設定します。まずはシーンビューから「Area2D」を選択します。
そしてインスペクタから「Physics Overrides」を開き「Space Override」のパラメータを「Combine」にします。
Space Override とは、この領域内の剛体に対する重力や減衰を行う設定です。
ここで指定するパラメータは公式のドキュメントに説明があります
enum SpaceOverride:
- SPACE_OVERRIDE_DISABLED = 0 — この領域では重力/減衰が発生しない
- SPACE_OVERRIDE_COMBINE = 1 — この領域ではこれまでに計算されたものに (priority orderに従って) 重力/減衰値を合成する
- SPACE_OVERRIDE_COMBINE_REPLACE = 2 — この領域ではこれまでに計算されたものに重力/減衰値を (priority orderに従って) 合成し、priority の低い領域は無視する
- SPACE_OVERRIDE_REPLACE = 3 — この領域ではpriority orderの低い領域を無視して、デフォルトであっても重力/減衰値を置き換える
- SPACE_OVERRIDE_REPLACE_COMBINE = 4 — この領域ではこれまでに計算された重力/減衰値を (priority orderに従って) 置き換えるが、残りの領域の計算は続けられる
領域 (Area2D) の重なりが発生しないのであれば、”Combine” で通常は問題ないはずです。強制的に力の方向を変えたい場合には “Replace” になると思います。
……と説明してみたものの、実際に動きを見ないとイメージできないと思うので、物理パラメータを設定していきます。
インスペクタから以下のパラメータを設定します
- Gravity Point: チェックを入れる
- Gravity Vec > x: 0
- Gravity Vec > y: 0
- Gravity: 280
「Gravity Point」にチェックを入れると、”Gravity Vec” の値を基準に動作するようになります。そして、”Gravity Vec” の (x, y) が (0, 0) なので、この領域の中心が基準となり、”Gravity” の力が発生します。
実際に動作を見てみます。
ボールの動きの制御が add_force() だと確認がしにくいので、apply_central_impulse() に書き換えて確認したほうが良いかもしれません。
extends RigidBody2D
func _process(delta: float) -> void:
if Input.is_action_just_pressed("ui_accept"):
# 右方向に力を加える
apply_central_impulse(Vector2(10000, 0))
水の浮力的な領域を作る
先程の例は重力装置的な領域としましたが、水の浮力をイメージした領域を作ることもできます。
CollisionShape2D を “RectangleShape2D” にして、水槽の水の部分のような領域に修正します。
そして、Area2D のインスペクタから以下のようにパラメータを設定します。
- Gravity Point: チェックを外す
- Gravity Vec > x: 0
- Gravity Vec > y: -1
- Gravity: 120
- Linear Damp: 0.5
「Gravity Point」のチェックを外しました。これにより、「Gravity Vec」は特定の方向に働く力の値となります。Gravity Vec の (x, y) が (0, -1) なので上方向へと働く力となります。「Gravity」の値は「120」です。この値が大きくなるほど浮力が大きくなります。「Linear Damp」は減衰値で、ここでは “水の抵抗” をイメージした値となります。
では実行して動作を確認してみます。
今回作成したプロジェクト
今回の内容をまとめたプロジェクトファイルを添付しておきます
水の浮力を実験したプロジェクトも添付しておきます。
参考
How to Apply Forces to a RigidBody in Godot (2D & 3D)
関連記事
物理オブジェクトを結合する「2D Joint」の使い方については以下の記事にまとめています。
【Godot】2D Jointの使い方