VNLEWIKI创作故事,连接世界。
简体中文
VNLE 使用手册

流程规则与游戏动作

← 接入教程与引擎

这些规则用于帮助你编写脚本或使用社区接入方案。JSON 包含数据,而非可执行源码。编辑器中的布局、选择状态和内部界面排列不会控制游戏。

在第一步之前

检查 format = vnle.story、containerVersion = 1,以及受支持的 execution.contractVersion 和 requiredCapabilities。示例使用 1.1,能力为 core.v1 和 substory.call-return.v1。JSON Schema 检查结构;执行时还需要检查引用、类型和命令是否受支持。遇到未知节点类型时,不能默默跳过。

字段参考 · JSON Schema

各节点的作用

kind行为
dialogue显示文本,等待 Continue,然后按 continuation 继续。
choice按 optionOrder 排列选项,以 option.condition 筛选,将选中选项的 effects 应用一次,然后按 continuation 继续。没有可用选项时遵循 whenEmpty。
branch判断 condition,然后进入 whenTrue 或 whenFalse。
action按顺序执行 effects,然后按 continuation 继续。
jump跳转到目标入口,不新增返回点。
call记住 continuation,然后进入目标入口。
return恢复最近保存的 continuation。没有对应 Call 的 Return 属于错误。
command计算参数,请求游戏动作,等待结果,然后进入匹配的结果分支。
end结束当前整段对话,即使当前位于子故事内。Return 则会回到调用方。

条件与带类型的值

操作数可以是 literal(带类型的固定值),也可以是 variable(通过 variableRef 引用变量)。compare 支持 eq、ne、lt、lte、gt、gte。all 要求所有子条件成立,any 要求至少一个成立,not 对子条件取反。请勿将布尔值与字符串 "true"/"false" 混淆。

变量操作包括 set、add 和 subtract。add/subtract 使用 −2147483648 至 2147483647 范围内的整数。按顺序计算操作,但若任何操作无效,不得只提交一部分更新。不要在每次对话开始时用初始 defaultValue 覆盖已保存的值。

Jump · 购买后前往仓库

跳转

已购买钥匙时,直接跳转到 Warehouse 入口。target 包含 flowRef 和 entryRef;execution.entries[entryRef].nodeRef 给出下一个节点。Jump 不会自动打开引擎场景或地图,这需要通过相应的游戏动作实现。

{
  "flowRef": "1f69a7b6-1bdc-4dff-b8ee-9b2819b35168",
  "id": "2e1710dc-c32f-418f-abca-e055d6fd28f3",
  "kind": "jump",
  "target": {
    "entryRef": "a4dd48dc-b0e2-47fc-b102-def52583a8fe",
    "flowRef": "2a0a259c-b76a-4de0-8cee-567713c2d320"
  }
}
Call Substory / Return · 米拉介绍仓库

调用子故事/返回

是的,这段流程由 JSON 描述。问候之后,故事会调用锁着的仓库的说明。游戏将 continuation 压入栈,然后进入 target。米拉说明完毕后到达 return,取出最近保存的 continuation,并执行 Already have the key?。嵌套调用遵循相同规则。

{
  "continuation": {
    "kind": "node",
    "nodeRef": "95e9a55f-f6b1-4696-9c52-6ea3ea2ac7e5"
  },
  "flowRef": "1f69a7b6-1bdc-4dff-b8ee-9b2819b35168",
  "id": "85b3aadf-2b95-4150-bc44-c0c796337874",
  "kind": "call",
  "target": {
    "entryRef": "2bac8555-3cae-4a21-9b8c-b410ab207ebb",
    "flowRef": "ea13b52e-092a-44ff-b087-0ce65d040c9a"
  }
}
{
  "flowRef": "ea13b52e-092a-44ff-b087-0ce65d040c9a",
  "id": "f8b8673a-cb86-40ae-a4f6-d37a5c1563c3",
  "kind": "return"
}

相关节点全部包含在导出的 story.json 中。Call 并不表示加载另一个 JSON 文件或脚本。maxCallDepth 限制嵌套深度。对空栈执行 Return 属于错误;End 则会结束整段对话并清空栈。

Command · 打开商店或给予钥匙

命令

“打开商店”是一个合适的例子:游戏打开自己的商店界面并暂停对话,只有商店界面关闭后才报告约定的结果,例如 purchased 或 cancelled。请在该 VNLE 项目中将这些名称和参数定义为游戏动作;open_shop 不是 VNLE 内置命令。

实际的港口导出数据使用 give_item:命令引用一个定义,提供物品 ID,并包含两个结果。游戏将钥匙加入背包,或拒绝接收。作者已经为这两种结果分别定义了后续路径。

{
  "arguments": {
    "3cac6185-4404-4a2a-be5f-14afa0f97137": {
      "kind": "literal",
      "value": {
        "entityType": "item",
        "type": "reference",
        "value": "2d71e57e-9b58-4fdb-a33f-42b89bd8bf04"
      }
    }
  },
  "commandRef": "0723beb0-daba-4602-a665-4efc27511bc3",
  "flowRef": "1f69a7b6-1bdc-4dff-b8ee-9b2819b35168",
  "id": "500c28a1-ed85-4c36-9a38-bedacce1c6b7",
  "kind": "command",
  "outcomes": {
    "12bc86a9-b2eb-4eb4-836c-a9efd936bf1b": {
      "continuation": {
        "kind": "node",
        "nodeRef": "382640aa-5b50-4715-9148-b470bc2f7cd6"
      },
      "resultBindings": {}
    },
    "1c527580-69a6-4e99-8503-1b0de3fc8e78": {
      "continuation": {
        "kind": "node",
        "nodeRef": "221d6b0c-99e1-45a8-bc73-768c4ee679eb"
      },
      "resultBindings": {}
    }
  }
}
{
  "id": "0723beb0-daba-4602-a665-4efc27511bc3",
  "key": "give_item",
  "name": "Give item",
  "category": "custom",
  "inputs": [
    {
      "id": "3cac6185-4404-4a2a-be5f-14afa0f97137",
      "name": "Item",
      "valueType": {
        "entityType": "item",
        "kind": "reference"
      }
    }
  ],
  "outcomes": [
    {
      "id": "12bc86a9-b2eb-4eb4-836c-a9efd936bf1b",
      "name": "Received",
      "results": []
    },
    {
      "id": "1c527580-69a6-4e99-8503-1b0de3fc8e78",
      "name": "Declined",
      "results": []
    }
  ]
}
  1. 在 execution.commands 中通过 commandRef 查找命令签名,在 content.commandDefinitions 中查找可读的键。
  2. 通过输入 ID 计算参数。缺少可选参数时,使用其声明的 defaultValue。
  3. 仅请求一次明确受支持的游戏动作。等待期间,禁止 Continue 或重复执行。
  4. 验证结果 ID 和带类型的结果值。outcome.resultBindings 将结果值赋给故事变量,然后按 outcome.continuation 继续。
  5. 技术故障不等同于作者定义的取消结果。请显示错误,并在游戏中提供受控的重试或取消路径。

在此项目中,如果背包拒绝接收钥匙,后续流程会退还五枚金币。这由导出的 continuation 实现,并非每个 Command 都会自动退款。背包、任务日志和引擎地图属于你的游戏系统,仅有 JSON 内容记录不会自动创建这些系统。

占位符 · “你还剩 5 枚金币”

文本和占位符

{
  "continuation": {
    "kind": "node",
    "nodeRef": "2e1710dc-c32f-418f-abca-e055d6fd28f3"
  },
  "flowRef": "1f69a7b6-1bdc-4dff-b8ee-9b2819b35168",
  "id": "0d7f959e-8fba-4882-ae98-b66d8416d4d2",
  "kind": "dialogue",
  "speakerRef": "807b2957-7bab-4a28-83a4-1f3d1589bbc2",
  "text": {
    "bindings": {
      "1084cf8e-515e-4b33-92f8-b5c260e88a9e": {
        "kind": "variable",
        "variableRef": "a547610e-661f-4b8d-a25b-21d042f1a2d5"
      }
    },
    "textRef": "fcc775ff-558e-4a41-823d-183aaff03e4b"
  }
}

直接拼接 kind: text 片段。对于 kind: placeholder,通过 placeholderRef 查找文本记录中的定义和 node.text.bindings 中的绑定。绑定提供固定值或变量值。在显示该句时获取值,并按类型格式化。类型和格式选项见字段参考。

PlaceholderDefinition · 消息

进度、限制与错误

将当前位置、故事变量、返回栈和待完成的游戏动作与游戏状态一起保存。语言是独立的游戏设置。storyBuildId 用于将存档与对应的执行版本关联;对于不兼容的故事版本,不要猜测后续路径。

maxAutomaticTransitions、maxConditionDepth、maxConditionTerms 和 maxCallDepth 限制自动执行或深度嵌套的流程。缺失引用、whenEmpty: fault 且没有可用选项、未知命令和无效类型都需要明确报错。重新绘制界面绝不能重复购买或重复修改变量。

交给开发者或 AI 接入

提供实际导出文件、引擎版本、目标平台、这些流程规则和对应引擎文档。说明界面元素及现有背包或商店功能。要求对方使用实际的 JSON 字段和有文档依据的引擎 API,并测试以下情况。

  1. 十枚金币:获得钥匙,剩五枚金币;两枚金币:不获得钥匙,也不扣钱。
  2. 背包拒绝接收:按故事流程退款,不得假报成功。
  3. Call/Return 回到预定节点;Call 内的 End 结束整段对话。
  4. 切换语言后保留进度;缺失译文和有意留空的文本均能正确处理。
  5. 随游戏发布的 JSON、图片和字体在开发目录之外也能使用;图片缺失时不会继续显示上一张立绘。