iT邦幫忙

2025 iThome 鐵人賽

DAY 13
1

暨上一篇將死亡與重生機制補齊之後,遊戲的基本循環已經成形。要繼續往 Rogue-lite 的隨機化與角色成長前進,第一步就是讓角色的數值可以成長

過去的攻擊與防禦都寫死在常數中,玩家和敵人的數值都是寫好一個固定的值,沒有辦法延伸到裝備、藥水或升級系統。這篇的目標是把這些數值模組化,建立共用的運算流程,並做出一個可以即時調整的面板方便驗證。整體拆成三件事:

  • 把攻擊、防禦改寫成具備基礎值/加成/倍率的 Component
  • 抽出共用的傷害計算函式,讓玩家與敵人走同一條管線
  • 建立一個 Debug UI 面板與快捷鍵,快速觀察屬性變化對戰鬥的影響

讓攻擊與防禦 Component 化

新的 AttackDefense Component 放在 src/components/stats.rs,各自保留 basebonusmultiplier 三個欄位,並提供 value() 來產出最終數值。額外準備 adjust_bonus()adjust_multiplier()reset_modifiers() 等方法,後續系統只要呼叫這些介面就能套用加成,而不用直接碰欄位。

// src/systems/setup.rs
commands.spawn((
    Player,
    Health::new(PLAYER_INITIAL_HEALTH),
    Attack::new(PLAYER_BASE_ATTACK),
    Defense::new(PLAYER_BASE_DEFENSE),
    Velocity::zero(),
    PlayerFacing::new(),
    InputVector(Vec2::ZERO),
));

玩家角色在產生時就擁有這兩個 Component,目前有的敵人包含史萊姆與獨眼巨人也在各自的 spawn_*_internal 裡加上 Attack::new(SLIME_BASE_ATTACK) 以及 Defense::new(SLIME_BASE_DEFENSE)

常數也同步改名為 *_BASE_ATTACK*_BASE_DEFENSE,與「最終傷害」做出區隔。之後如果要依照關卡或難度調整敵人,直接換掉基礎值即可。

統一的傷害流程

所有傷害判定都改用 compute_damage(attack, defense)。流程很單純:先加上攻擊方的加成與倍率,若對方有 Defense 就扣掉防禦值,最後保底至少 1 點。玩家揮劍與史萊姆的碰撞攻擊都走同一條管線,確保之後加成不會出現「玩家有生效、敵人沒生效」的落差。

// src/systems/attack.rs
let total_attack = attack.value() * attack_count as i32;
let defense_value = defense.map(|value| value.value());
let damage = compute_damage(total_attack, defense_value);
health.current = (health.current - damage).max(0);
// src/systems/enemy.rs
let defense_value = defense.map(|value| value.value());
let damage = compute_damage(attack_stat.value(), defense_value);
health.current = (health.current - damage).max(0);

因為邏輯集中於 compute_damage,其他系統只需要關心「什麼時候送出攻擊事件」與「攻擊打得到誰」,維護起來更輕鬆。之後如果想加入暴擊或屬性傷害,只要在這裡擴充即可。

即時屬性面板與快捷鍵

為了避免每次測試都得重新編譯,我做了一個 Debug 面板顯示當前屬性,放在血條下方,開場自動產生:

PLAYER STATS
ATK  xxx  (base xxx, bonus +xx, x1.x)
DEF  xxx  (base xxx, bonus +xx, x1.x)
Adjust: [1] ATK +5  [2] ATK -5  [3] DEF +3  [4] DEF -3
        [Q] ATK x-0.1  [W] ATK x+0.1  [A] DEF x-0.1  [S] DEF x+0.1  [R] Reset

屬性展示1 UI

player_attribute_debug_input_system 監聽鍵盤輸入,遇到指令就直接修改 Component:

  • 1 / 2: 攻擊加減 5 點
  • 3 / 4: 防禦加減 3 點
  • Q / W: 攻擊倍率 -0.1 / +0.1(下限 0)
  • A / S: 防禦倍率 -0.1 / +0.1
  • R: 重置加成與倍率

每次修改後會同步更新 UI,也會在 log 印出最新數值,方便對照敵人的受傷情況或玩家被打的血量。面板的文字全部換成 ASCII,因為如果不換的話會有缺字問題。

測試情形

完成後主要測了四種情境:

  1. 基礎數值:開場不做任何調整,UI 顯示 15/4,揮劍檢查敵人掉血量與 log 呈現一致,攻擊力 15 扣除史萊姆防禦 2 之後,顯示玩家的攻擊造成 13 點傷害。

基本攻擊

  1. 攻擊加成:連續按 1 提升攻擊力,敵人死亡的刀數明顯減少,log 也同步印出更新後的值。

攻擊力連續加成

現在的攻擊力已經達到 40,相當輕鬆就秒殺史萊姆。

秒殺史萊姆

  1. 防禦調整:讓史萊姆靠近,改變防禦倍率,確認受到的傷害會隨著倍率增減,且即使防禦超過攻擊仍至少掉 1 點血。

史萊姆的基礎攻擊力是 5,而玩家角色的原始防禦力是 4,在承受攻擊之後就造成傷害 1。

基本防禦承受攻擊

在防禦力已經升高到 22 之後,在承受史萊姆的 5 點攻擊力之後,即便防禦力比攻擊力還要高,一樣還是會被扣一點,這樣依然保有遊戲的樂趣。

防禦力升高承受攻擊

小結與後續

把攻防變成 Component 是後續數值系統的基礎,現在只要某個效果想調整攻擊或防禦,就能直接操作 Attack / Defense 的 bonus 與 multiplier。不必再東挖一個常數、西改一個系統,整體維護成本低很多。

今天的程式碼分享在 repo


上一篇
死亡與重生迴圈
下一篇
道具效果與自動拾取系統
系列文
Bevy Rogue-lite 勇者冒險篇 × Rust 遊戲開發筆記18
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言