Skip to content

如何做多结局 ​

多结局是视觉小说的核心吸引力。本章展示几种常见的多结局设计模式。

结局路由模式 ​

最简单的模式——用一个变量路由到不同结局:

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

设计建议:

  1. 早期分支——在游戏早期就让玩家做重大选择,增加重玩价值
  2. 中期汇合——分支不要完全独立,让不同路径有交叉
  3. 晚期再分——在结尾前根据累积变量做最终判定
  4. 隐藏条件——设置一些隐藏条件,鼓励玩家探索

回溯与多结局 ​

回溯系统让玩家可以尝试不同选择:

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 好感度和最终选择。

基于 .NET 10 + Avalonia 12 · Apache-2.0 License