拆特效不拆檔:一場老遊戲改裝的源頭追獵
委託很單純:一款老線上遊戲的畫面太熱鬧,主人想把幾個技能與武器系統的視覺特效拆掉——傷害照算,煙火免了。手上有一份前人留下的舊補丁當範本,手法一看就懂:特效定義是 UTF-16 的 XML,一個技能對應一組「發射器」,把發射器的內容清空、只留外殼標籤,遊戲照常運轉,特效歸零。
聽起來是體力活。實際上是三層陷阱。
陷阱一:同一個特效有兩套定義
範本補丁有個耐人尋味的細節:同一個技能,它改了兩個檔案。追下去才發現,多年前的一次視覺改版讓新舊兩套特效定義並存,客戶端依設定各走一套——只清其中一套,另一套照播。教訓:動手前先全目錄搜尋目標名稱,數清楚它有幾個分身,不要相信第一個找到的檔案就是全部。後來也真的在四個不相干的雜項檔裡,撈出漏網的同類特效。
陷阱二:過濾字串裡藏著兩顆地雷
要移除的是「爆擊/命中」類特效,幾百個發射器散在十幾個檔案,自然想用名稱過濾:含 hit 或 _cri 的就清。跑完抽查,清單裡躺著一排「白色光環」——white 這個字,肚子裡有一個 hit。另一顆:XML 屬性名 classname 的結尾正好是 name,粗糙的 name="..." regex 會先咬到它,回傳的全是類別名。兩顆都不是高深問題,但過濾規則上線前,把命中清單全量列出來人工掃一遍——這個習慣讓兩顆地雷都死在測試裡,沒進補丁。
陷阱三:換皮道具的替身術
最陰的一層。遊戲有「特效替換道具」,裝備後技能特效換一套外觀。原版發射器早清空了,裝上道具特效卻復活——因為替換機制不是在原特效上疊加,而是把引用整個改名:一張資料表寫著「原本播 A,改播 B」,你清了 A,它播的是 B。十一種道具、上百條替換規則,一條一條去獵 B 是無底洞。正解是反過來改那張表:把每一條的 B 改回 A——替換照常發生,只是目標是那個早已清空的殼。
頓悟:改引用,不改資源
替換表這一課點醒了整個補丁的思路。回頭再找,武器外觀系統也有一張「外型 → 特效名稱」的總表,每一筆都有「爆擊特效」欄位——近兩千筆,一條 regex 清空,涵蓋所有現有外型,連未來新增的都自動包含。對比之前在十幾個檔案裡追殺三百多個發射器,高下立判。
資料驅動的遊戲,資源(特效、模型、音效)只是倉庫,真正決定「播不播」的是引用它的那張表。改資源是治標:名單會過期、藏匿點會增生;改引用是治本:一個欄位,一條規則。當然也有例外——少數特效寫死在動作事件裡,表上查無此人,這種只能回頭清資源。兩軌並存,但主從要分清。
收尾:讓改版變成一鍵
老遊戲每次改版都會刷新這些檔案,手工補丁等於簽了無限期勞務約。最後把整套流程——複製原檔、依規則清空、改寫兩張表、XML 格式驗證——收進一支重建腳本,官方更新後重跑一次即可。掃描規則寫成「自動發現」而非「固定清單」,未來新特效塞進哪個檔案都逃不掉。
帶走的三課:一,動手前先數清楚目標有幾個分身;二,字串過濾的命中清單必須全量人工驗過一次;三,刪不掉的東西,就去改指向它的手指。