iT邦幫忙

2025 iThome 鐵人賽

DAY 24
1
Rust

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

巫師 Boss 的遠程攻擊

  • 分享至 

  • xImage
  •  

暨上一篇把多個關卡流程設定完,這篇要把傳送門的門前放上一個 boss。然後也一直想做遠程攻擊,所以就放了一個巫師站在門前施放魔法,這樣可以維持距離又能造成玩家的壓力。

這回的重點:

  • 調整巫師常數,延伸觸發半徑並建立專屬魔法球的速度、尺寸、顏色。
  • 為巫師新增魔法球元件與存活計時,確保魔法球能在 ECS 裡受控。
  • 改寫巫師 AI:判斷距離後進退,冷卻完就產生魔法球。
  • 撰寫魔法球移動 / 命中系統,統一透過事件扣血,避免和原本的近戰邏輯打架。

巫師常數與素材

new_demo/src/constants.rs 先引入 Color,新的一組常數定義巫師的遠程設定:

pub const WIZARD_BOSS_ATTACK_RADIUS: f32 = 260.0;
pub const WIZARD_BOSS_CAST_MIN_DISTANCE: f32 = 140.0;
pub const WIZARD_BOSS_PROJECTILE_SPEED: f32 = 360.0;
pub const WIZARD_BOSS_PROJECTILE_LIFETIME: f32 = 2.0;
pub const WIZARD_BOSS_PROJECTILE_SIZE: f32 = 18.0;
pub const WIZARD_BOSS_PROJECTILE_HIT_RADIUS: f32 = 24.0;
pub const WIZARD_BOSS_PROJECTILE_COLOR: Color = Color::srgb(0.72, 0.28, 0.92);
  • ATTACK_RADIUS:施法上限(傳送門前站著也會被打)。
  • CAST_MIN_DISTANCE:過近時會後退,防止玩家卡在腳邊無限揮空。
  • PROJECTILE_*:速度/壽命/碰撞半徑與視覺尺寸,魔法球顏色直接用 Color::srgb 設成紫色。

boss 素材

素材方面使用新增的 assets/characters/enemies/wizard.pngassets/weapons/enemy/wizard_staff.png,法杖縮放改成 1.0,並把 offset 調到 8.5, -2.0 讓武器貼在右手。


魔法球元件

為了讓巫師的魔法球能在 ECS 裡統一管理,new_demo/src/components/enemy.rs 增加兩個 component:

#[derive(Component)]
pub struct BossWizardProjectile {
    pub velocity: Vec2,
    pub damage: i32,
}

#[derive(Component)]
pub struct BossWizardProjectileLifetime {
    pub timer: Timer,
}

魔法球會帶著速度 / 傷害值,搭配 Timer 自動過期。產生時同時加上 LevelEntity,換關即會被清掉。


巫師 AI 進退距離 + 施放魔法

new_demo/src/systems/enemy.rsboss_wizard_ai_system 原本只是共用近戰巡邏邏輯,現在改成專屬流程:

  1. 抓距離並調整站位to_playerdistance 是整個策略的基礎。距離小於 CAST_MIN_DISTANCE 就以 retreat_dir 往反方向滑動;距離過大則微幅前進,始終保持在 140~260 之間。因為巫師只需左右移動,所以 clamp 只作用在 translation.x
  2. 與巡邏系統整合:雖然是頭目,仍沿用 EnemyPatrolbounds()。這樣做的好處是,萬一場景很狹長,巫師還是會遵守房間左右邊界,不會退到牆外。
  3. 施法冷卻EnemyAttack.cooldown.tick(delta) 後判斷 finished(),若是第一次施法會立即重置冷卻,確保巫師能在看到玩家的瞬間開火。
  4. spawn_wizard_projectile:呼叫時會把 transform.translationdirection 傳入,函式內部會根據 WIZARD_BOSS_STAFF_OFFSET_XWIZARD_BOSS_CAST_HEIGHT_OFFSET 把產生點往前、往上偏移,呈現出從法杖頂端發射的視覺效果。
  5. 朝向更新patrol.direction = dx.signum() 維持左右 facing 的正負號,後續如果要切換 sprite 甚至決定施法音效,都能沿用這個欄位。

這段仍然保留原始巡邏 fallback,若玩家離開觸發/牽制距離,EnemyBehaviorState::Patrolling 會接手,巫師就像一般敵人一樣在房間內來回,避免卡在單一點位等待玩家回頭。


魔法球系統 - 移動、命中與事件

巫師的魔法會在 boss_wizard_projectile_system 中統一更新,相關程式如下:

pub fn boss_wizard_projectile_system(
    mut commands: Commands,
    time: Res<Time>,
    mut projectile_query: Query<(
        Entity,
        &mut Transform,
        &BossWizardProjectile,
        &mut BossWizardProjectileLifetime,
    )>,
    player_target_query: Query<(
        &Transform,
        Option<&Defense>,
    ), (With<Player>, Without<PlayerDead>, Without<BossWizardProjectile>)>,
    mut player_health_query: Query<
        &mut Health,
        (With<Player>, Without<PlayerDead>, Without<BossWizardProjectile>),
    >,
    mut damage_events: EventWriter<PlayerDamagedEvent>,
    mut enemy_attack_events: EventWriter<EnemyAttackHitEvent>,
) { /* ... */ }
  • 位移與壽命:每個魔法球根據 velocity 持續向前,BossWizardProjectileLifetimeTimer 到期就 despawn,確保不會永久漂浮或在離線畫面耗效能。
  • 查詢互斥:玩家查詢加上 Without<BossWizardProjectile>,避免和 projectile_query 同時借用 Transform 造成 B0001 錯誤,不然可能會遇到 panic,並且透過條件約束避免重複抓取同一批 component。
  • 命中偵測:使用 distance <= WIZARD_BOSS_PROJECTILE_HIT_RADIUS 做圓形範圍判斷。若未來要做更複雜的彈道(例如穿透或爆炸),可以在這邊擴充碰撞邏輯。
  • 累計與事件:命中結果先裝進 hits,統一處理 despawn 與扣血,並發出 PlayerDamagedEventEnemyAttackHitEvent。這樣 UI、音效、震動等下游行為完全不需修改,只要監聽既有事件即可。

同一幀可能有多顆魔法球命中,程式會先把結果放進 hits 陣列,再統一 despawn,確保血量只在成功命中時扣一次,且事件順序穩定。若玩家已死亡,player_health_query.single_mut() 會失敗並落到 else 分支,只把子彈清掉不再寫事件,避免額外 panic。


整合與關卡配置

  • src/plugins/enemy.rs:把 boss_wizard_projectile_system 加進 Update,放在近戰傷害前面,魔法球命中仍會觸發相同的傷害事件。
  • src/resources/level.rs:第一關 boss_wizards: 1,進遊戲就能立即觀察巫師招式,,目前還在思考 boss 會在哪幾關出現。
  • src/systems/level.rs:產生巫師時把 NameEnemyAttack 初始值調整好,保持 Fallback 跟巡邏邏輯一樣。

實際 demo

配合上一篇的系統,boss 固定站在傳送門前,只要玩家到一定的距離內,就會開始釋放魔法攻擊玩家。


小記

加上一個遠程攻擊的法師 boss 後,傳送門的威脅感立刻提升許多。不過我預想的會是:

  • 打地圖上的小怪到一定數量後,才能開啟 boss 出現的條件。
  • boss 打完後,會出現寶箱和傳送門,可以前往下一關。
  • 讓魔法球朝玩家移動時加速度或彈道變化,讓戰鬥更有節奏。
  • 針對玩家的防禦或狀態效果調整傷害公式,例如讓魔法傷害穿透部分防禦。
  • 把魔法球系統抽成通用模組,未來其他敵人或玩家技能都能重用。

今日程式碼同步至 repo


上一篇
關卡系統與傳送門
系列文
Bevy Rogue-lite 勇者冒險篇 × Rust 遊戲開發筆記24
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言