符文堡壘的設計筆記:把打字練習變成守塔遊戲
從程式碼拆解《符文堡壘》:目標怎麼鎖定、難度怎麼爬升、字母試煉為何一排一排練,以及每分鐘單詞和正確率怎麼算。
《符文堡壘》是一款打字守塔遊戲:怪物頭上頂著字母或英文單字,從右邊走向左邊的高塔,你在牠撞上高塔前把字打出來,塔上的法師就會射出魔法彈。這篇筆記的內容都來自遊戲的程式碼,推測的部分會特別註明。想先玩一下,可以到遊戲頁面。
一個鍵,一發魔法
戰場是一張 384×216 的像素畫布,分成四條路線,上面兩條是蝙蝠和幽靈,下面兩條是史萊姆、哥布林和骷髏。同一條路線的怪物會排隊,標籤不會疊在一起,而且要整個進到畫面後才能打。
高塔有 5 顆心,小怪撞上一次扣一顆。在「字母試煉」裡,打對一個鍵就擊退一隻怪;在「單詞冒險」裡,每打對一個字母,怪物會被往後推 2.5 像素,整個字打完才會消失。
該打誰:鎖定與 Backspace
字母試煉沒有鎖定,按下的鍵會找帶著同一個字母、離高塔最近的那隻。出題時會避開畫面上已有的字母和上一個出過的字母,所以一個鍵幾乎都只對應一隻小怪。
單詞冒險是先鎖定再打完。打出第一個字母時,程式會鎖定「以這個字母開頭、離高塔最近」的怪物;之後打錯也不會跳到別隻身上,進度也保留。被鎖定的標籤有青色外框,下一個要打的字母底下有底線。
我們推測鎖定是為了不讓進度四散:沒有鎖定的話,手滑剛好按到另一個字的開頭,就會變成好幾個字各打一半。但鎖錯時就需要一個退出鍵,也就是 Backspace:它會放開鎖定,並把那個字的進度歸零,所以換目標不是免費的。為了少鎖錯,挑字時會在最多 30 次抽選中,優先選開頭字母不在畫面上的字。
字母試煉:一次練一排
練習範圍有五種:「中排・基準鍵」(A S D F G H J K L)、「上排」、「下排」、「全部字母」和「大小寫混合」,預設是中排。程式裡有一張標準指法表,每個字母對應一根手指,並用顏色標示:小指紫、無名指藍、中指綠、食指黃。
程式沒寫為什麼分排,但從結構可以推測用意:只練一排時,手指動作的方向一致,中排幾乎不動、上排往上、下排往下,可以先記熟「哪根手指管哪個鍵」。大小寫混合模式中,每個字母有一半機會是大寫,提示會叫你用另一隻手的 Shift,例如 F 歸左手食指,就提示右手按 Shift,這是標準指法的做法。
字母試煉也會針對弱點出題:
// Letters the player keeps missing come up more often.
function pickLetter(r) {
const pool = [...(LETTER_SETS[r.set] || LETTER_SETS.all)];
const live = new Set(r.enemies.filter((e) => !e.dead && !e.boss).map((e) => e.text.toLowerCase()));
let list = pool.filter((c) => !live.has(c) && c !== r.lastCh);
if (!list.length) list = pool;
const ws = list.map((c) => 1 + Math.min(3, (r.misses[c] || 0) * 0.5));
權重是 1 加上「錯誤次數 × 0.5」,加成最多 3,所以常錯的鍵最多以 4 倍機率出現。錯誤次數記的是按錯時本來該按的鍵,以及走到高塔的怪物身上的字母,而且整局只增不減。
單詞冒險:226 個字,附中文意思
字庫有 226 個英文字,分三級:「學徒」3 到 4 個字母(88 個)、「術士」5 到 6 個字母(77 個)、「大法師」7 個字母以上(61 個,最長 9 個)。多是奇幻和日常用字,像 rune(符文)、potion(藥水)。
每個字都附繁體中文意思,開啟「顯示中文意思」時會用小一號的字顯示在單字下方,介面是中文時預設開啟。挑字時,最近出現過的 18 個字不會重複。
難度怎麼往上爬
速度和出怪頻率都寫在這幾行:
const ramp = (r) => 1 + 0.012 * r.waveKills + 0.15 * (r.wave - 1);
const baseSpeed = (r) => (r.mode === 'letters' ? 15 : [0, 10.5, 9.5, 8.6][r.tier]) * r.speedMul * ramp(r);
const spawnGap = (r) => (r.mode === 'letters' ? 1.55 : [0, 2.5, 2.9, 3.3][r.tier]) / Math.pow(r.speedMul, 0.85) / ramp(r);
const maxAlive = (r) => (r.mode === 'letters' ? 6 : 5);
- 字母怪每秒走 15 像素;單字怪依難度是 10.5、9.5、8.6,字越長走得越慢,出怪間隔也依序是 2.5、2.9、3.3 秒(字母是 1.55 秒)。場上最多 6 隻(字母)或 5 隻(單字)。
- 「慢」「普通」「快」是 0.6、1、1.4 倍。出怪間隔除以的是倍率的 0.85 次方,所以選「慢」時走路速度降到 6 成,出怪頻率只降到約 6.5 成。
- 每擊退一隻怪,速度和出怪頻率加 1.2%;每進下一波再加 15%。一波有 30 隻(字母)或 16 隻(單字)。
公式裡沒有時間,只有擊退數和波數:壓力跟著你的進度往上加,打得慢,壓力也長得慢。
流星雨:存滿才能放
魔力只靠打對的鍵累積:字母試煉每鍵加 4,25 個鍵存滿;單詞冒險每個字母加 1.6,要 63 個。存滿後按空白鍵,畫面上的小怪全被流星清掉,魔力歸零。
限制也不少:流星擊倒的怪只拿一半分數;它不算打字次數,不會讓每分鐘字數和正確率灌水,也不加連擊;它傷不了巨龍,只把牠往後推 30 像素,若你正鎖定巨龍的咒語,進度還會歸零。看起來它是危急時爭取時間用的,不是用來代替打字。
每一波的最後:巨龍
小怪處理完、畫面清空後,「暗焰巨龍」登場,音樂從每分鐘 88 拍換成 126 拍。牠頭上有 4 段咒語:字母試煉是 4 組各 4 個字母,從同一個練習範圍抽;單詞冒險是同難度的 4 個不重複單字。
巨龍很慢,每秒 4.2 像素,每過一波快 12%;但牠一碰到高塔,這局直接失敗。每打完一段咒語,牠會被推回一段。牠在場時還會召喚飛行小怪,場上已有 2 隻就不召喚。字母試煉中,同一個鍵如果同時對得上小怪和巨龍,先算小怪,程式註解寫的理由是小怪才是威脅。打倒巨龍可拿「300 × 波數」獎勵分,接著可選「下一波」,補 2 顆心(最多 5 顆)。
打字當下的回饋
- 聲音全用 Web Audio 即時合成。打對是一聲短促的「嗶」,每連續打對 6 個,音高往上爬一階;打錯是一聲低沉的嗡。每 10 連擊會響起三個音的提示音。
- 打對時,魔法彈從法師的光球劃弧線飛出,打過的字母變金色,怪物閃白光。打錯時光球冒出灰紫色火花,畫面輕輕一震。
- 螢幕鍵盤上,下一個該按的鍵用手指顏色亮起,空白鍵左邊寫著像「F · 左手食指」的提示;按下的鍵閃青色是對、紅色是錯。
- 連擊倍率是「1 + 連擊數 ÷ 30」,60 連擊時到上限 3 倍。打錯或有怪撞塔,連擊歸零。
結算畫面算了什麼
- 單詞冒險顯示「每分鐘單詞」:打對的字母數 ÷ 5 ÷ 分鐘數,也就是把 5 個字母當一個字,這是打字測驗常見的算法。字母試煉則顯示「每分鐘字母」。
- 正確率是打對 ÷(打對 + 打錯),只有畫面上有該打的目標時按錯才算錯。時間只算實際遊玩,暫停不算;連打好幾波,數字會一路累計。
- 「多練這幾個鍵」列出錯最多的 3 個鍵、次數和該用的手指,下方還有小鍵盤熱度圖。記下來的是本來該按的鍵,而不是實際按到的鍵,我們推測這是為了指出你還不熟的位置。
- 過關時,正確率 96% 以上且至少剩 4 顆心給 3 顆星,88% 以上 2 顆,其餘 1 顆。失敗時會提示:「試試『慢』速度:先打得準,速度自然會跟上。」
給剛開始練的人
- 標題畫面寫著「兩手食指放在 F 和 J 的凸點上,準備施法」,螢幕鍵盤的 F、J 也畫了小凸線。
- 預設是中排、學徒級、普通速度,指法提示也預設開啟。
- 開著注音輸入法也能玩:偵測到非英文輸入時,遊戲改用按鍵的實體位置判斷,並跳出提示。
- 按住一個鍵不會連續觸發;大小寫混合模式下開著 Caps Lock 會提醒。
- 手機和平板可以直接點螢幕鍵盤。切換分頁時會自動暫停,Esc 暫停、再按一次繼續。