結論: 同じビルド分類の強化を3回選ぶたびに、分類専用のボーナスを1段階付与します。攻撃型なら弾ダメージ、回収型なら吸引範囲のように、選択履歴が能力値へ反映される仕組みです。
このシリーズでは、Brotatoのようなアリーナ型ゲームの仕組みを、Godotで一つずつ小さく再現しています。前回は、選んだビルド分類に応じて強化候補の重みを変え、育てている方向の候補が少し出やすくなるようにしました。
分類を表示し、抽選の重みも変えられるようになると、次は「同じ分類を選び続けた結果」を能力値へ返したくなります。今回は、攻撃・機動・回収・生存の各分類に専用ボーナスを設定します。
ボーナスは毎回の選択で付与しません。3回、6回、9回という区切りで段階的に与えます。これなら、1回のレベルアップで大きく強くなりすぎず、ビルドを選び続けた手応えも作れます。
- 対象:前回記事のビルド分類と重み付き抽選まで追加したプロジェクト
- 使用バージョン:Godot 4.7 系(4.7.x)
- 完成状態:同じ分類を3回選ぶたびに、分類専用の能力ボーナスが1回だけ付与される
1. まず分類ごとのボーナスを決める
今回の分類とボーナスを次のように設定します。
| 分類 | 3回選んだときのボーナス | 効果 |
|---|---|---|
| 攻撃 | 弾ダメージ +1 | 敵を倒しやすくする |
| 機動 | 移動速度 +25 | 敵の攻撃から離れやすくする |
| 回収 | 吸引範囲 +40 | 経験値ジェムを拾いやすくする |
| 生存 | 回復量 +1 | 回復アイテムの効果を上げる |
回収と生存のボーナスには、前回までに設定した上限を適用します。吸引範囲は300、回復量は5を超えないようにします。
同じ分類を6回選ぶと、2段階目のボーナスが発生します。攻撃なら弾ダメージがさらに1増え、回収なら吸引範囲が上限300に達するまで最大40増えます。今回の判定基準は、分類の選択回数とボーナス段階の比較。
2. ボーナス発生間隔を定数にする
main.gd に、何回選ぶとボーナスを付与するかを定義します。
const BUILD_BONUS_INTERVAL := 3
前回記事で追加した build_counts は、分類ごとの選択回数を保存しています。
var build_counts := {
"attack": 0,
"mobility": 0,
"collection": 0,
"survival": 0,
}
選択回数を3で割った整数部分が、現在のボーナス段階になります。攻撃を0〜2回選んだ間は0段階、3〜5回なら1段階、6〜8回なら2段階です。
間隔を定数にしておけば、あとで「5回ごと」へ変更するときも、判定式の修正は1か所で済みます。
3. 選択回数が段階をまたいだときだけボーナスを付与する
前回記事の _record_build() は、分類カウントを増やしてHUDを更新していました。今回は、増加前と増加後の段階を比較します。
func _record_build(upgrade: Dictionary) -> void:
var build_id: String = upgrade.get("build", "")
if not build_counts.has(build_id):
return
var previous_count := int(build_counts[build_id])
build_counts[build_id] = previous_count + 1
var previous_tier := floori(
float(previous_count) / BUILD_BONUS_INTERVAL
)
var current_tier := floori(
float(build_counts[build_id]) / BUILD_BONUS_INTERVAL
)
if current_tier > previous_tier:
player.apply_build_bonus(build_id)
_update_build_label()
攻撃を2回選んだ状態で、もう一度攻撃を選ぶと、previous_tier は0、current_tier は1です。この差を検出して、ボーナスを1回だけ付与します。
攻撃を4回目に選んだときは、段階が1のままなのでボーナスは発生しません。6回目で段階が2へ変わり、2段階目のボーナスが1回発生します。
1回の選択でカウントが1しか増えない現在の設計なら、current_tier > previous_tier の判定だけで足ります。将来、まとめて経験値を変換する処理を追加する場合は、段階差の回数だけボーナスを繰り返す処理へ拡張します。
4. Player側へ分類ボーナスの適用処理を追加する
能力値を実際に変更する処理は、player.gd の apply_build_bonus() にまとめます。main.gd は分類の段階を判定し、Playerは具体的なステータスを変更する役割です。
const BUILD_ATTACK_DAMAGE_BONUS := 1
const BUILD_MOBILITY_SPEED_BONUS := 25.0
const BUILD_COLLECTION_RANGE_BONUS := 40.0
const BUILD_SURVIVAL_HEAL_BONUS := 1
func apply_build_bonus(build_id: String) -> void:
match build_id:
"attack":
bullet_damage += BUILD_ATTACK_DAMAGE_BONUS
"mobility":
move_speed += BUILD_MOBILITY_SPEED_BONUS
"collection":
magnet_range = minf(
MAX_MAGNET_RANGE,
magnet_range + BUILD_COLLECTION_RANGE_BONUS
)
"survival":
heal_amount = mini(
MAX_HEAL_AMOUNT,
heal_amount + BUILD_SURVIVAL_HEAL_BONUS
)
ここで使っている bullet_damage、move_speed、magnet_range、heal_amount は、前回までにPlayerへ追加した現在値です。プロジェクト内の変数名を変えている場合は、実際の名前へ読み替えます。
回収と生存には minf() と mini() を使い、通常の強化と同じ上限を守ります。ボーナスだけ上限を無視すると、通常のレベルアップ候補と同じ能力なのに、ビルド経由のときだけ値が増え続ける状態になります。
5. 通常の強化とビルドボーナスを分ける
apply_upgrade() は、選択した1件の強化を適用する処理です。apply_build_bonus() は、分類を一定回数選んだときの追加効果です。
この2つを分けると、次の流れが読みやすくなります。
- レベルアップ画面で候補を選ぶ
apply_upgrade()で通常の強化を適用する_record_build()で分類の選択回数を増やす- 段階をまたいだ場合だけ
apply_build_bonus()を呼ぶ - HUDへ分類カウントを表示する
通常の「弾ダメージ +1」と、3回選択したときの「ビルドボーナス 弾ダメージ +1」は、同じ数値を増やしていても発生条件が違います。処理を1つへ混ぜると、通常強化のたびにボーナスまで入るミスが起きやすくなります。
6. ボーナス発生をHUDへ通知する
能力値だけが変わると、プレイヤーは何が起きたのか気づきにくいことがあります。_record_build() のボーナス発生箇所で、既存のステータス表示へ短い通知を出します。
前回記事で追加した BUILD_LABELS を利用できます。
if current_tier > previous_tier:
player.apply_build_bonus(build_id)
status_label.text = "%sビルドボーナス発生" % BUILD_LABELS[build_id]
既存の stats_changed シグナルが StatusLabel を毎フレーム更新する構成なら、通知がすぐ通常表示へ戻る場合があります。その場合は、BuildBonusLabel をHUDへ追加し、数秒だけ表示する処理を後で加えます。
まずは通知文を表示して、段階判定が動いたことを確認することを優先します。派手な演出は、数値と条件が正しく動いてから足します。
7. HUDの分類カウントと能力値を照らし合わせる
ビルドボーナスが発生したら、HUDの分類カウントと能力値を同時に確認します。
たとえば攻撃分類なら、次のように変化します。
| 攻撃の選択回数 | 攻撃カウント | 弾ダメージの変化 |
|---|---|---|
| 0〜2回 | 0〜2 | 通常の強化分だけ |
| 3〜5回 | 3〜5 | ビルドボーナス +1 |
| 6〜8回 | 6〜8 | ビルドボーナスが合計 +2 |
回収分類なら、3回ごとに吸引範囲が40増えます。ただし、300へ達したあとにさらにボーナス段階を進めても、値は300のままです。カウントは選択履歴として増え、能力値は上限で止まるという違いがあります。
この違いをHUDで確認できるようにすると、「選んだ回数」と「実際に伸びた値」を分けて検証できます。
8. 重み付き抽選との順番をそろえる
前回記事では、build_counts を使って候補の重みを計算しています。今回のボーナスも同じカウントを参照します。
レベルアップ1回の流れは、次の順番です。
- 前回までの
build_countsで候補の重みを計算する - 最大値に達していない候補から3件まで抽選する
- プレイヤーが1件を選ぶ
- 通常の強化を適用する
build_countsを1増やす- 段階をまたいだ場合にビルドボーナスを適用する
- 次回の抽選で新しいカウントを使う
選択前にカウントを増やすと、表示しただけの候補が重みとボーナスへ影響します。候補を確定したタイミングと、実際に選択したタイミングを分けておくことが大切です。
9. ボーナスの二重付与を防ぐ
二重付与は、同じ選択処理が複数回実行されたときに起きます。ボタンの pressed シグナルが同じ関数へ2回接続されていないか確認してください。
また、apply_upgrade() の中から _record_build() を呼び、さらにボタン処理からも呼ぶ構成にしないでください。分類を記録する場所は、_apply_selected_upgrade() から1回だけ呼ばれる _record_build() に統一します。
将来セーブ機能を追加するときも注意が必要です。保存した build_counts を読み込んだあとに、過去のボーナスを最初からすべて再適用すると、同じボーナスが二重になります。保存した能力値をそのまま復元するか、元の能力値と選択履歴から一度だけ再計算するかを決め、二重に適用しないようにします。必要に応じて付与済み段階も保存します。
10. よくあるエラーと確認ポイント
3回目を選んでもボーナスが出ない
previous_tier と current_tier の計算を確認します。float() と floori() を使い、2回から3回へ変わる境界で段階が0から1へ変化することを確認してください。
1回の選択でボーナスが2回入る
_record_build() が複数箇所から呼ばれていないか確認します。通常のボタン処理、apply_upgrade()、シグナル接続の3か所を検索します。
回復量が最大値を超える
apply_build_bonus() の生存分岐に mini(MAX_HEAL_AMOUNT, ...) があるか確認します。通常の回復量アップ側にも同じ上限処理が必要です。
吸引範囲のボーナスが効かない
回収アイテム側が固定値を読んでいないか確認します。アイテム生成時に player.magnet_range を参照する設計なら、ボーナス後に出現したアイテムにも新しい値が反映されます。
HUDの通知が見えない
status_label が別のシグナル処理ですぐ上書きされている可能性があります。専用の BuildBonusLabel を追加し、通常ステータスと通知を分けます。
11. 実行時の確認手順
コードとシーンを保存したら、次の順で確認します。
- F6またはF5でメインシーンを実行する
- HUDの分類カウントが0から始まることを確認する
- 攻撃分類の強化を3回選ぶ
- 「攻撃ビルドボーナス発生」の通知を確認する
- 弾ダメージが通常強化分に加えて1増えていることを確認する
- 回収分類を3回選び、吸引範囲が40増えることを確認する
- 生存分類を3回選び、回復量が1増えることを確認する
- 同じ分類を6回選び、2段階目のボーナスが1回だけ発生することを確認する
- 回収範囲300、回復量5の上限を超えないことを確認する
- 重み付き抽選、最大値候補の除外、候補の重複防止が引き続き動くことを確認する
候補はランダムに表示されるため、特定の分類を3回選ぶまでに時間がかかる場合があります。確認時は一時的に UPGRADE_POOL を絞るか、デバッグ用の直接適用処理を用意します。確認後は通常の候補一覧へ戻してください。
12. まとめ
今回は、同じビルド分類を3回選ぶごとに専用ボーナスを付与しました。
build_countsから現在のボーナス段階を計算する- 段階をまたいだときだけボーナスを1回付与する
- 能力値の変更はPlayer側へまとめる
- 回収範囲と回復量には既存の上限を適用する
- 選択処理を1か所へ集めて二重付与を防ぐ
- HUDの分類カウントと能力値を照らし合わせて確認する
これで、攻撃や回収の強化を選び続けた結果が、単なる表示上の分類から実際の能力ボーナスへつながりました。レベルアップの1回ごとの判断が、ゲーム全体のビルド形成として感じられるようになります。
次は、ビルドボーナスの段階や効果をセーブデータへ保存し、ゲームを再開しても育てたビルドが失われない仕組みを作ります。
参考:Godot 4とGDScriptの入門書
Godot 4とGDScriptの基本から2Dゲーム開発を学びたい場合は、今回のようなシーン構成やスクリプトの分割を体系的に確認できる書籍も参考になります。
※本記事にはアフィリエイトリンクが含まれています。
Godot 4 & GDScriptではじめる 2Dゲーム開発レシピ(Amazonで詳細を見る)
※本リンクはアフィリエイトリンクです。
統合版プロジェクトとの対応
この記事で扱う機能は、企画全体をまとめたauto_attack_final_verified.zipにも組み込んでいます。この記事のコードは段階的に追加するための説明なので、途中の記事と統合版全体のファイル構成は一致しない場合があります。
統合版では強化の分類回数とボーナスをPlayer側で管理し、候補の抽選は固定の重みを使います。分類回数による候補の重み付けや専用の通知は実装されていません。回収などの分類で6回目のボーナスを試すには候補の上限や種類を増やす必要があります。統合版には後続記事の機能も含まれます。記事の機能を確認するときは該当する操作を先に試し、最後にGodot 4.7でF5起動、入力、セーブ・ロード、再起動後の復元を確認してください。
