选项:让玩家做选择
选项是让对话变成互动的关键!玩家可以选择不同的路线,走向不同的结局。
最简单的选项
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
]
建议
- 至少一个无条件选项:确保玩家总有选择
- 选项文字简洁:一般不超过 20 个字
- 逻辑清晰:不要嵌套太深(建议最多 2-3 层)
- 提供退路:给玩家“返回“或“取消“的机会
常见问题
Q: 选项可以跳转到自己吗?
可以!这样可以创建循环:
node Cycle {
text: "要继续吗?"
choice: [
"继续" -> Cycle, // 跳回自己
"停止" -> return
]
}
Q: 所有选项都有条件,但都不满足怎么办?
选项的条件检测本质上是 给选项打标记。
简单来说:
- 当条件满足时,选项可用标记为“可选”。
- 当条件不满足时,选项可用标记为“不可选”。
- 具体的显示效果(灰色、隐藏、提示文本等)由程序实现决定,而非 mortar 直接控制。
⚠️ 如果所有选项条件都不满足,建议在DSL中保留至少一个无条件选项,以避免出现“无可选项”的情况。
Q: 嵌套选项可以嵌套多深?
技术上没有限制,但建议不超过 3 层,否则大家都会迷糊。
Q: 能在选项文字中使用插值吗?
可以。任何字符串都可以使用字符串插值。