MarkVenture 創作者指南
Version: 2.1 Date: 2026-05-04 Language: zh-TW
想像你的玩家打開故事,發現自己置身在一間陌生的密室。四面牆壁,一個上了鎖的保險箱,書架上藏著某個線索。他們開始探索、翻東西、嘗試每一把鑰匙,最後當保險箱彈開的瞬間,興奮地把連結傳給朋友。
這個體驗,你只需要 Markdown 就可以做到。
MarkVenture 語言以 Markdown 為基礎,在它上面加了一套腳本語法。場景、物件、玩家看到的文字,用 Markdown 寫;玩家做了某件事之後會發生什麼,用腳本語法寫。兩件事合在同一個純文字檔案裡,不需要寫程式。
還不熟悉 Markdown?網路上有很多五分鐘入門,搜尋「Markdown 教學」就找得到。MarkVenture 只用到最基本的幾個語法,跟著範例走很快就上手了。
目錄
入門
核心語法
進階功能
參考
1. 五分鐘上手
先從最小的開始
複製下面這段,貼到編輯器——你的第一個互動故事就完成了:
---
title: 神秘房間
language: zh-TW
---
## 房間
你站在一個陌生的小房間裡。牆上掛著一幅[畫](painting)。
thing painting "一幅風景畫":
[仔細看] show "畫的背面有一行字:「勇氣是唯一的鑰匙。」"
@here:
[離開] -> 走廊
## 走廊
你走出了房間,迎面是長長的走廊。
end
就這樣。 這個故事有兩個場景、一個可以互動的事物、一個結局。
四個核心概念:
| 寫法 | 作用 |
|---|---|
## 場景名稱 |
定義一個場景 |
@here: |
這個場景裡玩家可以做的事(後面縮排填選項) |
[文字] -> 目標場景 |
跳到另一個場景 |
thing id "描述": |
定義可互動的事物 |
加入更多機制
掌握基礎之後,看看加入道具、鎖、多個場景之後能做到什麼。把下面這段貼到編輯器試試:
---
title: 密室逃脫:書房
language: zh-TW
---
## 書房
你醒來,發現自己在一間陌生的書房。房門緊閉,空氣有點沉。
桌上放著一個[木盒子](box),書架上有一本[奇怪的書](book)。角落有扇[木門](door)。
@here:
[把書架推開] -> 書架後面
thing box "一個上鎖的木盒子":
fallback "這東西打不開。"
use rusty-key:
remove rusty-key
give door-key "銀色的小鑰匙"
show "盒子打開了,裡面有一把銀色的小鑰匙!"
thing book "封面上有奇怪的圖案,書背沾著一點鏽跡。":
[翻開書] show "扉頁上寫著:『鑰匙就在眼前,只是被藏起來了。』"
item door-key "銀色的小鑰匙"
thing door "一扇厚重的木門,裝著一把鎖。":
fallback "門是鎖著的。"
[嘗試推開] show "門紋絲不動。"
use door-key:
show "鎖打開了!你推開門,走出了書房。"
end
### 書架後面
你把書架推開,後面露出一個凹槽,裡面躺著一把[生鏽的鑰匙](rusty-key)。
item rusty-key "一把有點生鏽的鑰匙"
@here:
[返回書房] -> 書房
這個範例多用了幾個機制:item 宣告讓道具可以撿進背包、use 讓道具能觸發事件、fallback 設定預設反應。後面的章節會逐一說明。
基本結構
| 寫法 | 作用 |
|---|---|
--- 包住的區塊 |
故事設定(標題、語言、標籤等,詳見語法速查) |
## 場景名稱 |
定義一個場景 |
### 場景名稱 |
定義子場景(小地點、靠近檢查) |
@here: |
這個場景裡玩家可以做的事 |
[文字] -> 目標場景 |
跳到另一個場景 |
[文字] show "..." |
同場景動作(不跳轉) |
thing id "描述": |
定義可互動的場景事物 |
item id "描述" |
定義可以撿起的道具 |
2. 場景與導航
每個場景由兩個部分組成:
場景描述(scene content)——玩家看到的文字:空間、氛圍、當下的狀態。用 Markdown 散文寫,就像劇本裡的場景說明。
腳本區塊(script block)——這個場景裡有什麼事物、可以做什麼、觸發什麼。用縮排結構語法寫,像劇本裡的舞台指示。
兩者最大的不同:場景描述是靜態的,玩家一進場景就看到;腳本區塊是動態的,定義玩家的行動和後果。你是創作者,每個玩家都是獨立的執行者,也是自己那場演出的唯一觀眾。
跳場景
用 [選項文字] -> 場景名稱 連結場景。場景名稱要完全一致:
## 走廊
黑暗的走廊通往兩個方向。
@here:
[往左走] -> 圖書室
[往右走] -> 廚房
[原地等待] show "你聽到遠處傳來腳步聲。"
## 圖書室
這裡放滿了書。
## 廚房
爐子上的鍋子還在冒煙。
同場景互動
選項後面直接接指令,就是不跳轉的同場景動作:
[搖搖看] show "盒子裡有東西在滾動。"
如果需要執行多個指令,用 [文字]: 加縮排:
[仔細查看]:
show "盒子裡有東西在滾動。"
set box_checked = true
指令區、選項區與 then: 區塊
同一個互動區塊(@here:、entity 互動等)裡,指令、選項、then: 必須按照以下順序出現,不可混用:
指令區(set / show / discover / …)
選項區([選項] / if + [選項])
then: 區塊(共用的後續指令)
use/apply 區(道具互動,永遠最後)
指令區在選項顯示前執行,適合初始化或條件判斷。
選項區列出玩家可以點的選項。選項區的 if 裡只能有選項,不能有其他指令。
then: 是語法糖:它的指令會在每個選項的動作結尾自動附加,等同手動複製到每個選項裡。
use/apply 區定義道具對這個對象的效果,必須放在最後。
@here:
show "門微微開著。" ← 指令區:進入場景時立刻執行
[推門進去] -> 走廊 ← 選項區
[轉身離開] -> 大廳
then: ← 每個選項執行後都會附加這段
set door_checked = true
use key: ← use/apply 區:永遠最後
show "這裡不需要鑰匙。"
上面的範例等同於:
@here:
show "門微微開著。"
[推門進去] -> 走廊:
set door_checked = true
[轉身離開] -> 大廳:
set door_checked = true
use key:
show "這裡不需要鑰匙。"
注意: 若選項區完全沒有選項,then: 無法套用,parser 會報錯。
子場景
## 是主場景,### 是子場景(圖片顯示略小,適合「靠近檢查」的感覺):
## 書房
寬敞的書房,最醒目的是靠牆的大書架。
@here:
[靠近書架] -> 書架
[坐到書桌前] -> 書桌
### 書架
書架上的書都被翻動過。
@here:
[返回] -> 書房
### 書桌
桌面上散落著文件。
@here:
[返回] -> 書房
場景文字格式
場景描述支援部分 Markdown 格式:
| 語法 | 效果 |
|---|---|
**文字** |
粗體 |
*文字* |
斜體 |
> 文字 |
引言(縮排灰字) |
## 密室
**鐵門**緊緊鎖住。你環顧四周,感覺*時間正在流逝*。
> 一張紙條從門縫滑了進來。
3. 腳本區塊
這是 MarkVenture 最有趣的地方。
場景描述之後可以跟著腳本區塊(script block),定義玩家可以互動的東西。腳本區塊裡有三種類型:
| 類型 | 宣告方式 | 說明 |
|---|---|---|
| 場景事物(scene object) | thing id "描述": |
只在定義它的場景有效 |
| 道具(item) | item id "描述" |
可撿進背包,跨場景使用 |
| 線索(clue) | clue id "描述" |
可發現,跨故事記錄 |
定義場景事物與道具
在場景描述裡用 [顯示名稱](id) 標記事物或道具,然後在下方定義它的互動行為(interaction)。id 只能包含小寫英文字母、數字、連字號:
## 入口大廳
大廳中央有一扇[鐵門](iron-door)。
thing iron-door "一扇沉重的鐵門":
[敲門] show "沉重的回響。沒有人回應。"
[嘗試推開] show "紋絲不動。"
如果事物只出現在場景描述裡、不需要互動行為,可以省略冒號和區塊:
thing iron-door "一扇沉重的鐵門"
可撿起的道具
用 item 前綴宣告,玩家就可以把這個道具撿進背包:
地上有一把[生鏽的鑰匙](rusty-key)。
item rusty-key "一把生鏽的老鑰匙"
玩家會看到「撿起」選項;撿起後,鑰匙進入背包,可以對場景中的事物使用。
道具也可以定義自己的互動行為(撿起前後行為不同):
撿起前:「撿起」排第一,其他互動選項接在後面:
item journal "一本皮面日記":
[翻開看] show "扉頁上有人用鉛筆寫了幾個字。"
玩家在場景裡點擊日記,會看到「撿起 日記」和「翻開看」兩個選項。
撿起後:點擊背包裡的日記,會看到「使用 日記…」和「翻開看」兩個選項。「使用 日記…」進入 use mode,玩家再點場景中的目標觸發組合互動;「翻開看」則直接執行。
沒有定義任何互動行為的道具,撿起後點擊仍然直接進入 use mode,行為與之前相同。
用道具解謎
玩家從背包選道具,點場景中的目標,觸發互動。邏輯寫在目標這邊,不是道具這邊:
thing iron-door "一扇沉重的鐵門":
fallback "這個東西對門好像沒什麼用。"
use rusty-key:
show "鎖打開了!"
[進入內部] -> 走廊
fallback 是玩家使用了「沒有對應互動的道具」時看到的訊息。不寫的話,預設顯示「沒有反應。」
消耗道具
用完的道具加上 remove 從背包移除:
thing iron-door "一扇沉重的鐵門":
use rusty-key:
remove rusty-key
show "鎖打開了,鑰匙也斷在裡面。"
[進入內部] -> 走廊
use 與 apply 的區別
use(道具對場景事物,item-on-scene-object): 邏輯寫在場景事物的互動行為裡。玩家點擊場景事物,互動面板會列出「使用 [道具]」的選項。效果只在該場景生效。
apply(道具對道具,item-on-item): 邏輯寫在 item 的互動行為裡。玩家必須先從背包選取道具(進入使用模式),再點擊目標道具才能觸發。不會顯示在選項列表,全故事生效。
這個設計是刻意的:提前顯示「可以用 A 對 B」會洩漏謎題答案。需要隱藏提示的組合,應把目標設計成 item 而非場景固定實體。
道具本身的互動行為: 若道具定義了 link 互動,玩家點擊背包裡的道具時,會開啟互動面板,顯示「使用 [道具]…」(進入 use mode)和所有定義的互動選項。適合「隨時可翻查」的道具,例如筆記本、地圖、日記。
合成道具
一個道具對另一個道具使用,可以產生新道具。邏輯寫在「目標道具」的互動行為裡:
地上有[木棍](stick)和[破布](cloth)。
item stick "一根普通的木棍"
item cloth "一塊破爛的布":
apply stick:
remove stick
remove cloth
give torch "火把"
show "你把布纏在木棍上,做出了火把!"
item torch "用布纏繞木棍製成的火把"
give item-id "顯示名稱" 把道具直接加進背包。透過 give 取得的道具,仍然需要在某個場景的腳本區塊裡宣告 item item-id "描述",否則 parser 會報錯。若場景描述文字裡已有 [名稱](item-id) 的引用,give 後面的名稱可省略;否則建議提供,省略時 parser 會警告,背包會顯示 ID。
對自己使用道具(@self)
玩家選中道具後,點「使用」按鈕,觸發 @self 互動(效果不限場景,全故事生效):
@self:
use potion:
remove potion
set health += 50
show "你喝下藥水,感覺恢復了不少。"
use armour:
remove armour
set defence += 10
show "你穿上了盔甲。"
@self 可以分散寫在多個場景裡,引擎會自動合併。但同一道具只能在一個 @self 區塊定義,重複定義會在儲存時報錯。@self 底下只能有 use/apply 區塊(可搭配 if),選項或其他指令不會有任何效果,parser 會報錯。
4. 條件與變數
追蹤狀態
用 set 改變變數,用 if 根據變數控制選項:
## 走廊
@here:
[開門]:
set door_open = true
show "門打開了。"
if door_open:
[走進去] -> 房間內部
變數名稱只能包含小寫英文字母、數字、底線(_),例如 door_open、has_key、quest_started。
變數不需要預先宣告——set 會自動建立,未宣告的變數預設值是 false / 0 / ""。
在 frontmatter 設定初始值(也可以讓你清楚記錄故事裡用了哪些變數):
---
title: 我的故事
variables:
health: 100
gold: 0
has_invitation: false
---
數值運算
set gold += 10 ← 加
set health -= 20 ← 減
set level = 5 ← 直接設定
條件判斷
@here:
if health > 50:
[繼續前進] -> 深處
else if health > 20:
[謹慎前進] -> 謹慎探索
else:
[太虛弱了,先休息] -> 休息處
比較運算子: = != > < >= <=
字串比較不分大小寫("Lumos" 和 "lumos" 視為相同)。
字串包含: includes
if player_name includes "龍" ← 名字裡有「龍」
if not notes includes "龜" ← 記錄裡沒提到龜
if event_order includes "13" ← 順序裡 1 和 3 連續出現
不分大小寫,連續字元比對。若要判斷非連續字元包含,用多個 includes 搭配 and:
if guilty includes "1" and guilty includes "3"
邏輯連接詞: and or
if has_sword and health > 30:
[戰鬥] -> 戰鬥場景
if gold >= 100 or has_invitation:
[進入宴會廳] -> 宴會廳
道具條件
if has rusty-key ← 道具目前在背包裡
if taken rusty-key ← 道具曾經被撿起過(即使已消耗也為 true)
taken 常用來在道具被撿走後,讓場景描述不再顯示它。
場景文字裡的條件
在描述文字中直接插入條件,根據狀態顯示不同文字:
## 書房
你站在書房。{if door_open}門是開著的。{else}門是關著的。{/if}
{if not taken crystal}角落台座上有一顆[魔法水晶](crystal)。{/if}
你目前有 {gold} 金幣,血量 {health}/100。
隨機數字
@here:
[擲骰子]:
random dice 1 6
show "你擲出了 {dice}!"
if dice >= 4:
show "成功!"
else:
show "失敗。"
語法:random 變數名稱 最小值 最大值(包含兩端)
顯示訊息
show 顯示一則訊息,緊接著繼續執行後續指令:
@here:
[閱讀信件]:
show "信上寫著:『鑰匙藏在書架後面。』"
set read_letter = true
分段暫停(pause)
如果一個動作要顯示多則訊息,可以加上 pause,讓引擎在顯示這則訊息後暫停、等讀者點「繼續」,再執行後面的指令:
@here:
[閱讀日記]:
show "日記的字跡潦草,看來是匆忙寫下的。第一頁寫著一個地址,後面幾頁撕掉了。" pause
show "你翻到最後一頁。空白,只有角落蓋了一個紅色印章。"
不加 pause 的話,多則 show 會同時出現在畫面上(依序以打字機效果呈現)。
pause 也可以單獨一行使用,在不需要新增文字的情況下插入一個暫停點:
@here:
[查看線索]:
show "第一段訊息。"
if resolved some-clue:
pause
show "因為你先前發現了某件事,這裡有額外的訊息。"
玩家輸入
prompt 有兩種用法:自由文字輸入,或選項點選介面。
自由文字輸入
@here:
[說出你的名字]:
prompt player_name
[繼續] -> 歡迎
## 歡迎
「歡迎你,{player_name}!」
選項點選(單選,預設)
玩家點選一個選項,變數存入選項文字。
@here:
[選擇陣營]:
prompt faction [正義聯盟, 暗影公會, 中立商會]
if faction = "正義聯盟":
show "你加入了正義聯盟。"
複選
玩家可勾選多個選項,變數存入 1-based index 串接字串(如 "13" 代表第 1 和第 3 個)。
@here:
[誰是共犯?]:
prompt suspects [管家, 廚師, 園丁] multiple
if suspects = "12":
show "管家和廚師都有嫌疑。"
else if suspects includes "3":
show "園丁也在你的懷疑名單裡。"
排序
玩家依序點選,變數存入點選順序的 index 字串(如 "312" 代表先選第 3、再選第 1、最後選第 2)。
@here:
[重建案發順序]:
prompt timeline [聽見槍聲, 燈光熄滅, 玻璃破碎] order
if timeline = "132":
show "正確!"
else:
show "再想想。"
線索系統
線索是「拿來想的」,和道具(拿來用的)不同。每條線索有兩個階段:發現(discover)和解開(resolve)。玩家執行 discover 動作後,線索才出現在線索欄;之後需要進一步探索才能 resolve。
發現線索(discover)
線索不會自動出現。創作者必須在互動動作裡明確寫 discover,才能讓玩家發現線索。
單一發現路徑:在 discover 指令裡直接定義線索(內聯定義):
thing sign "石板指示牌":
[仔細看]:
discover note "一張撕去一角的便條紙" (thread: culprit)
多個發現路徑:先宣告線索,再在多個地方用 discover 引用:
clue note "一張撕去一角的便條紙" (thread: culprit)
thing table "書桌":
[仔細搜查]:
discover note
thing chair "椅子底下":
[往下看]:
discover note
規則:
- 有獨立宣告時,
discover不能再附上定義(否則報錯:重複定義) - 無獨立宣告時,
discover必須附上定義(否則報錯:未定義) - 線索 ID 全域唯一,不可在不同地方定義兩次
線索宣告語法(多路徑用)
clue clue-id "線索描述"
clue clue-id "線索描述" (thread: thread-name)
clue clue-id "線索描述" (thread: thread-name, optional)
thread-name:選填,將線索歸入謎題線optional:標記後不影響謎題線的完成計算
謎題線進度顯示
謎題線面板的顯示行為由 engine 自動管理,創作者不需要額外設定:
| 狀態 | 觸發條件 | 顯示 |
|---|---|---|
| 不出現 | 該謎題線沒有任何線索被 discover | 面板不顯示此謎題線 |
| 仍有線索未發現 | 至少一個 required 線索已 discover,但有 required 線索尚未 discover | 謎題線標題旁顯示「仍有線索未發現」 |
| 仍有線索未解開 | 所有 required 線索已 discover,但有 required 線索尚未 resolve | 謎題線標題旁顯示「仍有線索未解開」 |
| 已解開 | 所有 required 線索已 resolve | 謎題線標題旁顯示「已解開」 |
Optional 線索不納入進度計算:標記 optional 的線索不影響上述任何狀態判斷,玩家找到是驚喜,沒找到不影響進度提示。
設計建議:required 線索的 discover 和 resolve 應放在不同互動點,讓玩家能經歷「仍有線索未解開」的狀態。若 discover 和 resolve 在同一個動作裡,玩家會直接跳過此中間狀態。
重複發現:若玩家重複觸發已發現過的線索的 discover 動作,系統不會重複記錄,但會顯示提示訊息「線索『xxx』已記錄過了。」讓玩家確認曾在此處發現線索。這個提示會夾在該互動前後的 show 訊息之間,能幫助玩家回憶線索的發現情境。
Thread 描述(frontmatter,選填)
---
title: 我的故事
threads:
culprit: "誰是兇手"
---
解開線索(resolve)
resolve clue-id
resolve clue-id = "字串值"
resolve clue-id = true
resolve clue-id = false
resolve clue-id = 42
省略值時預設為 true。false 也是合法的值,代表「答案是否定的」。
執行 resolve 時,引擎會自動在故事 log 插入一條「線索解開:XXX」的提示,樣式有別於 show 訊息。不需要另外用 show 提示玩家。
條件判斷
if has clue-id # 線索已被發現(執行過 discover)
if resolved clue-id # 線索已被解開(不管值為何)
if not resolved clue-id # 線索尚未解開
if resolved clue-id = "值" # 解開為特定值
if resolved thread-name # 謎題線內所有 required 線索都已解開
完整範例
---
title: 我的故事
threads:
culprit: "誰是兇手"
---
## 書房
桌上的[便條紙](note)有些皺摺。
thing table "書桌":
[翻看桌面]:
discover note "一張撕去一角的便條紙" (thread: culprit)
show "字跡潦草,只看得出是左撇子寫的。"
## 審訊室
@here:
if has note:
[嫌疑人是左撇子]:
resolve note = "左撇子"
[字跡看不出端倪]:
resolve note = false
if resolved culprit:
[宣布結論] -> 結局
ID 規則
- 線索 ID 全域唯一,不可重複定義(含宣告與內聯定義)
- 同一場景內,所有 thing/item/clue ID 也不可重複
道具數量
道具不支援堆疊——同一個 ID 在背包只能有一個。需要追蹤數量請用數值變數:
set gold += 1 ← 正確:用變數追蹤數量
give gold-coin ← 避免:無法堆疊
5. 結束故事
用 end 命令標記故事結局。觸發後,玩家會看到結束畫面,並出現「重新開始」按鈕和評分介面。
故事至少需要一個 end,否則 parser 會發出警告(仍可存檔,但玩家無法看到結局畫面)。場景若沒有任何離開方式(無跳轉連結、也無 end),parser 同樣會警告。
最簡單的用法,在腳本區塊頂層寫 end,玩家到達時立即結束:
## 結局
你找到了出口,逃出了密室。陽光灑在臉上。
end
也可以把 end 放在選項的動作裡,讓玩家先看到訊息再結束:
## 最終對決
@here:
if has_sword and has_shield:
[迎戰魔王]:
show "你成功擊敗了魔王,拯救了王國!"
end
[逃跑] -> 逃跑路線
多結局的常見寫法:
## 尾聲
@here:
if good_deeds >= 3:
[光明結局] -> 好結局
else if evidence_found:
[真相結局] -> 真相結局
else:
[普通結局] -> 普通結局
## 好結局
你做出了正確的選擇。世界因你而改變。
end
## 真相結局
真相終於大白,但代價不小。
end
## 普通結局
一切回到原點,彷彿什麼都沒發生過。
end
6. 跨集存檔
如果你的故事分成多集,可以讓 EP1 的選擇影響 EP2 的劇情。EP1 結束時儲存指定的變數,EP2 開場時讀取還原。
EP1:儲存變數
在 end 之前加 save,指定要傳到下一集的變數名稱:
## 最終結局
你做出了選擇。
@here:
[結束]:
save mystery-manor-ep1-save [player_choice, found_key]
end
mystery-manor-ep1-save是存檔識別碼,慣例為{slug}-save,發佈後不要更改- 方括號裡列出要儲存的變數名稱(以逗號分隔)
- 同一個故事可以有多個
save(對應不同結局),每次觸發都會新增一筆存檔
EP2:讀取前集存檔
在第一個場景加上 @load 區塊,故事開始時會自動顯示讀取視窗:
---
title: Mystery Manor EP2
---
## 序章
你記得那個抉擇——或者,你對它一無所知。
@load mystery-manor-ep1-save [player_choice, found_key]:
if _loaded:
if player_choice:
show "記憶湧上來。你記得自己當時的選擇。"
else:
show "你對那段往事的記憶模糊不清。"
else:
show "那段往事的記憶消散了,你只能繼續向前。"
@here:
[繼續前行] -> 第一關
@load只能放在第一個場景,故事開始時自動觸發一次,之後不再觸發- 方括號裡列出要從存檔讀取的變數名稱(需與
save裡的列表一致);只有列出的變數會被載入,其餘忽略 - 讀取完成後,
_loaded會自動設為true(有存檔)或false(跳過) - 讀取成功時,系統會自動在訊息區顯示「已讀取上集探索記錄」提示,不需要在故事裡另加
show @load底下的指令在讀取視窗關閉後執行
封鎖入場
若前集存檔是必要的,在存檔識別碼後加上 required,沒有存檔的登入玩家將在入場時被攔截:
@load mystery-manor-ep1-save [player_choice, found_key] required:
show "記憶還在。你記得上次發生的事。"
存檔行為
| 已登入 | 未登入 | |
|---|---|---|
save |
自動儲存到帳號(最多 10 筆) | 顯示存檔碼,請玩家自行複製保存 |
@load |
顯示存檔列表供選擇 | 顯示輸入框,玩家貼上存檔碼 |
7. 場景圖片
標記需要圖片的場景
在場景標題下方加上提示詞:
## 廢棄工廠
{img: "abandoned factory, rusty machinery, dim light, realistic"}
你走進一間廢棄已久的工廠...
或用 auto 讓系統以場景名稱自動生成:
## 神秘森林
{img: auto}
同一部故事裡,如果某個場景需要不同風格,可以在 {img} 裡加 style:,會取代 frontmatter 的全局 style:
## 序章
{img: "山丘小徑,晨霧,清晨陽光", style: "寫實水彩,柔光"}
## 古宅大廳
{img: "宏偉廳堂,燭光昏黃,塵封的地毯"}
圖片比例: 16:9 為佳,上傳其他比例會自動裁切。
上傳圖片(推薦方式)
- 用任何工具生成圖片(ChatGPT、Gemini、Ideogram、Canva)
- 儲存為圖片檔案(上傳上限 10MB;寬度超過 1536px 會自動縮小,並轉存為 JPEG)
- 在編輯頁面的「場景圖片」區塊,點擊場景旁的「上傳圖片」
AI 自動批次生成(需 API Key)
在帳號設定頁填入 API Key 後,編輯頁面會出現「生成缺少的圖片」按鈕,可一次生成所有標記了 {img:} 的場景圖片。
| 服務 | provider 值 |
API Key 來源 |
|---|---|---|
| Google Gemini | gemini |
aistudio.google.com |
| OpenAI DALL-E 3 | dall-e-3 |
platform.openai.com |
| Stability AI | stable-diffusion |
platform.stability.ai |
在 frontmatter 指定服務與風格:
---
title: 我的故事
ai_image:
provider: gemini
style: "soft watercolor illustration, warm light"
---
不指定 provider 時,系統依照已填入的 Key 自動選擇(順序:Gemini → OpenAI → Stability AI)。
提示詞技巧:
- ✅ 具體:「古老的圖書館,高聳的書架,暖黃燈光,油畫風格」
- ✅ 指定風格:「水彩插畫」「像素藝術」「電影感寫實」
- ❌ 太抽象:「美麗的地方」「神秘的感覺」
8. 設計建議
給選項真正的意義:
@here:
[正面迎戰] -> 戰鬥 ← 高風險高回報
[嘗試談判] -> 談判 ← 需要滿足條件
[轉身離開] -> 逃跑 ← 安全但放棄機會
盡量避免「假選擇」——三個選項只有一個有意義的設計,玩家很快就會感覺到。
一個場景一個焦點:
不要把所有東西塞進同一個場景。讓玩家用選項主動「靠近檢查」,而不是在一個場景裡塞滿十個物件。
加有趣的 fallback 訊息:
玩家一定會亂試道具。與其讓他們看到無聲無息的「沒有反應」,不如給每個事物一個有個性的 fallback 訊息——這本身就是敘事的一部分。
「n選1、不可回頭」的選擇:
大多數選項玩家可以重試,但有些時刻需要讓玩家做出真正的、一次性的選擇——例如內心的表態、對某件事的最終看法。這類選項可以在出現前用 show "..." pause 製造一個停頓節點:玩家主動按繼續後,才看到選項。訊息內容加上這個停頓本身,就在告訴讀者:這一刻不能隨便點選。
[你怎麼看這件事]:
show "你站在那裡,心裡把幾個答案都轉了一遍──" pause
[「這是對的選擇」]:
...
[「我不後悔,但我知道代價」]:
...
[「說不清楚。只是走到了這一步」]:
...
發佈前測試清單:
- 所有場景都能抵達
- 沒有意外的死胡同(除非故意設計)
- 所有結局都可以達到
- 道具 ID 和引用名稱一致
- 試過至少兩條不同路線
9. 語法速查
Frontmatter
---
title: 故事標題
description: 故事簡介(顯示在列表頁)
language: zh-TW # 支援:zh-TW, en, ja, ko
tags: [解謎, 恐怖, 短篇]
fallback: "這樣做沒有任何效果。"
variables:
health: 100
gold: 0
has_key: false
ai_image:
provider: gemini
style: "watercolor illustration"
threads:
entry-method: 入侵手法
timeline: 時間線
---
場景定義
## 主場景名稱
### 子場景名稱
場景描述格式
**粗體文字**
*斜體文字*
> 引言文字
場景選項(@here)
@here:
[跳到場景] -> 目標場景
[同場景動作]:
show "訊息文字"
[動作加訊息] show "訊息文字"
end
if condition:
[條件選項] -> 場景
else if other_condition:
[另一選項]:
show "訊息文字"
else:
[預設選項] -> 場景
指令(選項底下縮排)
show "訊息文字"
show "訊息文字" pause ← 顯示後暫停,等讀者點「繼續」再執行後續指令
pause ← 不顯示新文字,直接插入暫停點
set variable = value
set variable += 5
set variable -= 5
random var_name 1 6
prompt variable_name
prompt variable_name [選項1, 選項2, 選項3]
prompt variable_name [選項1, 選項2, 選項3] single
prompt variable_name [選項1, 選項2, 選項3] multiple
prompt variable_name [選項1, 選項2, 選項3] order
discover clue-id "描述" (thread: thread-id)
discover clue-id
resolve clue-id
resolve clue-id = value
give item-id "顯示名稱"
remove item-id
save save-key [var1, var2]
end
跨集存檔
@load save-key [var1, var2] ← 故事開始自動觸發讀取視窗(可跳過)
@load save-key [var1, var2]: ← 加上執行邏輯時用冒號+縮排
if _loaded:
show "有前集存檔時顯示"
else:
show "無存檔或跳過時顯示"
@load save-key [var1, var2] required ← 無存檔的登入玩家直接被攔截
save save-key [var1, var2] ← 儲存指定變數到跨集存檔
實體與道具
thing entity-id "描述文字" ← 場景事物(無互動行為)
thing entity-id "描述文字": ← 場景事物(有互動行為)
[自訂動作] show "訊息"
fallback "使用無效道具時的訊息"
use other-item-id: ← item-on-scene-object
show "互動訊息"
item entity-id "描述文字" ← 道具(無互動行為)
item entity-id "描述文字": ← 道具(有互動行為)
apply other-item-id: ← item-on-item
show "互動訊息"
[繼續] -> 場景
remove other-item-id
clue clue-id "描述文字" ← 線索(無分類)
clue clue-id "描述文字" (thread: thread-id) ← 線索(歸屬分類)
clue clue-id "描述文字" (thread: thread-id, optional) ← 選填線索
@self: ← 自用道具的目標
use potion:
remove potion
show "效果描述"
場景文字中的條件
{if condition}顯示文字{/if}
{if condition}A{else}B{/if}
{variable_name}
條件語法
if has_key ← 布林值為 true
if not has_key ← 布林值為 false
if has item-id ← 道具目前在背包
if taken item-id ← 道具曾經被撿起(即使已消耗也為 true)
if gold >= 100 ← 數值比較
if name = "勇者" ← 字串比較(不分大小寫)
if name includes "龍" ← 字串包含(不分大小寫)
if a and b
if a or b
else if condition
else