8.Mainのスクリプトの作成
スクリプトをアタッチ
「Main」のスクリプトを書いていきましょう。
「シーン」ドックの「Main」を右クリックして「スクリプトをアタッチ」を選択します。
「ノードにスクリプトをアタッチする」ダイアログが開きます。「テンプレート」の横のチェックボックスをチェックします。前回チェックしていた場合は、チェックした状態になっています。
パスがres://main.gdになっていますので、変更せずに「作成」ボタンを押します
先頭行がextends Nodeで、_readyと_process関数が用意された状態でmain.gdが作成されます。また、Scriptビューが開きます。「Ctrl+S」で変更を保存します。
メンバー変数の作成
スクリプトのextends Nodeの下に変数を追加します。
まずは、外部に出すメンバー変数mob_sceneを追加します。「インスペクター」ドックを利用して、インスタンス化する「Mob」シーンを設定可能にします。また、得点を表すメンバー変数scoreを追加します。
extends Node @export var mob_scene: PackedScene var score
スクリプトを入力したら「Ctrl+S」で保存します。この時点で「インスペクター」に、「main.gd」>「Mob Scene」という項目が「<空>」の状態で追加されます。
この「<空>」の部分にmob.tscnをドロップするか、「<空>」をクリックして「読み込む」を選び、mob.tscnを選択します。すると、インスタンス化する「Mob」シーンがスクリプト内から利用可能になります。
「Mob」シーンを追加したら「Ctrl+S」で変更を保存します。
Playerとのシグナル接続
「シーン」ドックで「Player」を選択して、右サイドバーの「ノード」ドックを開きます。「シグナル」タブを選択して、「player.gd」の「hit()」を接続します。ダブルクリックするか、右クリックして「接続」を選びます。
「メソッドにシグナルを接続」ダイアログが開きます。ノードは「Main」が選択されています。また、デフォルトでは「受信側メソッド」が「_on_player_hit」になっています。
この「_on_player_hit」を「game_over」に変更して「接続」ボタンを押します。
main.gdの中にgame_over関数が追加されます。
このgame_over関数にはシグナルが接続されている緑色のアイコンが表示されます。また、「ノード」ドック>「シグナル」>「player.gd」>「hit()」に、緑色のアイコンでgame_over関数が接続されていることが示されます。「Ctrl+S」で変更を保存します。
サンプルの写経は、ほぼこのシグナルの操作を身に付けると言っても過言ではありません。
game_overとnew_gameの実装
先ほど作ったgame_over関数の処理を書きます。また、new_game関数を作って処理を書きます。
# ゲーム終了 func game_over() -> void: $ScoreTimer.stop() $MobTimer.stop() # 新ゲーム開始 func new_game() -> void: score = 0 $Player.start($StartPosition.position) $StartTimer.start()
game_over関数では、「ScoreTimer」ノードと「MobTimer」ノードの進行を停止します。
new_game関数では、まず得点を0にします。そして「StartPosition」ノードの位置で、「Player」インスタンスを開始します。最後に「StartTimer」ノードを開始します。
各種タイマーとのシグナル接続
次は「MobTimer」「ScoreTimer」「StartTimer」という3つのタイマーで、シグナルを接続します。それぞれのタイマーの「timeout()」シグナルを、「Main」スクリプトに接続していきます。
「シーン」ドックに並んでいる順番に接続をおこなっていきます。まずは「MobTimer」を選択して、右のサイドバーの「ノード」ドックの「シグナル」の「timeout()」を右クリックします。そして「接続」を選択します。
「メソッドにシグナルを接続」ダイアログが開きます。ノードは「Main」が選択されています。また、デフォルトでは「受信側メソッド」が「_on_mob_timer_timeout」になっています。そのまま「接続」ボタンを押します。
同じように「ScoreTimer」「StartTimer」も接続していきます。そして「Ctrl+S」で変更を保存します。
タイマーの処理を実装
タイマーからシグナルを受けた際の処理を実装します。
まずは処理が短い_on_score_timer_timeoutと_on_start_timer_timeoutです。
func _on_score_timer_timeout() -> void: score += 1 func _on_start_timer_timeout() -> void: $MobTimer.start() $ScoreTimer.start()
_on_score_timer_timeout関数では、スコアータイマーのtimeoutシグナルが発行されるたびに、scoreの値を1ずつ大きくします。
_on_start_timer_timeout関数は、ゲーム開始の待機時間が終了するとtimeoutシグナルが発行されます。ここでは、MobTimerとScoreTimerを開始します。
次は_on_mob_timer_timeoutです。こちらは少し処理が長いです。ただ、おこなっていることはそれほど難しくはありません。
func _on_mob_timer_timeout() -> void: # Mobシーンのインスタンスを作る var mob = mob_scene.instantiate() # MobSpawnLocationのランダムな進行比率からMobの登場場所を決める var mob_spawn_location = $MobPath/MobSpawnLocation mob_spawn_location.progress_ratio = randf() # MobSpawnLocationを利用して、Mobの登場場所を設定 mob.position = mob_spawn_location.position # MobSpawnLocationのパスの向きに90度足して方向とする var direction = mob_spawn_location.rotation + PI / 2 # 向きをランダムに左右45度変えて、Mobに設定する direction += randf_range(-PI / 4, PI / 4) mob.rotation = direction # 速度のベクトルをランダムに設定する var velocity = Vector2(randf_range(150.0, 250.0), 0.0) mob.linear_velocity = velocity.rotated(direction) # 子ノードとして、Mobのインスタンスを追加する add_child(mob)
おこなっていることを箇条書きにします。
- Mobシーンのインスタンスを作る。
- パスのどこかの地点を、発生場所にする。
- 画面の内側を向かせる。向きについては若干ランダムを入れる。
- ランダムに速度を決める。
- Mainの子ノードとして、Mobのインスタンスを追加する。
角度について少し補足です。Godot Engineでは、角度の計算はラジアンを使います。円周率PIは180度に相当します。角度とラジアンの変換はdeg_to_rad関数、rad_to_deg関数でおこなえます。
randfやrandf_range、deg_to_radやrad_to_degなどの関数は、グローバルスコープの関数です。グローバルスコープには多くの関数が用意されています。
実行確認
ここまでの内容を実行してみましょう。_ready関数からnew_game関数を呼び出すようにします。
func _ready() -> void: new_game()
そして、画面右上の▶マークの「プロジェクトを実行」ボタンを押します。
「確認」ダイアログが出るので「現在のものを選択」ボタンを押します。
ゲーム画面が表示されて、「Player」の表示と、「Mob」の生成が成功しているのが分かります。
もし、誤って他のシーンをメインシーンにしてしまっている場合は、「ファイルシステム」ドックのmain.tscnを右クリックして、「メインシーンとして設定」を選択します。
ここまで確認できたら、new_game()をコメントアウトしてpassを追加します。実際のゲームではいきなり開始せずに、別の手段で開始させるからです。
func _ready() -> void: #new_game() pass
ここまでのスクリプト
ここまでのスクリプトを掲載します。
extends Node
@export var mob_scene: PackedScene
var score
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
#new_game()
pass
# Called every frame. 'delta' is the elapsed time since the previous frame.
@warning_ignore("unused_parameter")
func _process(delta: float) -> void:
pass
# ゲーム終了
func game_over() -> void:
$ScoreTimer.stop()
$MobTimer.stop()
# 新ゲーム開始
func new_game() -> void:
score = 0
$Player.start($StartPosition.position)
$StartTimer.start()
func _on_mob_timer_timeout() -> void:
# Mobシーンのインスタンスを作る
var mob = mob_scene.instantiate()
# MobSpawnLocationのランダムな進行比率からMobの登場場所を決める
var mob_spawn_location = $MobPath/MobSpawnLocation
mob_spawn_location.progress_ratio = randf()
# MobSpawnLocationを利用して、Mobの登場場所を設定
mob.position = mob_spawn_location.position
# MobSpawnLocationのパスの向きに90度足して方向とする
var direction = mob_spawn_location.rotation + PI / 2
# 向きをランダムに左右45度変えて、Mobに設定する
direction += randf_range(-PI / 4, PI / 4)
mob.rotation = direction
# 速度のベクトルをランダムに設定する
var velocity = Vector2(randf_range(150.0, 250.0), 0.0)
mob.linear_velocity = velocity.rotated(direction)
# 子ノードとして、Mobのインスタンスを追加する
add_child(mob)
func _on_score_timer_timeout() -> void:
score += 1
func _on_start_timer_timeout() -> void:
$MobTimer.start()
$ScoreTimer.start()
メイン部分ができましたので、次はゲーム周辺のUIを作っていきます。得点表示やタイトル画面などを作っていきましょう。
