実践ガイド

Godotで放置ゲームを作る④:Timerで発電パネルを自動生産させる

※本記事にはアフィリエイト広告(Udemy)を含みます。

前回のおさらい

前回③では、Signalを使って「発電する」ボタンとGameManagerをつなぎ、クリックするたびに電力が増える仕組みを作りました。ただ、これだけだと放置ゲームとしては物足りません。今回は、クリックしなくても電力が自動で増えていく仕組みを追加します。

今回のゴール

今回作るのは「発電パネル」という設備です。電力を使って発電パネルを購入すると、ゲーム実行中は、おおむね1秒ごとに電力が増えていきます。終了中も生産するには保存時刻との差分などを使う処理が別途必要です。

STATION OPSでは今回はじめて「購入」という概念が登場します。何かを購入すると、その分だけ次回の購入コストが上がっていく、という放置ゲームによくある仕組みも、あわせて実装します。

GameManagerに発電パネルの状態を追加する

GameManager.gdに、以下の変数を追加します。

var power_panel_count: int = 0
var power_panel_cost: float = 10.0

const POWER_PANEL_COST_RATE: float = 1.15
const POWER_PANEL_PRODUCTION: float = 1.0
  • power_panel_count:現在所持している発電パネルの数
  • power_panel_cost:次に1基購入するときのコスト
  • POWER_PANEL_COST_RATE:購入するたびにコストが何倍になるか
  • POWER_PANEL_PRODUCTION:発電パネル1基あたりの毎秒の生産量

なぜコストが購入するたびに上がっていくのか

購入コストを増やすことで、次の購入までの間隔や資源配分を調整できます。購入するたびにコストを1.15倍にすることで、「次の1基を買うために、また電力を貯める」という遊びのリズムが生まれます。この数値は今回10→11.5→13.2…のように上昇していきますが、ちょうどよい上昇率は実際に遊びながら調整していく部分です。

発電パネルを購入する処理を書く

func buy_power_panel() -> bool:
    if power < power_panel_cost:
        return false
    power -= power_panel_cost
    power_panel_count += 1
    power_panel_cost *= POWER_PANEL_COST_RATE
    resources_changed.emit()
    return true

if power < power_panel_cost: return falseの行で、電力が足りない場合は何もせずに処理を終えるようにしています。こうすることで、電力がマイナスになってしまう不具合を防いでいます。

Timerで自動生産を実装する

Timerノードとは

Timerとは、指定した時間が経過するたびにtimeoutというSignalを発信してくれるノードです。「1秒ごとに何かをする」といった、時間を基準にした繰り返し処理を作りたいときに使います。

シーンに置かず、コードからTimerを生成する

今回のTimerはGameManager.gd内で生成します。第1回で作った_ready()に初期化処理を追加してください。下の例を別の_ready()として重複させないでください。

var _production_timer: Timer

func _ready() -> void:
    _production_timer = Timer.new()
    _production_timer.wait_time = 1.0
    _production_timer.autostart = true
    _production_timer.timeout.connect(_on_production_tick)
    add_child(_production_timer)

今回はGameManagerのスクリプトを直接オートロードしているため、シーンエディタで直接ノードを配置できません。そのため、_ready()の中でTimer.new()を呼び出してノードを作り、add_child()で自分の子として追加しています。wait_time = 1.0が「1秒ごと」、autostart = trueが「起動と同時に自動でカウントを始める」設定です。

毎秒、発電パネルの数だけ電力を増やす

func _on_production_tick() -> void:
    if power_panel_count > 0:
        power += power_panel_count * POWER_PANEL_PRODUCTION
        resources_changed.emit()

TimerのtimeoutSignalが発信されるたびにこの関数が呼ばれ、所持している発電パネルの数だけ電力を加算します。

購入ボタンをUIに追加する

①②で用意した「UpgradeList」の中に、発電パネルの購入行を追加します。

  1. UpgradeListの子として「HBoxContainer」を追加し、名前を「PowerPanelRow」にする
  2. その子として「Label」を追加して「PowerPanelInfo」と名付け、所持数とコストを表示する
  3. 同じく子として「Button」を追加して「PowerPanelBuyButton」と名付け、テキストを「購入」にする

Main.gd側では、前回の_ready()に接続処理を加え、既存の_update_display()にパネル表示を統合します。関数を同名で重複定義しないでください。

@onready var power_panel_info: Label = $Margin/VBoxContainer/UpgradeList/PowerPanelRow/PowerPanelInfo
@onready var power_panel_buy_button: Button = $Margin/VBoxContainer/UpgradeList/PowerPanelRow/PowerPanelBuyButton

func _ready() -> void:
    click_button.pressed.connect(_on_click_button_pressed)
    GameManager.resources_changed.connect(_on_resources_changed)
    power_panel_buy_button.pressed.connect(_on_power_panel_buy_pressed)
    _update_display()

func _on_power_panel_buy_pressed() -> void:
    GameManager.buy_power_panel()

func _update_display() -> void:
    power_label.text = "電力: %.2f" % GameManager.power
    power_panel_info.text = "発電パネル (%d基) - 次: %.2f" % [
        GameManager.power_panel_count,
        GameManager.power_panel_cost
    ]
    power_panel_buy_button.disabled = GameManager.power < GameManager.power_panel_cost

表示は小数第2位で丸めていますが、購入可否は丸め前の値で判定します。境界では表示が同じでも購入できない場合があるため、必要に応じて数値の丸め方と判定方法をそろえてください。最後の行のdisabled = GameManager.power < GameManager.power_panel_costが、電力が足りないときにボタンを押せなくする処理です。ボタンが押せない状態になっていれば、プレイヤーは一目で「まだ買えない」と分かります。

動作確認

「発電する」ボタンでコスト分の電力を貯め、発電パネルを1基購入してみてください。購入後は、ボタンを押さなくても1秒ごとに電力が増えていけば成功です。

次回予告

次回⑤では、酸素・部品という新しい資源を追加し、電力→酸素→部品という連鎖構造を作ります。

Godot学習におすすめのオンライン講座

Timerとオートロードを組み合わせた設計は、放置ゲームに限らずいろいろな場面で応用できます。基礎から体系的に学びたい方はこちらもどうぞ。

Godotのコースを見る

参考リンク


← 記事一覧・検索