結論: レベルアップした瞬間に AttackTimer の待ち時間を短くすると、プレイヤーの成長を実際の攻撃へ反映できます。今回は攻撃間隔を 0.8 秒から 0.6 秒へ短くし、画面にも現在値を表示します。
このシリーズでは、Brotatoのようなアリーナ型ゲームで使われる仕組みを、Godotで一つずつ小さく再現していきます。前回は敵を倒して経験値を獲得し、3経験値でレベルアップするところまで作りました。今回は、レベルアップを見た目だけで終わらせず、攻撃の速さへつなげます。
- 対象:前回記事の経験値とレベルアップを追加したプロジェクトを動かせる人
- 使用バージョン:Godot 4.7 系(4.7.x)
- 完成状態:レベル2になると攻撃間隔が
0.8秒から0.6秒になる
1. レベルアップの結果をAttackTimerへ反映する
前回のレベルアップは、level の数値とHUDの表示が変わるだけでした。ゲーム中の変化として感じられるように、レベルアップ時に AttackTimer.wait_time を更新します。なお、wait_timeを変更しても進行中の残り時間は変わらず、短い攻撃間隔が適用されるのは次のタイムアウト後です。残り時間もすぐリセットする場合はstart()を呼びます。Godot公式Timer資料。
| 状態 | 攻撃間隔 |
|---|---|
| 開始時 | 0.8秒 |
| レベル2 | 0.6秒 |
| レベル3 | 0.4秒 |
攻撃の生成処理はそのままにして、Timerの設定だけを変えます。この分け方なら、弾の種類や敵の検索処理を変更せずに、攻撃速度の強化を追加できます。
2. 強化値をPlayer側で管理する
player.gd に、開始時の攻撃間隔、1回の強化量、下限値を追加します。
const START_ATTACK_INTERVAL := 0.8
const ATTACK_INTERVAL_UPGRADE := 0.2
const MIN_ATTACK_INTERVAL := 0.2
var attack_interval := START_ATTACK_INTERVAL
attack_interval はプレイヤーの現在の攻撃間隔です。ATTACK_INTERVAL_UPGRADE を0.2秒にして、レベルアップのたびに待ち時間を0.2秒ずつ短くします。
下限を 0.2 秒にしているのは、レベルを上げ続けたときに待ち時間が0以下にならないようにするためです。Timerの待ち時間を極端に短くすると、弾が増えすぎて動作確認が難しくなります。
3. レベルアップ時にAttackTimerを書き換える
経験値の処理は前回と同じです。レベルが上がった直後に、強化処理を呼び出します。
func gain_experience(amount: int) -> void:
if amount <= 0:
return
experience += amount
while experience >= EXPERIENCE_TO_LEVEL_UP:
experience -= EXPERIENCE_TO_LEVEL_UP
level += 1
_apply_level_up_upgrade()
stats_changed.emit(level, experience, EXPERIENCE_TO_LEVEL_UP, attack_interval)
強化処理は別の関数に分けます。
func _apply_level_up_upgrade() -> void:
attack_interval = maxf(MIN_ATTACK_INTERVAL, attack_interval - ATTACK_INTERVAL_UPGRADE)
$AttackTimer.wait_time = attack_interval
maxf() は、2つの浮動小数点数のうち大きい方を返します。現在値から0.2秒を引いた結果が下限を下回った場合でも、0.2 秒を維持できます。
4. HUDにも攻撃間隔を表示する
レベルと経験値だけでなく、現在の攻撃間隔もシグナルで通知します。
signal stats_changed(level: int, experience: int, experience_to_next: int, attack_interval: float)
main.gd では、シグナルの引数を4つ受け取ります。
func _on_player_stats_changed(level: int, experience: int, experience_to_next: int, attack_interval: float) -> void:
status_label.text = "Level %d XP %d/%d Attack %.1fs" % [level, experience, experience_to_next, attack_interval]
シグナルの宣言、connect()、受け取り側の関数で引数の数が違うと、起動時にエラーになります。引数を増やしたら、3か所をまとめて確認してください。
5. 実行して強化の効果を見る
F5で実行し、画面左上の表示を確認します。
- 開始時に
Level 1 XP 0/3 Attack 0.8sと表示される - 敵を1体倒すと経験値が1増える
- 3体目を倒すと
Level 2 XP 0/3 Attack 0.6sになる - その後に生成される弾の間隔が短くなる
見た目だけでは攻撃間隔の変化を判断しにくい場合があります。まずHUDの数値が 0.8s から 0.6s に変わることを確認し、そのあと弾の生成間隔を見てください。
6. うまくいかないときの確認ポイント
- レベルは上がるが攻撃が速くならない:
$AttackTimer.wait_time = attack_intervalが強化関数に入っているか確認します。 - 攻撃間隔が毎回0.6秒になる:開始時の値ではなく、現在値
attack_intervalからATTACK_INTERVAL_UPGRADEを引いているか確認します。 - 攻撃間隔が0秒になる:
MIN_ATTACK_INTERVALとmaxf()の処理が残っているか確認します。 - 起動時にシグナルエラーが出る:
stats_changedの宣言、connect()、_on_player_stats_changed()の引数4つが一致しているか確認します。 - HUDの数値だけ変わらない:
CanvasLayer/HUD/StatusLabelのノード名と、main.gdのパスが一致しているか確認します。 - 矢印キーを押しても動いていないように見える:
Camera2Dがプレイヤーを画面中央に追従させています。ワールド軸や敵の相対位置を見てください。
7. 付属プロジェクトの検証結果
検証時の auto_attack_upgrade_verified.zip は、前回の経験値対応プロジェクトをコピーして作成しました。元のプロジェクトは変更していません。段階版ZIPは現在この記事からダウンロードできません。
Godot 4.7 stable で次を確認しています。
- プロジェクトとメインシーンを読み込める
- メインシーンを4秒間起動できる
- 開始時の
AttackTimer.wait_timeが0.8秒になる - 敵を倒すと経験値が1増える
- 3経験値でレベル2になり、攻撃間隔が0.6秒になる
- HUDが
Level 2 XP 0/3 Attack 0.6sに更新される
これはコードとヘッドレス実行による確認です。画面の色、敵の見え方、エディタのメニュー名は、利用しているGodotの表示設定によって異なる場合があります。
8. まとめと次の一歩
- レベルアップ時に
AttackTimer.wait_timeを短くし、成長を攻撃速度へ反映しました。 - 強化値をPlayer側で管理すると、攻撃処理を大きく変更せずに拡張できます。
- 下限値を設けると、レベルを上げ続けてもTimerが極端な値になりません。
- 次は、攻撃速度だけでなく、弾の数やダメージを選択肢から強化する仕組みへ進められます。
統合版プロジェクトとの対応
この記事で扱う機能は、企画全体をまとめたauto_attack_final_verified.zipにも組み込んでいます。この記事のコードは段階的に追加するための説明なので、途中の記事と統合版全体のファイル構成は一致しない場合があります。
統合版の必要経験値は10固定で、レベルアップ時は強化を選択します。攻撃速度を選ぶと間隔が変化します。この記事の3経験値と自動強化は途中段階の例で、stats_changedの引数も統合版では辞書1つです。統合版には後続記事の機能も含まれます。記事の機能を確認するときは該当する操作を先に試し、最後にGodot 4.7でF5起動、入力、セーブ・ロード、再起動後の復元を確認してください。
