好感度到了,他才会邀请你留下
雨越下越大,你走进一家快打烊的旧书店。林澈给你让出一把椅子,没再多说什么。
你帮他把窗边淋湿的书搬开,又听他讲起第一次来这里躲雨的夜晚。等他翻过门上的营业牌,忽然回过头:
“我还有一盒旧信没整理。要是你不急着走……陪我一会儿?”
我们要做的,就是让这句话因为你们刚才的相处而出现。不是进门就能选的固定分支,也不是让模型凭感觉宣布“好感度上升”。
读者会看到什么
Section titled “读者会看到什么”教学演示:实际 StoryOrbit 阅读器界面,配合预写文案和预设的行动识别结果;状态从下载稿的初始值,经真实运行时规则计算。这里没有调用模型,不能据此判断模型识别或生成质量。点击图片可放大。
01 · 还只是来躲雨
林澈 · 好感度 0
02 · 他开始愿意多说一点
林澈 · 好感度 2
03 · 闭店后的那盏灯
旧信剧情已解锁
这是一段可以继续写下去的相处。解锁旧信,并不意味着立刻恋爱或自动进入结局。林澈只是愿意让读者走近一点;接下来的回应仍由读者决定。
先写出值得留下的开场
Section titled “先写出值得留下的开场”解压后打开 pack.json,先看 entries[0].opening,暂时不用读完全部字段。
开场只有三段:读者推门进来,林澈让出椅子,窗缝的水滴在书上。干布就在手边,他却腾不出手。这就给了读者一个能自然做出的行动。
试着把最后一段换成你的细节:是一本夹着车票的诗集,还是封皮已经卷边的地图册?保留“书正在被雨打湿”和“读者可以帮忙”这两件事,下面的规则就仍然有用。
让他记得这两件小事
Section titled “让他记得这两件小事”这个短篇只用 0 → 1 → 2 三档好感。runtime.state 声明可记录的值,defaults 让每次新故事都从陌生开始:
{
"affinity": {
"type": "integer",
"minimum": 0,
"maximum": 2
},
"defaults": {
"affinity": 0,
"helped": false,
"listened": false,
"invited": false,
"letters_open": false
}
}
上面摘出了状态定义中的 affinity 和初始值;完整文件还声明了四个布尔字段。
helped:读者已经帮忙收好湿书。listened:读者已经听过并回应了那段往事。invited:林澈已经说出了邀请。letters_open:读者已经答应留下,旧信场景可以出现。
“我会帮你”不等于书已经收好了。 observations 中的问题负责辨认本轮真正发生的事;rules 再根据辨认结果更新状态。这里的 saved_books 问的是已完成的收书或擦书,排除了计划、引用和回忆。
收书的奖励规则是:
{
"kind": "update",
"id": "remember_helped",
"when": {
"op": "all",
"conditions": [
{
"op": "eq",
"path": "/helped",
"value": false
},
{
"op": "observed",
"id": "saved_books",
"value": true
}
]
},
"effects": [
{
"op": "set",
"path": "/helped",
"value": true
},
{
"op": "increment",
"path": "/affinity",
"by": 1,
"limit": 2
}
]
}
读一遍就能看出它的意思:尚未记过这件事,而且本轮确实收好了书,才标记 helped 并加一分。再收一次同样的书,不会重复加分。听往事的规则采用相同写法。
自然语言的辨认仍由模型完成,所以要试玩不同说法。规则能保证“一件事只加一次”,不能保证每一句含糊的话都被理解成你预想的意思。
好感够了,先给一个邀请
Section titled “好感够了,先给一个邀请”好感到 2,并不替读者答应留下。offer_invitation 只让林澈发出邀请,并等待回答:
{
"kind": "guidance",
"id": "offer_invitation",
"when": {
"op": "all",
"conditions": [
{
"op": "gte",
"path": "/affinity",
"value": 2
},
{
"op": "eq",
"path": "/invited",
"value": false
}
]
},
"instruction": "林澈已经开始信任读者。让他在对话中明确邀请读者闭店后留下,一起整理一盒旧信,然后等待回答。不要打开盒子,不要替读者答应。",
"completion": {
"op": "observed",
"id": "offered_invitation",
"value": true
}
}
这里的 completion 用于检查这轮是否真的出现了邀请。随后 remember_invitation 记住它;只有已经收到邀请、又明确答应,unlock_letters 才会把 letters_open 改为 true。
条件读取的是一轮开始时的状态。如果听往事的这一轮才把好感加到 2,邀请会在后续一轮出现;答应邀请以后,旧信内容也从后续一轮开始可用。教程里的这几个停顿,正好给人物和读者留出接话的空间。
把真正的秘密放在门后
Section titled “把真正的秘密放在门后”不要把第一封信的内容直接写进全局 instructions。那样即使剧情未解锁,模型也已经知道了它。
下载稿把这一幕放在 content 中,用条件控制是否提供给模型:
{
"id": "after_hours_letters",
"selection": "always",
"when": {
"op": "eq",
"path": "/letters_open",
"value": true
}
}
同一个内容块的 text 写着铁盒、第一只信封和林澈没能寄出的那句话。selection: "always" 表示通过 when 后这段内容必定进入创作上下文;它不会绕过条件,也不是把固定台词原样贴给读者。
这个门控制的是创作上下文。你还要在试玩时留意模型是否自行编造了相似情节,以及邀请是不是来得太突然。
现在换成你的故事
Section titled “现在换成你的故事”先只改人物和场景,保留两件具体的小事、一次邀请和一个隐藏片段。比如:帮修乐器、听完巡演失利的往事,才获得排练结束后听一首未发表作品的邀请。
然后沿着三条路线试玩:
| 你做什么 | 应该发生什么 |
|---|---|
| 刚进门就要求看旧信 | 旧信内容不会进入模型上下文 |
| 连续帮忙收书,但不听往事 | 好感停在 1,不重复刷分 |
| 收书、听往事,收到邀请后答应 | 好感到 2;答应以后,旧信场景可用 |
如果你把门槛改成 3,也要设计第三件能加好感的事,并调整状态上限和规则中的 limit。只把数字改大,会留下永远打不开的门。
用哪个 Pack 版本
Section titled “用哪个 Pack 版本”这个完整例子用 0.3 就能做到。 好感度、一次性事件和条件内容不需要视觉小说协议。
| 想继续加什么 | 版本与下一步 |
|---|---|
| 给“第一次被邀请留下”一个可收集的纪念 | 0.4 的成就与徽章;看成就和结局练习。不要把“获得成就”误写成自动结束故事。 |
| 把熄灯后的书店和林澈的立绘摆上舞台 | 0.5 的视觉小说呈现;看舞台练习。好感规则仍沿用这个状态系统。 |
| 接手一份旧 0.2 作品 | 先看迁移练习,再把原来的角色变量和分支映射到新的状态与内容。 |
表演资源的具体写法以当前 0.5 字段参考为准。
检查你改过的文件
Section titled “检查你改过的文件”下载包附带 Python 3.11+ 的结构校验器。在解压目录运行:
python3 -m venv .venv.venv/bin/python -m pip install -r requirements.txt.venv/bin/python check.py pack.jsonWindows PowerShell 的对应命令在包内 README.md。显示 Structural check passed 后,再在兼容的阅读器中试玩上述三条路线。结构通过,和故事是否自然,是两件都要确认的事。
查字段时,从 JSON 树展开 runtime 或 content,就能顺着例子的层次找到约束。准备交给别人阅读时,再看私有试玩与发布。