仙泉.香布纏.續Larch RPG 引擎實測

Larch RPG 引擎實測

《續》寫這一章時(2026-10-21)是 111 張地圖、609 張卡;2026-10-24 新版是 278 張地圖、921 張卡。做的過程踩到 Larch RPG 引擎很多條上限,最常見的樣子是整張地圖只剩一行「這張地圖的資料無法讀取」。這一章一條一條記:哪一天踩到、怎麼發現的、怎麼繞過、現在用哪個檢查擋住。大部分是 AI 讀本機預覽器附的引擎原始碼、在本機預覽實測出來的,幾條是我試玩時看到的。想用 Larch 做 RPG 的人,動手前可以把這一章當清單看一遍。2026-10-22、23 做玩法、把只放新地圖的新版推上新專案時,又踩到一批,寫在後面〈推上去的新版停在標題〉〈新版玩不通,檢查都過了〉〈做玩法時查到的〉三節。

整張地圖讀不出來

地圖讀不出來時,畫面上只有這一行
地圖讀不出來時,畫面上只有這一行

畫面上只看得到這一行。2026-10-21 寫這一章時,AI 在本機預覽把下表大部分重跑了一次:故意弄壞一張測試用的地圖,原因要從頁面的文字裡讀,例如「RPG Error: 角色動畫格式不正確」。下表的「引擎報的原因」除了任務提示那一條,都是這次重跑抄下來的。

日期 踩到什麼 引擎報的原因 現在的做法
2026-10-07 起 同一格放了兩個事件 事件需在地圖內,每格一個事件 見下面〈一格只放得下一個事件〉
2026-10-15 隨機挑一句的動作超過 10 個分支(想念的句子一組太多句) 隨機數值需要最小到最大(-9999–9999)與 1–10 個分支,每個分支的數字由小到大 分組;tests/check_crowd.py 擋
2026-10-16 同一張地圖兩個事件 id 撞名(推車和商車都叫 f3d-cart) ID 必須唯一,且只能包含英數、底線、連字號 改名;短測開頭先查
2026-10-17 事件 id 有中文 同上 id 一律英數,例如 decor-<序號>
2026-10-19 第五章趕工那張的任務提示 18 筆 遊戲任務格式不正確 Larch 當天取消上限,18 筆還原
2026-10-20 自製下雪一格畫面 256×960、24 格;傷兵道具圖 384×677 角色動畫格式不正確 每格寬高最多 512 像素、最多 16 格;check_static 9n
2026-10-21 地圖 65×65 地圖寬高需為 2–64,格子尺寸需為 8–96 一張最大 64×64,大的地方好幾張接
2026-10-21 hop 沒寫 times 跳躍需選擇主角或這個角色、1–10 次,目標要在地圖內 一定寫 times

任務提示那一條的經過:2026-10-19 AI 在做瀏覽器測試加速,跑到第五章發現趕工那張地圖進不去。任務提示(左上角那個「遊戲任務」)有 18 筆,當時引擎上限 16。AI 先拆成 16 筆、在 check_static 加了筆數檢查;我去問 Larch 能不能放寬。Larch 回覆這是他們自己設的上限,先放寬到 64,同一天就取消了,18 筆還原。當晚任務提示的字數、條件數上限也取消了。

10-22、23 又查到五條規則(事件數、移動路線、分頁、角色圖、id),整理在後面〈新版玩不通,檢查都過了〉的表裡。

整個畫面壞掉的那幾種

比讀不出一張地圖更嚴重的,是整個畫面、整個播放器壞掉。踩過的:

劇本裡只有一個答案的地方(第二章井邊、第九章最後那句「我愛妳。」),就直接演出、記進「思念」,不跳選項。

一張地圖最大 64×64

《續》現有的地圖裡,最大的兩張是嚴家 48×54 格、五原 56×40 格。2026-10-21 決定地圖整個重做,要畫整座城,AI 先實測,65×65 引擎就報「地圖寬高需為 2–64」。所以一張最大 64×64,一格半公尺,大的地方用好幾張接起來。

地圖重做分三層,是我定的:大地圖(64×64,一格約 18 公里)、城或村(好幾張 64×64 的塊相連)、屋內(從門進去的小地圖)。五原的色塊預覽是 13 張 64×64 接起來的,怎麼接寫在下面〈走過邊界換圖〉。

鏡頭只有三種倍率

AI 實測的算法:鏡頭至少看 24×18 格,再乘一個倍率,close 1.35、normal 1、wide 0.75;不設鏡頭,就是整張地圖蓋滿畫面。換算到 16:9 的畫面,wide 約 32×18 格,normal 約 24×13.5 格,close 約 18×10 格。玩家沒辦法自己縮放。

同一個位置、四種鏡頭(燒過的五原,56×40 格)
同一個位置、四種鏡頭(燒過的五原,56×40 格)

這件事 2026-10-07 就碰過。那天我說「希望效果細緻一點」,AI 把原本的一格切成 2×2 的細格。normal 至少看 24×18 格,切細以後畫面上的人和物件會變兩倍大,所以《續》的地圖都不設鏡頭,讓整張圖蓋滿畫面;人物倍率從 1.6 改成 3.2,資料庫裡角色的走路速度加倍,看起來跟切細以前一樣大、走起來一樣快。

重做地圖的鏡頭是我定的:城裡每一塊用 wide,屋內用 normal,大地圖不設鏡頭、人物縮一半。

一格只放得下一個事件

一格只能有一個事件,連只負責顯示一張圖的「自由圖片」事件也算在內。違反就是上表第一列那一行錯誤。寫這一章截圖時又碰到一次:AI 把主角的起點挪到魏家門口最下面那一排,那一排是出巷子的觸碰事件,整張讀不出來。

繞法:

互動點的名牌也繞過一次(2026-10-18)。新介面的名牌是不透明的,疊在補畫的物件上會把物件蓋掉,所以把名牌拆出來,放到圖框頂端那一格,另外放一個沒有步驟的事件。這個事件不能用平行觸發:實測沒有步驟的平行事件不顯示名牌。

圖照下緣排前後

AI 讀引擎原始碼查到的規則(2026-10-08):人和事件一起排前後。自由圖片的排序值是圖框的 y + h − 1,人用自己站的那一列,小的先畫;一樣大的話圖先畫,人在上面。

所以每個物件的圖框下緣,就對齊它佔地最下一列的下緣。站在南邊的人畫在物件前面,站在圖框裡北邊的人被蓋住。

魏家門口的老樹。左:站在第 13 列,樹畫在他上面。右:站在第 14 列,跟樹同一列,他畫在樹上面
魏家門口的老樹。左:站在第 13 列,樹畫在他上面。右:站在第 14 列,跟樹同一列,他畫在樹上面

跟著來的幾條:

擋路格和看不見的牆

碰撞是一層看不見的圖層,要擋的格子塗一格圖塊(mapkit.collision_layer)。最早那幾張整張俯視畫的地圖,每一格擋不擋寫在各地圖的 cell() 裡,改了就重產疊圖,一格一格看:綠的能走、紅的擋、藍點是事件(python3 art/overlay.py <圖>)。

嚴家的碰撞疊圖:綠色能走、紅色擋路、藍點是事件
嚴家的碰撞疊圖:綠色能走、紅色擋路、藍點是事件

2026-10-09 我說棚子旁邊走不過去,地圖不要有看不見的牆。AI 的做法:產圖以後量每個佔地格,物件圖在那一格畫到的不到四成,就自動放開(layout.thin_cells);一格一根的直柵欄、設計檔寫 solid: true 的例外。check_static 列出還擋著、圖上又沒畫的格子,負控制是不放開時,燒過的五原那個棚子的 (18,24) 要抓得到。

另外幾條:

走過邊界換圖、路過的人

這兩個做法是 2026-10-21 做五原色塊預覽時實測出來的。

左:走過邊界換圖。右:路過的人用兩頁組出來
左:走過邊界換圖。右:路過的人用兩頁組出來

走過邊界:兩塊相接的那條邊上,每一格各放一個觸碰事件,跳到隔壁那一張同一個位置、往裡一格,落點不能是對面的換圖格;角落那一格兩邊都接時,只接一邊。node tests/city_walk.mjs 確認北門兩邊走得過去,python3 art/wuyuan/plan.py 擋相鄰兩塊的邊對不上(一邊能走、一邊是牆)。

路過:我要城裡有人從街的一頭走到另一頭,消失,再從起點出現。引擎沒有這種走法,用兩頁組:看得見的那一頁在背景走到終點,把自己的變數設成真;看不見的那一頁在背景用 hop 跳回起點、停一下,再把變數設回假。hop 一定要寫 times,漏了整張地圖讀不出來。2026-10-21 AI 在本機做了正反兩個測試:寫了 times: 1 的地圖正常,沒寫的就是上表那一行錯誤。

其他會動的人:

HUD:一個擴充功能最多 8 個

《續》自己做了一個外掛 xu-voice,裡面 8 個 HUD:說不出口、蓋板、地窖的米、準備出路、年份與刻痕、乾糧、右上的時鐘、心境面板。

2026-10-18 加到第 9 個時,所有 HUD 一起不見,地圖和事件照跑,沒有任何錯誤訊息。所以心境面板和升階的小提示合成一個 HUD。2026-10-20 我問「應該都不限制了吧」,前一天 Larch 才取消了任務提示的上限。AI 用當天 13:17 更新的 Larch 程式重測:8 個全部載入,10 個、16 個一個都沒有。2026-10-21 寫這一章時,8 個和 10 個又各跑了一次,結果一樣。那時這個 8 是實測值,原始碼裡沒找到寫明的那一行。

2026-10-23 做成就時,AI 找到了那一行:播放器檢查擴充功能時寫著「一個擴充功能最多 8 個常駐 UI」,超過就整個擴充功能驗證失敗,它的 HUD 一個都不載入。上限是一個擴充功能各算各的。AI 另開一個測試用的擴充功能來量(tests/hud_plugin2_probe.mjs):它放 2 個、8 個,xu-voice 原本的 8 個都還在,最多 16 個一起載入;它塞到 9 個,只有它自己的 HUD 全部不見,xu-voice 照舊。所以真的要第 9 個面板,放進第二個擴充功能就好。多開的要自己排位置,16 個擠在四個角會疊在一起。

上:原本 8 個 HUD,右上角的年份時鐘在(截圖時時鐘把 223 年算成黃初三年,寫這一章時才發現,已經改成黃初四年)。下:加到 10 個,一個都沒載入
上:原本 8 個 HUD,右上角的年份時鐘在(截圖時時鐘把 223 年算成黃初三年,寫這一章時才發現,已經改成黃初四年)。下:加到 10 個,一個都沒載入

原始碼裡查得到的是另外幾條:

主角的樣子跟著資料庫走

引擎畫主角,用的是資料庫裡「目前的主角」,地圖上主角事件給的圖、綁的角色都蓋不過。換造型要用 hero 步驟換主角,實測換的時候位置不變:

這個「目前的主角」會跨地圖沿用。2026-10-20 我試玩第七章,划完船進屋、進曹營都還在划船。現在離開地圖前一定換回走路的樣子,check_static 9l 擋,負控制是故意不換要抓得到。

事件分頁和自動觸發

存檔點

2026-10-10 我試玩完說:「存檔好像不方便 是不是有需要提醒玩家存檔之類的 至少戰鬥前」。

引擎有一個 save 步驟,會叫出播放器的存檔視窗,關掉才往下(專案沒開存檔功能時,等 1.5 秒就往下,不會卡)。AI 寫的 src/savepoints.py 放在兩種地方:

node tests/save_probe.mjs 驗:存進第一格、重新整理、按「繼續遊戲」,回到同一張地圖。還有一件要我在一般瀏覽器確認:存檔視窗按 Esc 關不掉,要按右上角的叉;無頭瀏覽器關掉視窗以後,鍵盤焦點回不到地圖,測試另外補了一下。

年份標記

2026-10-10 那一輪試玩,我留了三則同一類的回饋:「劇情是要指又過一年還是怎樣」「這裡也是做一次任務過1年對嗎」「玩家可能體感不到幾秒 但是要描寫出有幾個月」。

AI 寫的 src/timemarks.py 是這樣做的:劇本文字不動,每次跳年、跳季,畫面先暗一下(強度 0.85、1.2 秒),旁白報一句「年號(西元),季節。」,例如「熹平三年(174),春。」;任務提示前面加同一個時間,對話卡加在第一句。右上角另有一個常駐的時鐘 HUD,跳年那一下跟著換;沒有旁白報時間的地圖,一進場也會更新時鐘。check_static 用 is_mark() 擋年號對不上西元的句子。

第零幕第一張地圖一進場:旁白報「建寧二年(169),秋。」,右上是時鐘,主角右邊那塊小石碑是存檔點
第零幕第一張地圖一進場:旁白報「建寧二年(169),秋。」,右上是時鐘,主角右邊那塊小石碑是存檔點

測試時摸到的

2026-10-19 為了讓瀏覽器測試跑快一點,AI 又摸到引擎幾個行為:

日常驗內容不開瀏覽器:xu/game/tools/sim/ 照引擎的事件語意(分頁、自動、條件、平行、各種條件判斷)用 Node 重做一遍,整部遊戲不到一秒走完。它模擬不到的是走路和碰撞、名牌遮擋、介面外觀、計時細節、音訊和存檔視窗,這幾樣靠短測和截圖。截圖要等畫面上的「NOW LOADING」消失,一次只跑一個瀏覽器測試。

推上去的新版停在標題

2026-10-23 清晨,只放新地圖的新版第一次推上一個新的 Larch 專案(52a99bd9)。整份專案 JSON 107 MB,送出去被擋回 413。專案 JSON 裡的卡片本來就存兩份:最外層的 nodes、edges,和 boards[0] 裡的同一份。AI 以為最外層那份是重複的,官方編輯器存檔也是這樣存,就把它拿掉,49 MB 推上去了。讀回來比:故事卡 714 張、CDN 網址 2736 個,都跟本機組出來的一樣。

AI 開線上預覽才發現:標題畫面正常,按「開始遊戲」不動。AI 去讀預覽播放器拿到的那份資料:新專案最外層的 nodes 0 張、edges 0 條;舊的《續》專案有送最外層那份,609 張、22 條,同一支測試進得了遊戲。預覽播放器讀的是最外層那一份。

本機測不出來,是因為本機預覽(larch-preview)把專案交給播放器之前,會從白板補回 nodes、edges,模仿上架遊戲的做法,所以本機試玩全過。

修法在 xu/game/src/push_larch.py 的 slim():最外層的 nodes、edges 跟白板同一份一起送;新版只送從起點走得到的卡(連線,加上卡片內容裡提到的任何卡 id,寧可多算)。新版 714 張裡走得到的是 195 張,第五章以後還沒接上的 519 張這次不送,兩份加起來 48.9 MB。讀回時也比最外層 nodes 的張數跟白板一樣。推完除了讀回比數字,還要開線上預覽、按開始遊戲、翻幾句。

新版玩不通,檢查都過了

同一天重推之前,又發現已經推上去的新版第一、二章玩不通。check_static、本機逐句比對、讀回卡數都過了,下面三種它們都抓不到(1ef2bea5):

10-22、23 這兩天碰到的引擎規則整理成下表。「引擎報的原因」是 AI 寫這一節時,從本機預覽器附的引擎原始碼(rpg-core)抄的;larch.ink 線上的播放器是同一個檔,256 那一條線上也有。

規則 引擎報的原因 碰到的地方、現在的做法
一張地圖最多 256 個事件,圖層最多 16 層 事件數量不可超過 256 第一章逃難的路走進邊界那一塊 275 個、第五章宵禁的街 281 個;同一列的遮擋層合併(見下面)
移動要選主角或這個角色,路線 1–8 段、每段 1–20 步 移動需選擇主角或這個角色,路線 1–8 段、每段 1–20 步 新版重算走位截成空路線;改成往牆走一步
主角事件不能有分頁,空的也不行;一個事件最多 98 頁;每一頁的 actor 只能是 none 或 npc 事件分頁格式不正確 自由時間給主角事件加了空的分頁,第四章開場停在讀取畫面;曹營的休息點一個事件 200 頁,改成每張圖只放自己那個時代的年份(玩法)
每一頁都要一張合格的角色圖,看不見的事件也要 角色動畫格式不正確 看不見的事件,每一頁放一張透明的角色圖(mapkit.INVISIBLE)
事件 id 在一張地圖裡不重複、頁 id 在一個事件裡不重複,最長 80 字,只能英數、底線、連字號 ID 必須唯一,且只能包含英數、底線、連字號;分頁 ID 必須唯一 新版那三張讀不出來的地圖,除了空路線,也查到重複的 id

怎麼抓到的

這幾種都要等引擎讀地圖那一刻才報錯,畫面上只有一行「這張地圖的資料無法讀取」。現在推之前多兩道:

check_static 也補上上表後四列的規則,拿修之前的建置跑,城破、燒過的五原都抓得到。

256 那一條,我問過:「你確定是在本機預覽還是 Larch 上?256 上限是真的嗎」。提交訊息記的是:larch.ink 線上的播放器一樣有這一句。修法是 src/occ_merge.py:同一列(圖框下緣同一列,引擎照它排前後)左右相接或重疊的原畫遮擋層,合成一張圖、一個事件,都是同一張原畫剪的,畫面不變。只對超過 240 個事件的地圖做,5 張地圖少了 250 個事件,最多剩 239 個。把後面永遠站不到人的遮擋層拿掉也算過,只省得了 3 個,沒有用。

第八、九章接上新版之前,也照這套跑過:假設原畫都畫好的建置,check_static 198 條到 0 條,走不到 2 條到 0 條,其中一條是檢查自己的誤報(看不見的自動事件被當成要走過去,修好附負控制);第八、九章 33 張地圖加第六章一張,在本機播放器逐張載入都載得起來。

做玩法時查到的

2026-10-22、23 做玩法那一章的系統時,又摸到幾條:

不讓同一個坑再踩一次

上面這些,大部分踩到以後都變成一項檢查,多半配一個負控制,故意弄壞,要抓得到。hop 的 times、地圖大小這兩條,目前靠產地圖的程式照規矩寫,check_static 沒有另外檢查;事件 id 的字元,2026-10-22 起也查了。平常跑的幾道:

cd xu/game
python3 src/build.py            # 產 dist/project.json
python3 tests/check_static.py   # 白名單、上限、每格一個事件、角色圖上限、配樂……
python3 tests/check_crowd.py    # 路人:撞臉、站在牆裡、走不到、隨機動作的上限
node tools/sim/lint.mjs         # 跳轉、任務提示、變數、劇情進度打錯字
python3 tests/check_reach.py    # 新版:任務框指的目標走不走得到
node tests/probe_mapload.mjs <起點卡 id>   # 單獨載一張地圖,印出引擎藏在畫面外的錯誤

推上 Larch 之前,要推的地圖逐張在本機播放器載入一次;推完開線上預覽,按開始遊戲,翻幾句。

地圖正在照這些上限整個重做:五原拆成好幾張 64×64,城裡用 wide 鏡頭,每一塊的邊一格一格接起來。整張畫當地圖、人走到屋後被擋住的做法在走進原畫,一件一件拼的第一套在建城工法:拼出來的城。

← 上一章下一章 →