iT邦幫忙

2025 iThome 鐵人賽

DAY 29
0
Rust

Bevy Rogue-lite 勇者冒險篇 × Rust 遊戲開發筆記系列 第 29

關卡調整以及新增敵人

  • 分享至 

  • xImage
  •  

玩家的負面狀態有中毒,但目前並沒有一個敵人角色可以造成這種狀態,所以需要新增「蜘蛛」這個新敵人。它的攻擊模式不太一樣,並且會吐出造成中毒效果的細絲。

同時也需要整理關卡獎勵流程,讓每一層打完 Boss 的報酬與武器成長有明確節奏,並重新調整 Boss 的能力,會依照玩家目前能力放大 10%~30%。

這回的重點:

  • 蜘蛛會在中遠距離吐出蛛絲,命中會直接把玩家設成中毒狀態,並持續造成傷害。
  • 關卡內的寶箱、以及打倒 Boss 後掉落的獎勵,改成固定表單,確保武器與盾牌依序升級。
  • Boss 的血量、攻擊、防禦改成參考玩家目前的基礎數值,再放大 10%(一般關卡)或 30%(最終關卡)。

蜘蛛的蛛絲攻擊與中毒效果

蜘蛛的 AI 會在距離合適時,射出一條帶碰撞檢查的蛛絲專案。spider_ranged_attack_system 讀取玩家位置並控制冷卻 (new_demo/src/systems/enemy.rs:486-531),衝出的一瞬間會呼叫 spawn_spider_web_projectile 建立射出物。核心片段如下:

if distance > attack.radius || distance < SPIDER_MIN_ATTACK_DISTANCE {
    continue;
}

let direction = to_player.normalize_or_zero();
if direction == Vec2::ZERO {
    continue;
}

spawn_spider_web_projectile(
    &mut commands,
    transform.translation,
    direction,
    attack_stat.value(),
);

專案本體在 spider_web_projectile_system 內追蹤碰撞與傷害 (new_demo/src/systems/enemy.rs:900-1035)。這段程式碼我只顯示命中處理的部分:

if along.abs() <= half_length {
    let perpendicular = (to_player - projectile.direction * along).length();
    if perpendicular <= SPIDER_WEB_PROJECTILE_HIT_RADIUS {
        let damage = compute_damage(projectile.damage, defense_value);
        if damage > 0 {
            health.current = (health.current - damage).max(0);
            damage_events.write(PlayerDamagedEvent { damage, remaining_health: health.current });
        }

        if let Some(poison) = poison_component.as_mut() {
            poison.reset_timer();
        } else if !poison_active {
            commands.entity(player_entity).insert(Poisoned::new(
                PLAYER_POISON_TICK_SECONDS,
                PLAYER_POISON_TICK_DAMAGE,
            ));
        }

        enemy_attack_events.write(EnemyAttackHitEvent);
        commands.entity(entity).despawn();
        continue;
    }
}

蜘蛛攻擊

可以看到命中後會先計算傷害,再檢查玩家是否已經中毒;若沒有,就立即插入新的 Poisoned 組件。毒傷本身透過 PLAYER_POISON_TICK_SECONDSPLAYER_POISON_TICK_DAMAGE 控制,這樣就能和既有的 update_player_health_ui、音效系統整合。

關卡寶箱與 Boss 獎勵的節奏

場景啟動時會挑出三個地板擺放寶箱,順序固定為寶箱怪 + 兩個補給箱 (new_demo/src/systems/level.rs:1123-1204)。這段程式會先從候選地板中挑出三個位置,再交給 Chest::new 建立寶箱。

let mut chest_payload: Vec<(Vec3, ChestContents, &'static str)> = Vec::new();

if let Some(pos) = slots.get(0) {
    chest_payload.push((*pos, ChestContents::Mimic, "RewardChestMimic"));
}

if let Some(pos) = slots.get(1) {
    let effect = match rng.gen_range(0..3) {
        0 => PickupEffect::Heal(ITEM_HEALTH_POTION_HEAL_AMOUNT),
        1 => PickupEffect::RestoreStamina(ITEM_STAMINA_POTION_AMOUNT),
        _ => PickupEffect::CurePoison,
    };

    chest_payload.push((*pos, ChestContents::Item(effect), "RewardChestElixir"));
}

這樣每一個房間都保證有寶箱怪製造緊張感,剩下的兩格則是補給抽籤。prop_position_valid 會確保寶箱距離門口、出生點與出口都有安全距離,不會把走道堵死。

打倒 Boss 之後的掉落改成依照關卡索引直接指定裝備 (new_demo/src/systems/level.rs:1258-1291):

let loot_items = match rewards.level_index {
    0 => vec![
        PickupEffect::EquipShield(ShieldKind::Level1),
        PickupEffect::EquipWeapon(WeaponKind::Level2),
    ],
    1 => vec![PickupEffect::EquipWeapon(WeaponKind::Level3)],
    2 => vec![PickupEffect::EquipWeapon(WeaponKind::Level4)],
    _ => vec![
        PickupEffect::EquipWeapon(WeaponKind::Level5),
        PickupEffect::EquipShield(ShieldKind::Level2),
    ],
};

這個 switch 直接定義四層的掉落順序,不會再隨機跳階。後續只是把效果裝進 Chest::new,在出口門附近一字排開。透過這個表單,玩家會依序拿到 Lv2~Lv5 武器與升級的盾牌,數值成長非常明確。

打完 Boss 後才會出現傳送門

現在打完 Boss 後,會出現獎勵寶箱,以及傳送門

Boss 能力值依玩家成長

wizard_boss_stats 會讀取玩家目前的攻擊、防禦與最大血量,再乘上 1.1(一般關卡)或 1.3(最終戰)當作 Boss 的新數值 (new_demo/src/systems/level.rs:1060-1083)。具體寫法如下:

let multiplier = if final_boss { 1.3 } else { 1.1 };

if let Some(stats) = player_stats {
    let attack = ((stats.attack as f32) * multiplier).ceil() as i32;
    let defense = ((stats.defense as f32) * multiplier).ceil() as i32;
    let health = ((stats.max_health as f32) * multiplier).ceil() as i32;
    return (health.max(1), attack.max(1), defense.max(0));
}

也就是說 Boss 的基礎能力直接跟玩家綁定,即使玩家撿到 Lv5 武器或升級,也不會把 Boss 秒掉;反過來說如果升級不足,Boss 也只會高出 10%,仍然能靠走位與補品打過。


小結

現在的戰鬥節奏比過去多了中遠距離威脅,蜘蛛若沒及時閃開就會吃到毒。打倒 Boss 後也能明確看到下一把武器、下一面盾牌的升級。

今日程式碼同步至 repo


上一篇
打擊特效與動畫調整
系列文
Bevy Rogue-lite 勇者冒險篇 × Rust 遊戲開發筆記29
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言