Taplot 写完分支之后怎么验——观众会不会走到死胡同?

文章导读
能验,但要换一种验法:把分支表从剧本文字导成带 ID 的路径清单,再用脚本把从开场到每个终点的走向全部枚举出来,然后对着结果看断头路、走不到的节点和重复终点。人工点几遍只能覆盖主线和顺手点到的支线,嵌套分支和回头选项很容易漏。脚本枚举不判断台词写得好不好,它只保证结构上每条路径都落得到终点。
📋 目录
  1. Ⅰ 把分支表整理成带 ID 的路径清单
  2. Ⅱ 写一段通用遍历骨架,枚举从开场到每个终点的全部路径
  3. Ⅲ 检查三类问题:断头路、不可达节点、重复终点
  4. Ⅳ 把枚举结果和时长对一遍
  5. Ⅴ 改完剧本再跑一次脚本,比对前后差异
A A

能验,但要换一种验法:把分支表从剧本文字导成带 ID 的路径清单,再用脚本把从开场到每个终点的走向全部枚举出来,然后对着结果看断头路、走不到的节点和重复终点。人工点几遍只能覆盖主线和顺手点到的支线,嵌套分支和回头选项很容易漏。脚本枚举不判断台词写得好不好,它只保证结构上每条路径都落得到终点。

判断标准可以落到三条:分支表能导出“节点ID + 选项文本 + 去向ID + 是否终点”,遍历脚本能枚举出全部路径,并且每条路径都以标记过的终点结束。适用场景是分支超过一屏、有嵌套或回头选项的剧本。做法是先导表、再遍历、最后拿路径时长做一次粗筛。边界也要说清:脚本只验结构,不验选项是否吸引人、情绪节奏是否成立,这些仍然要人工看。

把分支表整理成带 ID 的路径清单

剧本里的分支通常写成缩进段落或大纲,人看得懂,脚本读不了。第一步是把每条选项拆成一行记录,只保留四个字段,不要在这一步混入演出描述。

节点ID选项文本去向ID是否终点
start推门进去hall否
hall往左走left_room否
hall往右走side_path否
left_room回头hall否
side_path(无选项)—是

字段含义:节点ID 是这一行选项所在的节点,同一节点可以有多行;选项文本是观众看到的按钮文案,留空表示该节点没有可选项;去向ID 是点下去之后到达的节点;是否终点标在“去向ID”这个节点上,表示走到它就是一次正常结束。

终点节点一定要单独留一行、选项文本留空、是否终点写“是”。否则脚本无法区分“设计好的结尾”和“忘了写后续”,这两种情况在数据里长得一模一样。整理完导成 CSV 或 JSON 都行,只要列名固定,后面脚本不用改。

写一段通用遍历骨架,枚举从开场到每个终点的全部路径

下面这段不依赖任何平台接口,只吃上面那张表。用栈或递归都行,这里用递归,把当前走过的节点集合带上,遇到回环就停,避免无限深入。

import json

rows = json.load(open('branches.json', encoding='utf-8'))
START = 'start'
ENDS = {r['targetId'] for r in rows if r['isEnd']}   # 被标成终点的节点

out = {}
for r in rows:
    if r['optionText']:
        out.setdefault(r['nodeId'], []).append(r)

paths = []

def walk(node, path, seen):
    if node in ENDS:
        paths.append(path + [node + ' (终点)'])
        return
    nexts = out.get(node, [])
    if not nexts:
        paths.append(path + [node + ' (断头)'])
        return
    for r in nexts:
        step = node + ' -[' + r['optionText'] + ']-> ' + r['targetId']
        if r['targetId'] not in out and r['targetId'] not in ENDS:
            paths.append(path + [step + ' (去向缺失)'])
            continue
        if r['targetId'] in seen:
            paths.append(path + [step + ' (回环)'])
            continue
        walk(r['targetId'], path + [step], seen | {node})

walk(START, [], set())
for i, p in enumerate(paths, 1):
    print(i, ' | '.join(p))
print('路径总数:', len(paths))

把 branches.json 放在同一目录直接跑,输出大概是这种形态:

1 start -[推门进去]-> hall | hall -[往左走]-> left_room | left_room -[回头]-> hall (回环)
2 start -[推门进去]-> hall | hall -[往左走]-> left_room
3 start -[推门进去]-> hall | hall -[往右走]-> side_path (终点)
路径总数: 3

注意两处替换项:START 改成自己剧本的开场节点 ID;回环的判断按“当前路径”算,不要用全局已访问集合,否则第二条从同一节点出发的合法路径会被误判成回环而漏掉。

检查三类问题:断头路、不可达节点、重复终点

  • 断头路:节点有选项但去向ID为空或表里查不到;或者节点没有任何选项,却没被标成终点。前者是数据缺了,后者是剧本忘写结尾。
  • 不可达节点:节点ID出现在表里,但在遍历输出的所有路径中一次都没经过。常见原因是某个选项被改过去向,旧节点留在了表里。
  • 重复终点:多个终点节点的选项文本、去向和结尾描述完全一致,本质是同一个结尾占了两份,改一处容易漏另一处。

输出时给每类固定前缀,方便直接搜索和贴回工单:

Taplot 写完分支之后怎么验——观众会不会走到死胡同?
[断头] node=hall 去向ID为空 出现在路径: start -[推门进去]-> hall
[不可达] node=old_room 被 1 条选项指向,0 条路径经过
[重复终点] end_a / end_b 选项文本与去向完全一致

断头路按条数看,不可达按“被指向次数 / 实际经过次数”看,重复终点按组看。三类问题不多的时候不要急着改,先把清单发给写剧本的人确认哪条是设计意图——有些看似断头的节点其实是留待续写的占位。

把枚举结果和时长对一遍

结构没错不代表体验没错。在表里补一列可选字段 durationSec(节点停留时长),没有就先估一个区间中值,标明是粗估,别拿它当验收数据。

汇总方式是按遍历输出的每条路径,把沿途节点的 durationSec 加起来,同时记节点数。输出一张小表:路径编号、节点数、总时长、选项链。然后看两头:节点数和总时长明显偏短的路径,往往是一路点到终点、中间没有可停留的内容;明显偏长且节点数多的,要确认观众是否有耐心走完。

偏短偏长的界限不要硬套统一阈值,按自己剧本的正常区间定一个占位值就行,比如单次体验期望 40 到 90 秒,那低于 40 秒的路径先标出来人工看。这一步只做粗筛,不做结论。

改完剧本再跑一次脚本,比对前后差异

改完不重跑,等于没验。复跑方式就是原封不动再执行一次遍历脚本,把输出重定向到新文件,和上一版并排比。要比对的字段建议固定成这几项:

  • 路径总数、终点节点数、断头条数、不可达节点数;
  • 每条路径的节点数和选项链,按选项链做文本比对,找出被删掉或被合并的路径;
  • 新增的终点是否在旧清单里出现过,避免改一处多出一个重复结尾。

验证标准有两条:一是枚举出的路径条数与分支表里选项的展开关系一致,也就是每个节点的每个选项都在某条路径里出现过一次;二是每条路径都能落到被标成终点的节点上,输出里不出现“断头”和“去向缺失”。

还有一处边界要提前认下:带回头选项的剧本,路径条数会随回环截断规则变化,脚本截断了不代表这条回环不该存在。这类路径要单独列出来人工确认是不是设计意图,不要直接当成缺陷删掉。