はじめてのGodot 第5回: 隕石をランダムに降らせよう — タイマーとインスタンス化
前回作った隕石は、まだ1個しか存在しません。今回はこれを毎秒量産してランダムに降らせます。前回「別シーンにした」理由が、今日わかります。
今回のゴール

0.5秒ごとに、ランダムな位置から、ランダムな速さの隕石が降ってくる。
Timerノードを置く
「一定間隔で何かをする」にはTimerノードを使います。
main.tscnを開く(ファイルシステムからダブルクリック)Mainの子にTimerノードを追加し、名前をSpawnTimerに変更- インスペクタで設定:
- Wait Time:
0.5(0.5秒間隔) - Autostart: オン(ゲーム開始と同時に動き出す)
- Wait Time:
Timerは0.5秒ごとに timeout というシグナルを発します。前回はエディタでシグナルを接続しましたが、今回はコードから接続してみます。どちらも同じことができるので、両方知っておくと便利です。
メインシーンにスクリプトを書く
Main にスクリプトをアタッチします(main.gd)。
extends Node2D
const METEOR_SCENE = preload("res://meteor.tscn")
func _ready():
$SpawnTimer.timeout.connect(_on_spawn_timer_timeout)
func _on_spawn_timer_timeout():
var meteor = METEOR_SCENE.instantiate()
meteor.position = Vector2(randf_range(20, 460), -50)
meteor.speed = randf_range(200, 500)
add_child(meteor)
実行(F5 / MacはCmd+B)してみてください。隕石の雨が降ってきたら成功です。 まだ当たり判定はないので、すり抜けて大丈夫。
何をしたのか、1行ずつ
const METEOR_SCENE = preload("res://meteor.tscn")
前回作った隕石シーンのファイルを読み込んで、METEOR_SCENE という名前を付けています。const は var の仲間ですが「後から変わらない入れ物」です。res:// はプロジェクトフォルダを指すGodot独自の書き方です。
func _ready(): と $SpawnTimer.timeout.connect(...)
_ready() も _process() と同じ特別な関数で、ノードが登場した瞬間に1回だけ呼ばれます。初期設定を書く場所です。
$SpawnTimer は「子ノードのSpawnTimerを取ってくる」書き方。その timeout シグナルに _on_spawn_timer_timeout 関数を接続(connect)しています。「タイマーが鳴るたびに、この関数を呼んでね」という予約です。前回エディタでダブルクリックしてやったことと、まったく同じことをコードでやっています。
instantiate() と add_child() — 部品の量産
var meteor = METEOR_SCENE.instantiate()
instantiate() は、シーンから実体(インスタンス)をひとつ作る命令です。設計図からコピーを1個作るイメージです。
ただし、作っただけではまだゲーム内に存在しません。最後の add_child(meteor) でシーンツリーに追加して、初めて画面に現れて動き出します。「作る」と「置く」は別の操作 — ここが今回いちばんのつまずきどころです。
randf_range(最小, 最大) — 乱数
randf_range(20, 460) は20〜460のランダムな数を返します。x座標に使えば出現位置が、speed に使えば落下速度がバラつきます。y座標の -50 は画面の上端より外です(yマイナス=上、でしたね)。画面外から生まれて、降りてくる演出になります。
meteor.speed と書けるのは、前回 meteor.gd に var speed = 300.0 を定義したからです。外から変数を上書きして、個体差を付けています。
数字で遊ぼう
ゲームの難易度はこの数字たちで決まります。変えて実行してみてください。
- Wait Time
0.5→0.1: 弾幕地獄 - speed
randf_range(200, 500)→(600, 1000): 高速隕石 - x座標の範囲を狭める: 安全地帯ができてしまう(バグっぽい挙動も体験)
つまずきポイント
Node not found: "SpawnTimer": Timerノードの名前をSpawnTimerに変え忘れていませんか? 追加したばかりだと名前がTimerのままで、コードの$SpawnTimerが見つけられずこのエラーになります。シーンドックでノード名をダブルクリックしてSpawnTimerに直してください- 何も降ってこない(その1): SpawnTimerの Autostart がオフのままではないですか? インスペクタで確認してください
- 何も降ってこない(その2):
add_child(meteor)を書き忘れると、隕石は「作られたのに置かれていない」状態になります。エラーも出ないので気づきにくい、定番のミスです Invalid assignment of property or key 'speed':meteor.gdの変数名と一致しているか確認してください(前回speedと定義しました)
今回学んだこと
Timerノード +timeoutシグナルで「一定間隔の処理」を作る- シグナルはエディタでもコード(
connect)でも接続できる _ready()はノード登場時に1回だけ呼ばれる初期設定の場所instantiate()で部品の実体を作り、add_child()で置く。2つで1セットrandf_range()の乱数でゲームに変化を付ける
次回予告
隕石は降ってきますが、今はすり抜けるだけ。第6回でいよいよ当たり判定を実装し、「ゲームオーバー」を作ります。if 文の初登場回です。