iT邦幫忙

2025 iThome 鐵人賽

DAY 18
0

上一篇完成了盾牌的裝備,想到武器應該也可以比照辦理,所以決定擴充武器系統。預設目標是當玩家打敗 boss 之後,會出現並打開特定寶箱時,可以獲得更高等級的武器,可以立即感受到攻擊力的成長。也順便整理武器的圖片素材與資料結構,讓未來要新增武器時更容易維護。

設定目標:

  • 以資料驅動的方式定義武器等級、攻擊加成與對應圖片。
  • 玩家預設就拿 Lv1 武器,並能透過事件升級到 Lv2 ~ Lv5。
  • 寶箱會顯示不同等級的武器,打開後會替換玩家武器並更新攻擊力。
  • 玩家與寶箱共用同一條裝備武器事件管線,讓後續擴充更容易。

WeaponKind 與 EquippedWeapon

new_demo/src/components/equipment.rs 新增 WeaponKindEquippedWeapon

#[derive(Clone, Copy, Debug)]
pub enum WeaponKind {
    Level1,
    Level2,
    Level3,
    Level4,
    Level5,
}

impl WeaponKind {
    pub fn attack_bonus(&self) -> i32 {
        match self {
            WeaponKind::Level1 => 0,
            WeaponKind::Level2 => 6,
            WeaponKind::Level3 => 12,
            WeaponKind::Level4 => 20,
            WeaponKind::Level5 => 30,
        }
    }

    pub fn right_sprite_path(&self) -> &'static str {
        match self {
            WeaponKind::Level1 => "weapons/lv1.png",
            WeaponKind::Level2 => "weapons/lv2.png",
            WeaponKind::Level3 => "weapons/lv3.png",
            WeaponKind::Level4 => "weapons/lv4.png",
            WeaponKind::Level5 => "weapons/lv5.png",
        }
    }
}

所有武器數值與圖片素材都集中在這個 enum 中。EquippedWeapon 則儲存目前等級與攻擊加成,讓系統在任何地方都能讀取現況。武器素材同樣以 lv1.png ~ lv5.png 命名,放在 new_demo/assets/weapons/,方便後續擴充。

武器圖片

玩家角色誕生時,直接掛上 EquippedWeapon::new(WeaponKind::Level1),並把手中的 Sprite 改為對應貼圖:

commands
    .spawn((
        Player,
        Sprite::from_image(asset_server.load("characters/players/knight_lv1.png")),
        Attack::new(PLAYER_BASE_ATTACK),
        EquippedWeapon::new(WeaponKind::Level1),
        // ...
    ));

commands
    .spawn((
        Weapon,
        Sprite::from_image(asset_server.load(WeaponKind::Level1.right_sprite_path())),
        WeaponSprites {
            right_sprite: asset_server.load(WeaponKind::Level1.right_sprite_path()),
            left_sprite: asset_server.load(WeaponKind::Level1.left_sprite_path()),
        },
        Name::new(format!("Equipped{}", WeaponKind::Level1.display_name())),
    ));

如此一來,角色在出現時就擁有等級資訊,升級時也只要更新這個 component。


WeaponEquipEvent 統一升級流程

參考上一篇的盾牌流程,補上 WeaponEquipEventnew_demo/src/systems/equipment.rs 讀取事件後會:

  1. 計算新舊攻擊加成差值,呼叫 Attack::adjust_bonus 即時計算總攻擊力。
  2. 更新 EquippedWeapon,記住目前的武器等級及加成。
  3. 尋找玩家子節點裡的武器實體,替換成新的貼圖並更新節點名稱。
for event in events.read() {
    let new_bonus = event.kind.attack_bonus();
    attack.adjust_bonus(new_bonus - current_bonus);

    *equipped_weapon = EquippedWeapon::new(event.kind);

    if let Some(children) = children {
        for child in children.iter() {
            if let Ok((entity, mut weapon_sprites, mut sprite)) = weapon_query.get_mut(*child) {
                let right = asset_server.load(event.kind.right_sprite_path());
                weapon_sprites.right_sprite = right.clone();
                weapon_sprites.left_sprite = asset_server.load(event.kind.left_sprite_path());
                sprite.image = right;
                commands
                    .entity(entity)
                    .insert(Name::new(format!("Equipped{}", event.kind.display_name())));
                break;
            }
        }
    }
}

事件來源包含兩條:

  • 寶箱打開後由 chest_item_reveal_system 送出。
  • 玩家撿拾地上掉落的武器時,由 player_pickup_detection_system 送出。

EquipmentPluginhandle_weapon_equip_events 排在 Update 迴圈中,緊跟著寶箱與拾取的系統,確保畫面與數值同步更新。


Demo 寶箱 - 盾牌 + 武器升級

為了方便測試,新版的 spawn_weapon_demo_chests 將房間中心附近的地板挑出來,依序會有六個寶箱:兩個盾牌、四個武器。

let chest_loadout = [
    (
        ChestContents::Item(PickupEffect::EquipShield(ShieldKind::Level1)),
        ShieldKind::Level1.display_name(),
    ),
    (
        ChestContents::Item(PickupEffect::EquipShield(ShieldKind::Level2)),
        ShieldKind::Level2.display_name(),
    ),
    (
        ChestContents::Item(PickupEffect::EquipWeapon(WeaponKind::Level2)),
        WeaponKind::Level2.display_name(),
    ),
    // ... Lv3~Lv5
];

for (index, (contents, label)) in chest_loadout.iter().enumerate() {
    let Some(base_position) = positions.get(index) else { break };
    commands.spawn((
        Chest::new(contents.clone()),
        Sprite::from_image(asset_server.load("items/chests/chest_closed.png")),
        Transform::from_translation(Vec3::new(base_position.x, base_position.y, CHEST_Z))
            .with_scale(Vec3::splat(CHEST_SCALE)),
        Name::new(format!("{}Chest", label)),
    ));
}

靠近寶箱按空白鍵之後,展示武器素材並在計時器結束時替玩家升級。Log 會顯示攻擊總值,例如「武器裝備完成,當前攻擊:51 (SwordLv4)」,方便確認數值是否如預期增加。


實際效果

  • 玩家出生就拿著 lv1.png 武器,造型與攻擊力對應 WeaponKind::Level1
  • 打開寶箱後,武器 sprite 立即替換為 lv2.png ~ lv5.png,攻擊加成最多可提升 +30。
  • 同一顆寶箱可以示範盾牌與武器的流轉,之後改成 boss 掉落時,只要把 ChestContents 換成對應項目即可。
  • 敵人死亡掉落的道具也會顯示正確名稱,方便排查是否拿到武器或盾牌。

開箱升級武器

打完怪或開箱後,武器外觀與攻擊數值同步升級。


小結

目前把武器升級的資料與事件管線串起來,未來如果要新增其它武器類型(例如弓或法杖)時,只要擴充 WeaponKind 與對應的事件處理即可。寶箱與地上掉落都走同一條流程,也方便在 boss 戰後直接丟出高等級武器作為獎勵。

今日程式碼同步至 repo


上一篇
裝備系統與武器定位
系列文
Bevy Rogue-lite 勇者冒險篇 × Rust 遊戲開發筆記18
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言