iT邦幫忙

2025 iThome 鐵人賽

DAY 15
1

把耐力、異常狀態與道具補給串起來之後,繼續要讓這個循環更完整,也就是敵人被打倒會留下道具。所以玩家不只是在清怪,可能需要評估什麼時候砍下最後一刀、如何走位撿補給、以及要不要冒著中毒的風險換一瓶解毒藥水。

預計達成的目標:

  • 監聽敵人死亡,讓掉落道具出現在原地。
  • 把道具產生的邏輯抽出成共用函式,房間初始化與戰鬥掉落都能重用。

當敵人被打倒就掉東西

系統流程

掉落邏輯寫在 new_demo/src/systems/enemy.rs 裡現有的 despawn_dead_enemies_system,只要偵測到 Health 歸零且還沒進入淡出,就會立刻產生道具:

for (entity, transform, health, death_effect, mut sprite) in &mut query {
    if health.current > 0 || death_effect.is_some() {
        continue;
    }

    sprite.color.set_alpha(1.0);

    let drop_effect = random_pickup_effect(&mut rng);
    spawn_pickup_entity(
        &mut commands,
        asset_server.as_ref(),
        drop_effect,
        transform.translation,
    );

    commands.entity(entity).insert(EnemyDeathEffect {
        timer: Timer::from_seconds(ENEMY_DEATH_FADE_SECONDS, TimerMode::Once),
    });

    info!("敵人被擊倒,掉落{}並開始淡出", drop_label);
}

關鍵點:

  1. 使用敵人的 Transform:掉落物會精準出現在屍體所在格子,不會出現在其他地方。
  2. 保留淡出動畫EnemyDeathEffect 還是會加上去,所以原本的淡出與 despawn() 流程完全不動。
  3. log 清楚列出掉落品:實際測試就能清楚看到到底掉了紅色藥水還是解毒藥水。

排程順序

EnemyPlugin 讓這支系統永遠在 player_melee_attack_system 之後、enemy_death_effect_system 之前執行:

app.add_systems(
    Update,
    (
        ...,
        despawn_dead_enemies_system.after(player_melee_attack_system),
        enemy_death_effect_system.after(despawn_dead_enemies_system),
    ),
);

因此流程會是:先扣血 → 下一禎產生掉落 → 再淡出敵人。玩家只要走過去,player_pickup_detection_system 就會立即觸發效果。


把產生道具邏輯共用

items.rs 新增的輔助函式

為了避免「地圖補給」和「敵人掉落」兩邊各寫一次程式,所以會把把邏輯拆成三個工具:

fn pickup_effect_options() -> [PickupEffect; 3];

pub fn random_pickup_effect<R: rand::Rng + ?Sized>(rng: &mut R) -> PickupEffect;

pub fn spawn_pickup_entity(
    commands: &mut Commands,
    asset_server: &AssetServer,
    effect: PickupEffect,
    base_position: Vec3,
);
  • pickup_effect_options() 有紅、綠、灰三種藥水,哪裡要改掉落池只要動這裡。
  • random_pickup_effect() 接受任何 Rng,未來要依敵人種類換權重也很方便。
  • spawn_pickup_entity() 統一 Sprite 載入、縮放與 Z 軸偏移,確保地圖上的補給一致。

spawn_random_pickups 也改成呼叫這些函式,地圖初始化時丟出的道具和敵人掉落完全一樣,不會出現大小不同或命名不一致的狀況。

Component 仍然簡單

src/components/items.rs 的結構延續上一篇講到的:

#[derive(Component, Debug, Clone)]
pub struct Pickup {
    pub effect: PickupEffect,
}

#[derive(Debug, Clone)]
pub enum PickupEffect {
    Heal(i32),
    RestoreStamina(f32),
    CurePoison,
}

拿取時 player_pickup_detection_system 直接 match 效果:

  • Heal:補血但不會超過上限。
  • RestoreStamina:補耐力,搭配 log 顯示前後數值。
  • CurePoison:移除 Poisoned Component,解除持續扣血。

打怪變補給

戰鬥節奏跟之前相比,最大的改變是擊殺 = 資源回收。實測幾個情境:

  • 耐力管理:連砍把史萊姆秒掉後,可能會掉一瓶綠藥水,馬上補回攻擊成本,玩家可以繼續衝下一隻。
  • 中毒對策:身上掛著 Poisoned 的時候,只要再砍一刀把敵人收掉,就有機會掉出灰色解毒藥水,絕境翻盤的機率變高。
  • 血量滾雪球:紅色藥水一次補 40 HP,史萊姆攻擊只有 5,意思是即使被打七八下,只要擊殺就能把血量拉回安全線。

log 與 UI 互相呼應:

  • log 會顯示「敵人被擊倒,掉落紅色藥水並開始淡出」、「拾取綠色藥水:耐力 15.0 -> 75.0」。
  • 建好的 Debug 面板能看到數值即時顯示,確認補給是否生效。

整體體感變成「擊殺 → 撿補給 → 再出手」的節奏,玩家不再只是在清空房間,而是透過戰鬥取得續戰資源。

實際打怪完顯示道具的流程

實際打怪完顯示道具的流程


目前關於設定

  • 敵人會不會重複掉落? 不會。只要 EnemyDeathEffect 已經加在身上,就不會再跑一次掉落邏輯。
  • 掉落會消失嗎? 目前沒有計時器,玩家離開房間再回來仍然會看到道具。後面如果要加時間限制,只要在 spawn_pickup_entity 多掛一個計時器即可。

小結

現在有一些獎勵的機制,來鼓勵玩家攻擊對手,但還是有些地方可以再做調整,例如:

  • 掉落權重:目前三種藥水均分,也許可以讓不同敵人對應不同掉落池。
  • Elixir 試管:我有放其他的遊戲資源在 assets/items/elixirs/,預計之後會加入暫時提高上限的效果,會需要額外的計時 UI。
  • 視覺特效:想幫掉落物加上簡單的漂浮或粒子效果,讓道具更顯眼。

今日程式碼同步至 repo


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

尚未有邦友留言

立即登入留言