如何做多结局
多结局是视觉小说的核心吸引力。本章展示几种常见的多结局设计模式。
结局路由模式
最简单的模式——用一个变量路由到不同结局:
dsl
define "story.path" "" once
label chapter1_choice:
menu "你选择哪条路?"
"光明之路" -> path_light
"黑暗之路" -> path_dark
label path_light:
set "story.path" "light"
navigate "chapter2_light"
label path_dark:
set "story.path" "dark"
navigate "chapter2_dark"
// 最终章
label finale:
if {story.path == "light"}
navigate "ending_light"
else
navigate "ending_dark"多变量综合判定
用多个变量综合计算结局:
dsl
define "player.morality" 0 once // 道德值
define "player.power" 0 once // 力量值
define "player.relationships" 0 once // 关系值
label finale:
if {player.morality >= 80 && player.power >= 50}
navigate "ending_hero" // 英雄结局
else if {player.morality <= 20 && player.power >= 80}
navigate "ending_tyrant" // 暴君结局
else if {player.relationships >= 80}
navigate "ending_peace" // 和平结局
else if {player.morality <= 20}
navigate "ending_villain" // 反派结局
else
navigate "ending_neutral" // 中性结局结局解锁记录
用 gallery_unlock 记录已解锁的结局,同时同步自定义变量供 DSL 检查:
dsl
define "endings.hero" false once
define "endings.tyrant" false once
label ending_hero:
gallery_unlock "ending_hero" title="英雄结局"
set "endings.hero" true
// 显示结局画面
navigate "ending_menu"
label ending_tyrant:
gallery_unlock "ending_tyrant" title="暴君结局"
set "endings.tyrant" true
// 显示结局画面
navigate "ending_menu"为什么需要自定义变量
gallery_unlock 将 CG 记入 List<GalleryEntry>(状态键 __gallery_unlocked),DSL 表达式无法直接遍历列表检查某项是否存在。所以需要同步一个布尔变量供 if 条件使用。C# 侧可用 gameController.IsGalleryUnlocked("ending_hero") 直接检查。
然后在结局列表场景中根据解锁状态显示:
dsl
scene "endings_list" type=menu
text "结局列表" x=50% y=10% size=32 halign=center
if {endings.hero}
button "英雄结局(已解锁)" x=50% y=30% nav="ending_hero_review" color="#FFD700"
else
text "??? (未解锁)" x=50% y=30% size=16 color="#666666" halign=center
if {endings.tyrant}
button "暴君结局(已解锁)" x=50% y=40% nav="ending_tyrant_review" color="#FF4444"
else
text "??? (未解锁)" x=50% y=40% size=16 color="#666666" halign=center结局解锁持久化
gallery_unlock 的记录会持久化到全局存档,即使玩家重新开始游戏,已解锁的结局仍然可见。自定义变量需自行持久化或重新计算。
隐藏结局
某些结局需要满足隐藏条件:
dsl
label secret_check:
// 需要同时满足多个隐藏条件
if {story.met_sage && story.found_sword && story.purity >= 90}
navigate "ending_secret"
else
navigate "ending_normal"True End 模式
完成所有结局后解锁 True End:
dsl
define "endings.peace" false once
define "endings.villain" false once
label check_true_end:
if {endings.hero && endings.tyrant && endings.peace && endings.villain}
navigate "true_end_unlock"
else
navigate "ending_menu"
label true_end_unlock:
say "你已看遍所有可能的结局。" speaker="系统"
say "现在,真正的故事开始了..." speaker="???"
navigate "true_end_chapter"结局统计
在结局画面显示统计信息:
dsl
scene "ending_hero" type=menu
set "story.ending" "英雄"
image "Images/ending_hero.jpg" x=0 y=0 width=100% height=100%
text "结局 · 英雄" x=50% y=15% size=48 color="#FFD700" halign=center
text "游戏时间:{stats.playtime} 分钟" x=50% y=25% size=16 halign=center
text "道德值:{player.morality}" x=50% y=30% size=16 halign=center
text "选择次数:{stats.choices}" x=50% y=35% size=16 halign=center
button "返回标题" x=50% y=80% nav="title_main" halign=center分支树设计建议
开始
/ \
选择A 选择B
/ \
选择A1 选择B1
/ \ / \
结局1 结局2 结局3 结局4
\ /
True End设计建议:
- 早期分支——在游戏早期就让玩家做重大选择,增加重玩价值
- 中期汇合——分支不要完全独立,让不同路径有交叉
- 晚期再分——在结尾前根据累积变量做最终判定
- 隐藏条件——设置一些隐藏条件,鼓励玩家探索
回溯与多结局
回溯系统让玩家可以尝试不同选择:
dsl
label important_choice:
say "这个选择很重要。" speaker="系统"
menu "你确定?"
"选项A" -> choice_a
"选项B" -> choice_b
label choice_a:
set "story.choice_a_made" true
// ... 剧情A
// 玩家可以回溯到这里重新选择
label choice_b:
set "story.choice_b_made" true
// ... 剧情B回溯限制
如果用 block_rollback,玩家无法回溯到该点之前。这在某些"不可逆"的选择中是有用的。
完整示例
参见 最终作品——它有两个分支结局(守望者 / 远行者),基于 story.trust 好感度和最终选择。