2D / 3D ゲームを作成できる無料・オープンソースの軽快なゲームエンジン「Godot Engine 4」で、コイン画像を表示・物理演算するシーンを作成して、そのシーンを別のシーンのスクリプトから動的に子ノードとして作成・配置して、物理関数で指定した方向・力で飛ばす手順を紹介します。
※この記事は、サンプルアプリ TapTheTakarabako の開発のパート3でもあります。
※ GodotEngine のバージョンは 4.1.2 です。 .NET 版ではありません。
※「いらすとや」様の画像を使用しています。
※「ふい字」フォントを使用しています。
※記事で紹介するスクリプト / プログラム / コードは自己責任で使用してください。
コイン画像のプロジェクトへの追加
コイン画像のファイルのコピーをプロジェクトへ追加するには、エクスプローラの画像ファイルをファイルシステムドックのツリー上にドラッグ&ドロップします。
フォルダを変えたい場合は、再度ツリー上でドラッグ&ドロップして移動しましょう。
コイン画像は「草コインのイラスト | かわいいフリー素材集 いらすとや」を使用しました。
コイン画像を表示するシーンを作成
メニュー「シーン」→「新規シーン」を選択してから、シーンドックで「その他のノード」を選択します。
表示された「Node を新規作成」ダイアログで RigidBody2D を選択してルートノードを作成します。
下位ノードに画像を表示する Sprite2D クラスのノードと、当たり判定領域を設定する CollsionShape2D クラスのノードを追加します。
RigidBody2D +--- Sprite2D +--- CollisionShape2D(shape=CircleShape2D)
下位ノードを追加するには、シーンドックでルートノードを右クリックして表示される「子ノードを追加」メニューを押します。
表示されたダイアログで、Sprite2D, CollisionShape2D クラスを選択して「作成」ボタンを押して追加しましょう。
CollisionShape2D 子ノードを選択して、インスペクターノードで Shape プロパティの▼ボタンを押してリストを表示して「新規 CircleShape2D」を選択して割り当てます。
Sprite2D 子ノードを選択して、先ほどプロジェクトに追加したコインの画像ファイルをファイルシステムドックから、インスペクタードックの Texture プロパティにドラッグ&ドロップして設定します。
ルートノードを RigidBody2D から名前をシーンと同じものに変更します。
例では OtakaraCoin に変更しました。
ノードを設定したら、シーンを保存しましょう。
Ctrl+S などで保存ダイアログを表示して、シーンファイル名を snake_case 表記の otakara_coin と入力して「保存」ボタンを押します。
F6 キーを押すと、現在のシーンが再生されて、左上にコイン画像の右下が表示されました。
これは、左上原点に配置しているためです。
他のシーンで子ノードと使う際に原点に配置してあるほうが便利なので、位置は変更しません。
サイズは Sprite2D ノードを選択して、インスペクタードックの Node2D の Scale を変更することで調整できます。
※ 2D ビューで丸いハンドルをドラッグすることでも変更できます。
今回は無効にするので調整の必要はありませんが、CollisionShape2D ノードを選択してから 2D ビューでハンドルをドラッグすることで当たり判定領域を調整できます。
当たり判定をする必要がない場合は、 CollisionShape2D ノードを選択して、インスペクタードックで Disabled をチェックします。
当たり判定を有効にした状態でも後述のスクリプトでコインを飛ばすことはできますが、他のコインとぶつかりあってスムーズに飛びませんでした。
同じコインのシーンのノード同士だけ当たり判定をしないように RigidBody2D ノードの継承元の CollisionObject2D の Collision の Mask の 1 のボタンをクリックして他と同じくうす暗くすると、Layer で選択した自身が属する衝突判定のグループと当たり判定を行わないようにできます。
今回は一切の当たり判定を行わないので、前述のように CollisionShape2D ノードの Disable をチェックしました。
RigidBody2D をルートノードにした理由
RigidBody2D をルードノードにしない以下の構成にすると RigidBody2D の 物理関数apply_impulse を呼び出してもコイン画像が動きも落下もせずただ表示されるだけでした。
※失敗例なのでスキップして構いません。
Sprite2D +--- RigidBody2D +--- CollisionShape2D(shape=CircleShape2D)
下位の RigidBody2D ノードの物理関数を呼び出した際の処理は以下です。
# CoinSprite ノードを生成するために必要な coin_sprite シーンを読み込んでおきます。
sceneCoinSprite = preload("res://coin_sprite.tscn")
# シーンから子ノードを作成します。
var nodeCoinSprite = sceneCoinSprite.instantiate()
# 下位の RigidBody2D という名前のノードを取得して、それが持つ物理関数を呼び出します。
nodeCoinSpirte.get_node("RigidBody2D").apply_impulse(impulse, apply_point)
コインを表示するシーンを子ノードとして動的に作成
作成したコインを表示する otakara_coin シーンを GD スクリプトで読み込むことで、複製を子ノードとして配置できます。
# コイン画像を表示するシーンを子ノードとして生成する際に用います。
var scene_otakara_coin
# Called when the node enters the scene tree for the first time.
func _ready():
# otakara_coin ノードを生成するために必要な otakara_coin シーンを読み込んでおきます。
scene_otakara_coin = preload("res://otakara_coin.tscn")
読み込んだ PackedScene の instantiate 関数を呼び出すことで、そのシーンを子ノードとして作成できます。
# scene_otakara_coin シーンから、インスタンスを作成します。
var nodeOtakara:RigidBody2D = scene_otakara_coin.instantiate()
作成した後は、メンバ変数などを設定してから、シーンに追加します。
今回は実行中のシーンのルートノードの下位に配置します。
今回は、実行されるシーンのルートノードのスクリプトではなく、宝箱を表示するシーンのスクリプトでノードを作成して配置しています。
そのため、 get_root() でルートノードを得て、ルートノードの下位に作成したノードを配置しています。
そうしないと、このスクリプトを割り当てている宝箱のノードの下位に配置されてしまい、宝箱のノードの位置が基準になってしまいます。
node_otakara.position = position # 上位ノードの相対座標で設定します。そのため配置先はルートの下位にします。
get_tree().get_root().add_child(node_otakara) # シーンのルートノードの下位に作成した子ノードを追加します。
GD スクリプトから動的にシーンを子ノードとして作成・配置する手順については、以下の記事を参照してください。
作成したコインを物理関数で飛ばす
以下のスクリプトを用いて、シーンファイルから子ノードを動的に作成して一定範囲のランダムな向きと力で飛ばします。
前述したノードの作成とシーンへの配置をした後に、その RigidBody2D のノード自身に瞬間的に力を加える物理関数 apply_impulse を呼び出しています。
指定した範囲内で方向と力をランダムに決定して、そのベクトルを apply_impulse に与えることで、瞬間的に指定した方向に指定した勢いで RigidBody2D ノードを飛ばします。
# お宝を飛ばす際の勢いの下限値です。
var otakara_impulse_power_min = 500
# お宝を飛ばす際の勢いの上限値です。
var otakara_impulse_power_max = 1000
# お宝を飛ばす際の角度の下限値です。
var otakara_vector_degree_min = 95
# お宝を飛ばす際の角度の上限値です。
var otakara_vector_degree_max = 165
# お宝の子ノードを動的に作成して、それを飛ばします。
# position : お宝の子ノードの初期位置です。
func spawn_otakara(position:Vector2):
# coin_sprite シーンから、インスタンスを作成します。
var node_otakara = scene_otakara_coin.instantiate()
node_otakara.position = position # 上位ノードの相対座標で設定します。そのため配置先はルートの下位にします。
get_tree().get_root().add_child(node_otakara) # シーンのルートノードの下位に作成した子ノードを追加します。
# お宝が飛ぶ勢いをランダムに決定します。
var power = randf_range(otakara_impulse_power_min, otakara_impulse_power_max)
# お宝が飛ぶ方向をランダムに決定します。
var degree = randf_range(otakara_vector_degree_min, otakara_vector_degree_max)
# degree に応じた単位円のベクトルを作成します。180 度足して反対側に力を加えることで指定した方向に飛ばします。
var direction = Vector2()
direction.x = cos(deg_to_rad(degree))
direction.y = sin(deg_to_rad(degree)) * -1
# 物理関数 apply_impulse 渡す、加える力の値を、範囲内でランダムに決定した向きと力を掛け合わせて設定します。
var impulse = direction * power
# お宝の中心に方向をもった力を加えて飛ばします。
node_otakara.apply_impulse(impulse)#, apply_point)
return
物を飛ばす際の物理関数については以下の記事を参照してください。
宝箱をクリックしたときにコインを出す処理
宝箱をクリックした際の処理で、前述の SpawnOtakara 関数を呼び出します。
# 当たり判定の領域にマウスオーバーした際のシグナル input_event と接続した受信側メソッドです。
func _on_area_2d_input_event(viewport, event, shape_idx):
# マウス左ボタンを離した直後の場合
if Input.is_action_just_released("ClickOrTap"):
spawn_otakara(event.position)
return
ClickOrTap アクションについては「Godot4 クリック/タップしたイベントの検知と座標のログ出力」の「クリック / タップの入力アクションの作成」を参照してください。
テスト
F6 キーで現在開いているメインのシーンを実行して、宝箱をクリックすると SpawnOtakara 関数が呼び出して、コインのノードを作成して物理関数で飛ばすことが出来ました。
※この動画を撮影した際は、コインの Sprite2D の Scale は 1.0 の状態です。
まとめ
今回は、2D / 3D ゲームを作成できる無料・オープンソースの軽快なゲームエンジン「Godot Engine 4」で、コイン画像を表示・物理演算するシーンを作成して、そのシーンを別のシーンのスクリプトから動的に子ノードとして作成・配置して、物理関数で指定した方向・力で飛ばす手順を紹介しました。
参照サイト Thank You!
- Godot Engine – Free and open source 2D and 3D game engine
- 草コインのイラスト | かわいいフリー素材集 いらすとや
- Get Sprite Dimensions – Archive – Godot Forum
- PackedScene — Godot Engine (4.x)の日本語のドキュメント
- PackedScene — Godot Engine (4.x)の日本語のドキュメント#instantiate
- RigidBody2D — Godot Engine (4.x)の日本語のドキュメント#apply-impulse
記事一覧 → Compota-Soft-Press
コメント