メインシーン

今まで作成したすべてをまとめ、遊べるゲームにするときが来ました。

新しいシーンを作成し、 Node クラスを追加して Main という名前にします。 (Node2D ではなく、Nodeを使用する理由は、これはゲームロジックを扱うノードであり、これ自体に2D機能が不要であるためです)

インスタンス ボタン(チェーンリンクアイコン)をクリックし、保存した player.tscn を選択してください。

../../_images/instance_scene.webp

次に Main の子ノードとして以下のノードを追加し、名前を変更します:

  • Timer( MobTimer という名前) - モブが出現する頻度を制御する

  • Timer ( ScoreTimer という名前) - 一秒ごとに得点を上げる

  • Timer ( StartTimer という名前) - 開始する前に遅延させる

  • Marker2D (StartPosition という名前) - プレイヤーの開始位置を示す

Timer ノードの Wait Time プロパティを次のように設定します (値は秒単位です):

  • MobTimer: 0.5

  • ScoreTimer: 1

  • StartTimer: 2

さらに、 StartTimerOne Shot プロパティを「On」に設定し、 StartPosition ノードの Position(240, 450) に設定します。

モブの生成

The Main node will be spawning new mobs, and we want them to appear at a random location on the edge of the screen. Click the Main node in the Scene dock, then add a child Path2D node named MobPath. When you select Path2D, you will see some new buttons at the top of the editor:

../../_images/path2d_buttons.webp

中央のアイコン([点を空きスペースに追加])を選択し、表示されているコーナーをクリックしてポイントを追加してパスを描画します。ポイントをグリッドにスナップするには、[グリッドスナップを使う]が選択されていることを確認します。このオプションは、[選択Nodeをロック]ボタンの左側にあり、「交差する線と磁石」のアイコンで表示されています。

../../_images/grid_snap_button.webp

重要

時計回りにパスを描画します。そうしないと、モブは内側ではなく外側を向いて発生します!

../../_images/draw_path2d.gif

画像にポイント 4 を配置した後、[曲線を閉じる] ボタンをクリックすると、パスが完成します。

パスが定義されたので、 MobPath の子としてPathFollow2Dノードを追加し、 MobSpawnLocation という名前を付けます。このノードは自動的に回転し、パスの移動に従うので、パスに沿ってランダムな位置と方向を選択できます。

シーンは次のようになります:

../../_images/main_scene_nodes.webp

Mainスクリプト

Main にスクリプトをアタッチします。 スクリプトの上部に @export var mob_scene: PackedScene を追加して、インスタンス化するMobシーンを選択できるようにします。

extends Node

@export var mob_scene: PackedScene
var score

Main ノードをクリックすると、インスペクタの "Main.gd" の下に Mob Scene プロパティが見えるようになっています。

このプロパティの値は、ふたつの方法で指定できます:

  • 「ファイルシステム」パネルから mob.tscn をドラッグし、 Mob Scene プロパティにドロップします。

  • <空> の隣にある下矢印をクリックして「読み込み」を選び、mob.tscn を選択します。

次に、シーンドックの Player ノードを選択し、サイドバーのノードドックにアクセスします。ノードドックでは、シグナルタブが選択されていることを確認してください。

Player ノードのシグナルのリストが表示されます。リストの中から hit のシグナルを見つけてダブルクリックしてください (または、右クリックして "接続..." を選択)。これでシグナルの接続ダイアログが開きます。ゲームが終了したときに必要な処理を行う game_over という名前の新しい関数をこれから作ります。シグナル接続ダイアログの下部にある「受信側メソッド」ボックスに「game_over」と入力し、「接続」をクリックしてください。 Player から hit シグナルが発火されたとき、 Main スクリプト側で処理できるようになります。新しい関数に以下のコードを追加し、新しいゲームのセットアップを行う new_game 関数も追加します:

func game_over():
    $ScoreTimer.stop()
    $MobTimer.stop()

func new_game():
    score = 0
    $Player.start($StartPosition.position)
    $StartTimer.start()

Now we'll connect the timeout() signal of each Timer node (StartTimer, ScoreTimer, and MobTimer) to the main script. For each of the three timers, select the timer in the Scene dock, open the Signals tab of the Node dock, then double-click the timeout() signal in the list. This will open a new signal connection dialog. The default settings in this dialog should be fine, so select Connect to create a new signal connection.

Once all three timers have this set up, you should be able to see each timer have a Signal connection for their respective timeout() signal, showing in green, within their respective Signals tabs:

  • (For MobTimer): _on_mob_timer_timeout()

  • (For ScoreTimer): _on_score_timer_timeout()

  • (For StartTimer): _on_start_timer_timeout()

Now we define how each of these timers operate by adding the code below. Notice that StartTimer will start the other two timers, and that ScoreTimer will increment the score by 1.

func _on_score_timer_timeout():
    score += 1

func _on_start_timer_timeout():
    $MobTimer.start()
    $ScoreTimer.start()

_on_mob_timer_timeout() では、モブのインスタンスを作成し、 Path2D に沿ってランダムに開始位置を選び、モブを動かすようにします。PathFollow2D ノードはパスに沿って自動的に回転するので、これを使ってモブの方向と位置を選択します。移動速度は 150.0 から 250.0 の間でランダムに値を選びます(すべて同じ速度で動くとつまらなくなってしまいます)。

注意点として、新しいインスタンスは add_child() を使ってシーンに追加しなければなりません。

func _on_mob_timer_timeout():
    # Create a new instance of the Mob scene.
    var mob = mob_scene.instantiate()

    # Choose a random location on Path2D.
    var mob_spawn_location = $MobPath/MobSpawnLocation
    mob_spawn_location.progress_ratio = randf()

    # Set the mob's position to the random location.
    mob.position = mob_spawn_location.position

    # Set the mob's direction perpendicular to the path direction.
    var direction = mob_spawn_location.rotation + PI / 2

    # Add some randomness to the direction.
    direction += randf_range(-PI / 4, PI / 4)
    mob.rotation = direction

    # Choose the velocity for the mob.
    var velocity = Vector2(randf_range(150.0, 250.0), 0.0)
    mob.linear_velocity = velocity.rotated(direction)

    # Spawn the mob by adding it to the Main scene.
    add_child(mob)

重要

なぜ PI なのでしょうか?角度を必要とする関数では、Godot は度数ではなく、 ラジアン を使用します。円周率はラジアンの半回転を表し、約 3.1415 です ( 2 * PI に等しい TAU もあります) 。もし、度数を扱う方が好きであれば deg_to_rad()rad_to_deg() という関数を使って両者を変換する必要があります。

シーンのテスト

シーンをテストして、すべてが動作していることを確認してみましょう。new_game 関数の呼び出しを _ready()に追加してください:

func _ready():
    new_game()

また、 Main を「メインシーン」として設定してみましょう。 (ゲームが起動したときに自動的に実行されるシーンです。) 「プロジェクトを実行」ボタンを押して、プロンプトが表示されたら main.tscn を選択してください。

Tip

すでに他のシーンをメインシーンに設定していた場合は、ファイルシステムドックの main.tscn を右クリックして「メインシーンとして設定」を選択してください。

プレイヤーを移動でき、モブが発生したり、モブに当たった時にプレイヤーが消えるようになっているはずです。

全て動作していることが確認できたら、 _ready() から new_game() の呼び出しを削除して、 pass に置き換えてください。

私たちのゲームには何が足りないのでしょう?それは、ユーザーインターフェイスです。次のレッスンでは、タイトル画面を追加して、プレイヤーのスコアを表示することにします。