Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

选项:让玩家做选择

选项是让对话变成互动的关键!玩家可以选择不同的路线,走向不同的结局。

最简单的选项

node ChoiceExample {
    text: "你想去哪?"
    
    choice: [
        "森林" -> ForestScene,
        "城镇" -> TownScene
    ]
}

node ForestScene {
    text: "你来到了森林。"
}

node TownScene {
    text: "你来到了城镇。"
}

语法很简单:

  • choice: 关键字
  • 方括号 [] 里面是选项列表
  • 每个选项:"文字" -> 目标节点

选项的各种写法

基本跳转

choice: [
    "选项A" -> NodeA,
    "选项B" -> NodeB,
    "选项C" -> NodeC
]

带条件的选项

只有满足条件才显示:

choice: [
    "正常选项" -> 节点1,
    "有钥匙才显示" when has_key() -> 节点2,
    "等级>=10才显示" when is_level_enough() -> 节点3
]

两种条件写法

// 函数式写法:when 用空格分割

"选项文字" when 条件函数() -> 目标

// 链式写法:用括号括起来

("选项文字").when(条件函数()) -> 目标

特殊行为

Return - 结束节点

choice: [
    "继续" -> 下一节点,
    "退出" -> return  // 直接结束节点
]

Break - 中断选项

choice: [
    "选项1" -> 节点1,
    "选项2" -> 节点2,
    "算了,不选了" -> break  // 跳出选项,继续当前节点
]

text: "好吧,那我们继续。"  // 选了 break 才会来这里

嵌套选项

选项里还可以有选项!

choice: [
    "吃点什么" -> [
        "苹果" -> 吃苹果,
        "面包" -> 吃面包,
        "算了不吃了" -> return
    ],
    "做点什么" -> [
        "休息" -> 休息,
        "探险" -> 探险
    ],
    "离开" -> return
]

玩家先选“吃点什么“,然后会看到第二层选项。

实战示例

简单分支

node ChatWithNPC {
    text: "一个商人向你走来。"
    text: "他说:'需要买点什么吗?'"
    
    choice: [
        "看看商品" -> EnterShop,
        "问问消息" -> AskSth,
        "礼貌拒绝" -> SayNO
    ]
}

带条件的复杂选择

node Door {
    text: "你面前有一扇神秘的门。"
    
    choice: [
        "直接推门" -> PushDoor,
        "用钥匙开门" when has_key() -> OpenDoorWithKey,
        "用魔法破解" when can_use_magic() -> OpenDoorWithMagic,
        "算了,离开吧" -> return
    ]
}

多层嵌套

node Restaurant {
    text: "欢迎光临!想吃点什么?"
    
    choice: [
        "中餐" -> [
            "炒饭" -> End1,
            "面条" -> End2,
            "返回" -> break
        ],
        "西餐" -> [
            "牛排" -> End3,
            "意面" -> End4,
            "返回" -> break
        ],
        "不吃了" -> return
    ],
    
    text: "那再考虑考虑吧~"  // 选了 break 会到这
}

break 的妙用

node 重要选择 {
    text: "这是一个重要的决定。"
    text: "你确定吗?"
    
    choice: [
        "确定!" -> 确定路线,
        "让我再想想..." -> break  // 跳出选项
    ],
    
    text: "好的,慢慢考虑。"
    
    // 可以再来一次选择
    choice: [
        "现在确定了" -> 确定路线,
        "算了,不想选了" -> return
    ]
}

Return vs Break

容易混淆,让我们明确一下:

关键字作用后续执行
return结束当前节点不会执行后面的内容
break跳出当前选项会继续执行后面的内容

Return 示例

node 测试 {
    text: "开始"
    
    choice: [
        "退出" -> return
    ]
    
    text: "这句不会执行"  // 因为上面 return 了
}

Break 示例

node 测试 {
    text: "开始"
    
    choice: [
        "中断" -> break
    ]
    
    text: "这句会执行"  // break 后继续往下
}

条件函数

所有用在 when 后面的函数都必须:

  • 返回 Bool(或 Boolean)类型
  • 提前声明
// 在文件中声明这些函数
fn has_key() -> Bool
fn can_use_magic() -> Boolean
fn is_level_enough() -> Bool

详见函数:连接游戏世界

最佳实践

✅ 好的做法

// 清晰的选项文字
choice: [
    "友好地打招呼" -> 友好,
    "保持警惕" -> 警惕,
    "转身离开" -> 离开
]
// 合理使用条件
choice: [
    "普通选项" -> 节点1,
    "特殊选项" when special_unlock() -> 节点2
]

❌ 不好的做法

// 选项文字太长
choice: [
    "我觉得我们应该先去森林看看,然后再决定接下来怎么办..." -> 节点1
]
// 所有选项都有条件(可能全部不显示!)
choice: [
    "选项1" when cond1() -> A,
    "选项2" when cond2() -> B,
    "选项3" when cond3() -> C
]

建议

  1. 至少一个无条件选项:确保玩家总有选择
  2. 选项文字简洁:一般不超过 20 个字
  3. 逻辑清晰:不要嵌套太深(建议最多 2-3 层)
  4. 提供退路:给玩家“返回“或“取消“的机会

常见问题

Q: 选项可以跳转到自己吗?

可以!这样可以创建循环:

node Cycle {
    text: "要继续吗?"
    
    choice: [
        "继续" -> Cycle,  // 跳回自己
        "停止" -> return
    ]
}

Q: 所有选项都有条件,但都不满足怎么办?

选项的条件检测本质上是 给选项打标记

简单来说:

  • 当条件满足时,选项可用标记为“可选”。
  • 当条件不满足时,选项可用标记为“不可选”。
  • 具体的显示效果(灰色、隐藏、提示文本等)由程序实现决定,而非 mortar 直接控制。

⚠️ 如果所有选项条件都不满足,建议在DSL中保留至少一个无条件选项,以避免出现“无可选项”的情况。

Q: 嵌套选项可以嵌套多深?

技术上没有限制,但建议不超过 3 层,否则大家都会迷糊。

Q: 能在选项文字中使用插值吗?

可以。任何字符串都可以使用字符串插值。

下一步