做一款即時戰略《狼煙》(上):怎麼讓「自動出兵」變得有得玩

2026年6月13日 · wemee (with AI assistant)

遊戲開發 Phaser 即時戰略 遊戲設計 TypeScript

🔥 《做一款即時戰略》 這是一個兩篇的系列,記錄我替遊戲區做的第三款中型遊戲《狼煙》。

  1. 設計篇:怎麼讓「自動出兵」變得有得玩(本篇)
  2. 手感篇:兩個 bug 教我的「別想太複雜」

做完塔防《防線》戰術地城《推演》兩款中型遊戲後,我想再做一款不同類型的——即時戰略。成品在 /game/warfront

起點是一個很樸素的構想,我大概是這樣描述的:

分兩個陣營,可以升級主堡(加血、加收入),收入可以拿去產兵,兵會自動往對方主堡進攻。兵種分近戰、遠程、魔法⋯⋯

聽起來很完整,對吧?但這裡有個一講就懂、做下去才會痛的陷阱:如果兵是自動產、自動走、自動打,那玩家從頭到尾到底在「玩」什麼?

先別寫,先想:這會不會是個放置遊戲?

照原構想直接做,玩家其實只做一件事:把錢花在「升級」還是「出兵」。開局設定好,剩下就是看兩排小人對撞。那不是策略遊戲,是放置遊戲——開著掛機就好。

所以我這次沒有急著動手,先把方向攤開來談。我列了三條路:

  • A. 純拔河絞肉機:玩家只管出什麼兵、何時升級,戰鬥全自動。最簡單,但最容易變放置。
  • B. 經濟博弈:強化「攢經濟 vs 早壓制」的賭局,加入偵查與資訊不對稱。
  • C. 半即時指揮:在自動戰鬥上加一層玩家手動施放的技能,給臨場操作感。

最後選的是 C 的操作感 + B 的經濟張力 + 相剋三角的兵種。這篇就拆這幾根支柱——它們合起來,才把「放著看」變成「有得玩」。

支柱一:相剋是三角,不是滑桿

原構想裡近戰分「防高攻低 / 攻守均衡 / 攻高防低」。我一度想直接照做,但停下來想了一下:這三個其實是同一條軸上的三個刻度。同軸的強弱,結局幾乎一定是玩家算出「哪個 CP 值最高」,然後全出那一種——選擇就死了。

好玩的兵種系統要像剪刀石頭布:每個兵都剋一個、又被另一個剋。所以我把它重排成一個會互相咬的環:

  • 🛡️ 盾兵:高血低攻,剋騎兵(肉身擋下衝鋒)
  • 🏹 弓兵:遠程點放,剋盾兵(站遠了磨,盾兵追不到也打不痛)
  • ⚔️ 騎兵:高速高攻,剋弓兵(衝進去秒脆皮)

實作上,相剋就是一張「誰剋誰」的表加一個倍率函式:

const STRONG_AGAINST: Record<UnitType, UnitType[]> = {
  shield:  ['cavalry'],
  cavalry: ['archer', 'mage', 'cleric'], // 騎兵也擅長突進後排
  archer:  ['shield'],
  mage:    [],
  cleric:  [],
};

export function counterMultiplier(attacker: UnitType, defender: UnitType): number {
  if (STRONG_AGAINST[attacker].includes(defender)) return 1.6; // 剋:1.6 倍
  if (STRONG_AGAINST[defender].includes(attacker)) return 0.7; // 被剋:0.7 倍
  return 1;
}

再加兩個不進三角、但各有定位的角色:🔮 法師走範圍傷害(專剋擠成一團的近戰)、✨ 牧師只治療不攻擊(放大前排續航)。這樣「看到對方出什麼、我該怎麼拆」就變成一個真的要動腦的問題,而不是無腦堆最優解。

支柱二:攢經濟,還是現在就壓死你?

這才是整款遊戲真正的張力來源。金幣會隨時間長,你得決定花在哪:

  • 全力升級收入 → 後期碾壓,但前期空虛,可能被一波 rush 直接推掉主堡。
  • 全力出兵 → 早期壓制,但拖久了被對方的經濟拉開。

這個「賭時機」的拉扯,比任何單一兵種的數值都重要。它讓每一局都有「我現在該攻還是該守、該貪還是該穩」的判斷。

而為了讓這個賭局真的是賭,我加了戰爭迷霧:敵方後場被霧蓋住,你看不到對方在屯兵還是在衝經濟。要看穿,得花指揮值施放偵查。實作就是一條 x 界線,加上對敵方單位的可見性切換:

// 此 x 之後(敵方後場)平時看不到敵方部隊
export const FOG_X = BOARD_WIDTH * 0.6;

private updateFog(): void {
  const reveal = this.scoutMs > 0;             // 偵查生效中?
  for (const u of this.units) {
    if (u.side === 'enemy') u.setVisible(reveal || u.x <= FOG_X);
  }
  this.fogRect.setAlpha(reveal ? 0.12 : 0.5);  // 掀霧時淡出
}

於是「要不要花這點資源去偷看一眼」本身,就變成一個有重量的決定。

支柱三:給玩家一雙手——手動指揮

這是解決「放置遊戲」的關鍵。我加了一條跟金幣分開的資源:指揮值(CP),會隨時間回補,專門用來施放三個手動技能:

  • ☄️ 隕石:玩家手動點選戰場落點,造成大範圍爆發傷害。這個瞄準的動作,就是操作技巧的展現。
  • 📣 戰吼:全軍攻擊與移速短時間大幅提升,用來壓一波。
  • 🔭 偵查:掀開戰爭迷霧。

把資源分成兩種(金幣管軍隊與經濟、CP 管技能)是刻意的——這樣施放技能不會直接跟「出兵 vs 升級」搶錢,它有自己的節奏。玩家在盯著金流做巨觀決策的同時,還能用 CP 做微觀的臨場介入。手,就有事情做了。

支柱四:別讓贏家滾雪球

純粹「誰先推到對方主堡誰贏」有個致命傷:一旦一方領先就會滾雪球,輸家只能眼睜睜看著被推平,毫無樂趣。

我的解法是主場優勢:越靠近自家主堡的部隊,打得越兇、也越耐打。

// 攻擊方在自家主堡附近 → 傷害加成
if (Math.abs(attacker.x - homeX) <= HOME_RANGE) dmg *= 1 + HOME_DAMAGE_BONUS; // +35%
// 守方在自家主堡附近 → 受擊減免
if (Math.abs(target.x   - homeX) <= HOME_RANGE) dmg *= 1 - HOME_DEFENSE_BONUS; // -25%

再加上主堡本身會射防禦箭。這樣當你被推回家門口時,反而站上了最強的防守位置——你有機會穩住、甚至絕地反攻。戰線於是會來回拉扯,而不是一路崩到底。這就是「拉鋸」這兩個字的由來。

收尾

回頭看,《狼煙》最重要的設計決定,全都不是「哪個兵幾點攻擊」,而是怎麼把一個會自動運轉的系統,變成需要玩家不斷做決策的賽局:相剋逼你拆解、經濟逼你賭、迷霧逼你猜、指揮逼你出手、主場逼你別放棄。

兵多不會贏,做對決策才會贏——這是我給這款遊戲定的調性。

不過設計對了,不代表手感就對。核心做完、我坐下來實際玩之後,馬上撞到兩個讓人哭笑不得的 bug。下一篇講那兩個 bug,以及它們教我的一件事:身為工程師,最該警惕的就是把簡單的問題想複雜。

留言 0

留言載入中…