目前為止,都還沒有碰到關於裝備的部分,既然已經做到寶箱的這個階段,那麼寶箱不再只是掉藥水,而是能提供盾牌並直接替玩家增加防禦。另外,也趁機重新整理劍與盾的顯示方式,確保走路時的視覺保持一致。
預計達成的目標:
先放兩個盾牌的素材到專案中:
new_demo/src/systems/chest.rs
原本會隨機生成寶箱,今天改成 spawn_shield_demo_chests
,直接挑選最靠近房間中心的兩塊地板,生成一級與二級盾牌。
let chest_loadout = [ShieldKind::Level1, ShieldKind::Level2];
for (index, kind) in chest_loadout.iter().enumerate() {
let Some(base_position) = positions.get(index) else { break };
commands.spawn((
Chest::new(ChestContents::Item(PickupEffect::EquipShield(*kind))),
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", kind.display_name())),
));
}
流程與上一篇類似,按空白鍵打開寶箱後,ChestItemReveal
計時 2 秒,時間到就丟出 PickupEffect::EquipShield
。
新增的 new_demo/src/components/equipment.rs
定義 ShieldKind
、EquippedShield
與 ShieldVisual
Component。核心邏輯放在 new_demo/src/systems/equipment.rs
:
ShieldEquipEvent
由兩個來源送出:寶箱 (chest_item_reveal_system
) 與地上的拾取物 (player_pickup_detection_system
)。handle_shield_equip_events
讀取目前裝備的盾牌,算出新的防禦加成 (kind.defense_bonus()
),呼叫 Defense::adjust_bonus
更新屬性。ShieldVisual
子節點,就先刪除再生成新的 sprite,保持畫面上只有一面盾。for event in events.read() {
let new_bonus = event.kind.defense_bonus();
defense.adjust_bonus(new_bonus - current_bonus);
commands
.entity(player_entity)
.insert(EquippedShield::new(event.kind));
// 重新掛上盾牌 sprite
let shield_entity = commands
.spawn((
ShieldVisual,
Sprite::from_image(asset_server.load(event.kind.sprite_path())),
Transform::from_translation(Vec3::new(SHIELD_OFFSET_X, SHIELD_OFFSET_Y, SHIELD_Z))
.with_scale(Vec3::splat(SHIELD_SCALE)),
))
.id();
commands.entity(player_entity).add_child(shield_entity);
current_bonus = new_bonus;
}
為了把裝備的管線和其他系統解耦,新增 EquipmentPlugin
(new_demo/src/plugins/equipment.rs
),在 Update
排程裡讓 handle_shield_equip_events
跟在 chest_item_reveal_system
、player_pickup_detection_system
後面執行。
新增的常數集中在 new_demo/src/constants.rs
:
pub const WEAPON_IDLE_OFFSET_X: f32 = 10.0;
pub const WEAPON_IDLE_OFFSET_Y: f32 = 4.0;
pub const WEAPON_Z: f32 = 1.0;
pub const SHIELD_OFFSET_X: f32 = -8.0;
pub const SHIELD_OFFSET_Y: f32 = -1.0;
pub const SHIELD_Z: f32 = 1.0;
原本角色在往四周移動時,劍會朝著不同方向,但因為希望左手拿的是盾牌,右手拿劍,所以需要讓劍維持在固定的位置,可能會是比較簡易且方便的做法。
update_weapon_offset_system
直接套用常數,而不再依照 PlayerFacing
更新,因此往任何方向走都是同一個 sprite、同一個角度。揮劍動畫仍使用 WeaponSwing
計時轉動,但基準角度固定為 0.0
。SHIELD_OFFSET_X/Y
,確保位置固定在角色左側。偏移量調整出來的結果。
打開寶箱會先看到盾牌 sprite 飄在箱子上方,計時結束後,角色立刻換上左側盾牌並把防禦數值加上 4 或 8。而劍固定位置之後,玩家往上/下/左移動時,畫面不會再出現劍跟著旋轉的奇怪姿勢,揮刀動畫也更好辨識。
這篇的重點是把裝備當成完整的資料流:
PickupEffect::EquipShield
→ ShieldEquipEvent
→ 防禦數值、盾牌 sprite。constants.rs
的數值即可。EquipmentPlugin
讓裝備相關邏輯獨立管理,後續可以在同一插件下擴充武器、飾品等系統。打開寶箱拿到盾牌這個流程,預計不會出現在一般寶箱中,而是會在 boss 戰之後的獎勵,所以希望在剩下的篇章可以順利完成這個環節。
今日程式碼同步至 repo