点击 UI 跳转开场白与修改条目内容
点一个按钮 → 直接跳到另一个预写的开场白。在输入框打字 → 改变条目内容。这篇教你两种做法。
第一部分:按钮切换预写开场白
你要做的东西
一个有多个预写开场白的世界。玩家先看到「主开场」,上面有可点击的按钮。点击按钮后,聊天的第一条消息立刻切换成另一个预写的开场白——不需要 AI 重新生成,显示的就是你预先写好的原文。
原理
在 Yumina 中,你可以在编辑器的「首条消息」标签页里创建多个问候语。当玩家开始新会话时,所有问候语会被打包存成第一条消息的 swipes(左右滑切换)。玩家本来就可以在聊天界面通过左右滑手动切换,但我们要做的是:通过一个按钮,让玩家点一下就跳到指定的问候语。
这就是 switchGreeting(index) 这个 API 的作用——它让自定义组件可以用代码直接跳到第 N 个问候语。
玩家点击「进入黑暗洞穴」按钮
→ 代码调用 api.switchGreeting(1)
→ 第一条消息的内容切换到第 2 个问候语(index 从 0 开始,所以 1 = 第二个)
→ 玩家立刻看到你预写的黑暗洞穴开场文字一步步来
第 1 步:在「首条消息」里创建多个问候语
打开编辑器,在左侧边栏点击 首条消息 标签页。
这个标签页专门用来管理开场白。你可以创建多个问候语,每个问候语对应一个 swipe。
创建第一个问候语(主开场——呈现路线选择的场景):
点击「创建首条消息」按钮。在文本框中写入主开场的内容。这是玩家打开会话后最先看到的文字——在这里描述场景,引导玩家做选择:
*你在一片神秘森林的深处醒来。晨雾在古老的树木之间翻涌。*
你面前有两条路:
**左边**——一条狭窄的小径消失在黑暗中。空气变冷了,远处有回声。
**右边**——一条洒满阳光的小路,野花在微风中摇曳,鸟鸣声不断。
你要走哪边?为什么这里只描述场景、不直接让 AI 回复?因为开场白是你预写的固定文字,不是 AI 生成的。你可以精确控制玩家看到的每一个字。
创建第二个问候语(黑暗洞穴开场):
点击底部的「添加问候语」按钮。你会看到底部出现了数字标签 1 和 2,点击 2 切换到第二个问候语的编辑框。写入黑暗洞穴路线的开场:
*你踏上了左边的小路。头顶的树冠越来越密,吞噬了光线。几分钟后,小径缩窄成一道岩壁上的裂缝——一个洞穴的入口。*
*冷风从里面涌出,带着潮湿石头和金属的气味。深处闪烁着微弱的蓝绿色光芒——那是附着在洞壁上的发光菌类。*
*你深吸一口气,走了进去。身后,最后一丝日光缩成一条苍白的线,然后消失了。*
你独自身处黑暗之中。这段文字只有在玩家点击了「进入黑暗洞穴」按钮之后才会显示。在那之前,玩家看到的是第一个问候语(主开场)。
创建第三个问候语(阳光草地开场):
再点一次「添加问候语」。切换到标签 3,写入阳光草地路线的开场:
*你选择了右边的路。树木渐渐稀疏,温暖的阳光倾泻而入。几分钟后,森林让位于一片延伸到地平线的广阔草地。*
*各种颜色的野花在微风中轻轻摇曳。远处有一条溪流在阳光下闪闪发光。附近某处,一只鸟唱着你从未听过的旋律。*
*你感觉肩膀上的紧张感融化了。不管这是什么地方,它让人感到安全。*
欢迎来到永绽草地。问候语的顺序就是 index
底部数字标签的顺序就是 switchGreeting() 的 index 参数。标签 1 = index 0(默认显示),标签 2 = index 1,标签 3 = index 2。后面写按钮代码时会用到这个 index。
现在你有了 3 个问候语。保存世界后,新会话会默认显示第一个(主开场)。接下来我们要做按钮,让玩家点击后切换到第二个或第三个。
第 2 步:创建路线追踪变量
我们需要一个变量来记录「玩家选了哪条路线」。这个变量有两个用途:
- 让按钮在选择后消失(TSX 代码检查这个变量,如果不是
"none"就不显示按钮) - 让后续对话知道当前路线(行为规则可以根据这个变量切换世界观条目)
编辑器 → 左侧边栏 → 变量 标签页 → 点击「添加变量」
| 字段 | 填什么 | 为什么这样填 |
|---|---|---|
| 显示名称 | 当前路线 | 给你自己看的,方便识别 |
| ID | current_route | 代码里用这个 ID 来读写变量 |
| 类型 | 字符串 | 因为值是文字("none", "dark", "light") |
| 默认值 | none | 表示「还没选择」。按钮代码会检查这个值 |
| 分类 | 标记 | 只是分类标签,方便在变量列表里找 |
| 行为规则 | 不要修改这个变量。它由玩家的 UI 选择控制。 | 告诉 AI 不要自己改这个变量——只有玩家按钮能改 |
行为规则这个字段是给 AI 看的指令。如果不写,AI 可能在回复中自作主张改变这个变量的值(比如 AI 觉得"玩家走进了洞穴"所以自己把
current_route改成"dark")。写了之后 AI 就不会碰它。
第 3 步:(可选)创建世界观条目和行为规则
如果你希望选择路线后,AI 的后续回复会参考不同的世界观设定,需要做这一步。如果你只想切换开场白、不需要后续世界观变化,可以跳过。
创建两个世界观条目(默认禁用):
编辑器 → 知识库 标签页 → 新建条目
黑暗洞穴世界观条目:
| 字段 | 填什么 | 为什么这样填 |
|---|---|---|
| 名称 | 黑暗洞穴世界观 | 给你自己看的 |
| 区域 | 预设 | 预设区的条目每次都会发给 AI |
| 启用 | 否(关闭开关) | 默认不启用——等玩家选了黑暗路线后,行为规则再把它打开 |
内容:
[世界设定:暗影之口洞穴]
玩家正在探索暗影之口洞穴。关键细节:
- 古代矮人遗迹,已被遗弃数百年
- 发光菌类提供微弱的蓝绿色光线
- 深处隧道中潜伏着奇异生物
- 越往里走温度越低
保持紧张的恐怖生存氛围。描写回荡的声音、闪烁的阴影、滴水声和头顶石块的压迫感。阳光草地世界观条目: 同样新建一个,也默认禁用,内容描述草地的设定和氛围。
为什么默认禁用? 因为在玩家选择路线之前,这些世界观不应该影响 AI。只有当玩家选了路线之后,行为规则才把对应的那个启用、另一个禁用。
创建两个行为规则:
编辑器 → 行为 标签页 → 添加行为
行为 1:「选择黑暗路线」
| 字段 | 填什么 | 为什么这样填 |
|---|---|---|
| 名称 | 选择黑暗路线 | 给你自己看的 |
| 触发条件 | 选「动作」→ 动作 ID 填 choose-dark | 当 TSX 代码调用 executeAction("choose-dark") 时触发 |
然后在「执行动作」里依次添加:
| 动作类型 | 设置 | 作用 |
|---|---|---|
| 修改变量 | current_route 设为 dark | 记录玩家选了黑暗路线 |
| 启用知识条目 | 黑暗洞穴世界观 | 打开黑暗洞穴的设定 |
| 禁用知识条目 | 阳光草地世界观 | 关掉阳光草地的设定(防止两个同时生效) |
行为 2:「选择阳光路线」 同样创建,动作 ID 是 choose-light,动作反过来(启用草地世界观、禁用洞穴世界观)。
为什么不直接在 TSX 代码里 setVariable 就好了? 因为
setVariable只能改变量,不能开关条目。行为的「启用知识条目」/「禁用知识条目」动作才能在运行时启用/禁用知识库里的条目。所以按钮点击时,我们同时做三件事:setVariable(改变量)+executeAction(触发行为去开关条目)+switchGreeting(切换开场白)。
第 4 步:做带按钮的消息渲染器
这是让按钮出现在聊天界面的关键步骤。
编辑器 → 消息渲染器 标签页 → 选「自定义 TSX」→ 粘贴以下代码:
export default function Renderer({ content, renderMarkdown, messageIndex }) {
const api = useYumina();
const hasChosen = api.variables.current_route !== "none";
return (
<div>
{/* 正常渲染消息文字 */}
<div
style={{ color: "#e2e8f0", lineHeight: 1.7 }}
dangerouslySetInnerHTML={{ __html: renderMarkdown(content) }}
/>
{/* 路线选择按钮 */}
{/* messageIndex === 0 表示只在第一条消息上显示 */}
{/* !hasChosen 表示选了之后就不再显示 */}
{messageIndex === 0 && !hasChosen && (
<div style={{
display: "flex",
gap: "12px",
marginTop: "16px",
}}>
<button
onClick={() => {
api.setVariable("current_route", "dark"); // 记录选择,让按钮消失
api.executeAction("choose-dark"); // 触发行为规则,开关世界观条目
api.switchGreeting?.(1); // 切换到第 2 个问候语
}}
style={{
flex: 1,
padding: "16px",
background: "linear-gradient(135deg, #1e1b4b, #312e81)",
border: "1px solid #4338ca",
borderRadius: "12px",
color: "#c7d2fe",
fontSize: "15px",
fontWeight: "bold",
cursor: "pointer",
}}
>
进入黑暗洞穴
</button>
<button
onClick={() => {
api.setVariable("current_route", "light");
api.executeAction("choose-light");
api.switchGreeting?.(2); // 切换到第 3 个问候语
}}
style={{
flex: 1,
padding: "16px",
background: "linear-gradient(135deg, #365314, #4d7c0f)",
border: "1px solid #65a30d",
borderRadius: "12px",
color: "#ecfccb",
fontSize: "15px",
fontWeight: "bold",
cursor: "pointer",
}}
>
走向阳光草地
</button>
</div>
)}
</div>
);
}代码逐行解释:
const api = useYumina()— 获取 Yumina 的 API,可以读变量、改变量、触发动作、切换问候语api.variables.current_route— 读取当前路线变量的值hasChosen— 如果不是"none"说明已经选过了messageIndex === 0— 只在第一条消息上显示按钮(不是每条消息都显示)!hasChosen— 选了之后按钮消失api.setVariable("current_route", "dark")— 把变量设为"dark",这样hasChosen变为true,按钮消失api.executeAction("choose-dark")— 触发我们第 3 步创建的行为规则api.switchGreeting?.(1)— 把第一条消息切换到 index 1(第二个问候语)。?.是可选链——如果这个 API 不可用,不会报错
不想自己写代码?用工作室 AI
编辑器顶部 → 点击「进入工作室」→ AI 助手面板 → 用中文描述你想要什么,AI 会帮你生成代码。
第 5 步:保存并测试
- 点击编辑器顶部的「保存」
- 点击「开始游戏」或回到首页开一个新会话
- 你会看到主开场文字,下面有两个按钮
- 点「进入黑暗洞穴」——第一条消息立刻变成你预写的洞穴开场,按钮消失
- 发几条消息和 AI 对话——如果你做了第 3 步,AI 的回复会受到洞穴世界观的影响
- 想测试另一条路线?回到首页再开一个新会话,这次点另一个按钮
如果遇到问题:
| 现象 | 可能的原因 | 解决方法 |
|---|---|---|
| 看不到按钮 | 消息渲染器代码没保存或有语法错误 | 检查消息渲染器底部的编译状态,应该显示绿色「OK」 |
| 按钮点了没反应 | switchGreeting 还没部署到服务端 | 确认你使用的是最新版本 |
| 按钮点了开场白没切换 | 问候语数量不够 | 确认「首条消息」标签页里有 3 个问候语 |
| 按钮点了但没消失 | 变量没有被正确设置 | 回到编辑器检查变量的默认值是否是 none,以及消息渲染器代码是否正确检查了 current_route |
| 世界观没切换 | 行为规则没正确配置 | 检查行为的动作 ID 是否和代码里的一致(choose-dark / choose-light) |
第二部分:玩家输入修改条目内容
你要做的东西
在聊天界面里加一个文本输入框。玩家在里面打字(比如写一条自定义规则、角色名、或剧情指令),点击「应用」后,这些文字会被注入到知识库条目里,从而改变 AI 接下来的行为。
原理
Yumina 的条目支持宏语法。你可以在条目内容里写 {{variableId}},这是一个占位符。引擎在每次构建发给 AI 的提示词时,会自动把它替换成对应变量的当前值。
举个例子:
- 你在条目里写:
特殊规则:{{custom_rule}} - 变量
custom_rule的值是"所有魔法都可以使用" - AI 收到的提示词里,这一行变成了:
特殊规则:所有魔法都可以使用
关键:替换不是实时的。 它发生在每次构建提示词的时候——也就是玩家发下一条消息、AI 要回复之前。
完整时序:
1. 条目里写着:「特殊规则:{{custom_rule}}」
2. 变量 custom_rule 当前值 = "所有魔法都可以使用"
3. 玩家发消息 → 引擎构建提示词 → 把 {{custom_rule}} 替换成变量值
→ AI 收到「特殊规则:所有魔法都可以使用」→ AI 按此回复
4. 玩家在输入框输入 "魔法被禁止使用",点「应用」
5. 代码调用 setVariable("custom_rule", "魔法被禁止使用")
→ 变量值立刻更新了
6. 但此时 AI 还不知道!因为提示词还没有重新构建。
7. 玩家再发一条消息 → 引擎重新构建提示词 → 这次替换用的是新值
→ AI 收到「特殊规则:魔法被禁止使用」→ AI 开始遵守新规则一句话总结:改变量是即时的,但 AI 在下一条消息才会看到变化。
一步步来
第 1 步:创建一个字符串变量
这个变量用来存放玩家输入的内容。
编辑器 → 变量 标签页 → 「添加变量」
| 字段 | 填什么 | 为什么这样填 |
|---|---|---|
| 显示名称 | 自定义规则 | 给你自己看的 |
| ID | custom_rule | 条目里的 {{custom_rule}} 宏会找这个 ID |
| 类型 | 字符串 | 因为内容是玩家输入的任意文字 |
| 默认值 | 留空,或写一个默认值如 所有魔法都可以使用 | 留空 = 新会话开始时这条规则为空;写了默认值 = 一开始就有一条规则 |
| 行为规则 | 不要修改这个变量。它由玩家通过 UI 设置。 | 告诉 AI 不要自己改这个变量 |
第 2 步:在条目里用宏占位
现在创建一个条目,把 {{custom_rule}} 写进去。引擎会在构建提示词时自动替换它。
编辑器 → 知识库 标签页 → 新建条目
| 字段 | 填什么 | 为什么这样填 |
|---|---|---|
| 名称 | 世界规则 | 给你自己看的 |
| 区域 | 预设 | 预设区的条目每次都会发给 AI |
内容:
[世界规则]
以下规则在这个世界中生效,必须始终遵守:
{{custom_rule}}发生了什么? 引擎每次构建提示词时,会扫描所有条目内容里的
{{...}}。如果花括号里的文字匹配一个变量 ID,就用那个变量的当前值替换。所以{{custom_rule}}会被替换成变量custom_rule的值。如果变量值是空字符串,这一行就是空的——AI 看到的是「以下规则在这个世界中生效,必须始终遵守:」后面什么都没有。如果变量值是「魔法被禁止使用」,AI 看到的就是「以下规则在这个世界中生效,必须始终遵守:魔法被禁止使用」。
第 3 步:在消息渲染器里加输入框
我们需要在聊天界面里放一个输入框,让玩家能输入新规则。这个输入框放在消息渲染器里,只在最后一条消息的下方显示(避免每条消息都重复显示一个输入框)。
在你的消息渲染器 TSX 代码里,加上以下内容。如果你已经有第一部分的代码,就把这段加在 return 里面、消息文字后面:
// 在 Renderer 函数的开头,加上这些变量声明
const api = useYumina(); // 如果已经有了就不用重复
const msgs = api.messages || [];
const isLastMsg = messageIndex === msgs.length - 1; // 判断是不是最后一条消息
const [ruleInput, setRuleInput] = React.useState("");
const currentRule = String(api.variables.custom_rule || "");
// 在 return 的 JSX 里,放在消息文字的下面
{isLastMsg && (
<div style={{
marginTop: "12px",
padding: "12px",
background: "rgba(30,41,59,0.5)",
borderRadius: "8px",
border: "1px solid #334155",
}}>
<div style={{ fontSize: "12px", color: "#94a3b8", marginBottom: "6px" }}>
世界规则:{currentRule || "(未设置)"}
</div>
<div style={{ display: "flex", gap: "8px" }}>
<input
type="text"
value={ruleInput}
onChange={(e) => setRuleInput(e.target.value)}
placeholder="输入新规则..."
style={{
flex: 1, padding: "6px 10px", background: "#1e293b",
border: "1px solid #475569", borderRadius: "6px",
color: "#e2e8f0", fontSize: "13px", outline: "none",
}}
onKeyDown={(e) => {
if (e.key === "Enter" && ruleInput.trim()) {
api.setVariable("custom_rule", ruleInput.trim());
setRuleInput("");
}
}}
/>
<button
onClick={() => {
if (ruleInput.trim()) {
api.setVariable("custom_rule", ruleInput.trim());
setRuleInput("");
}
}}
style={{
padding: "6px 14px", background: "#4338ca", borderRadius: "6px",
color: "#e0e7ff", fontSize: "13px", fontWeight: "600",
cursor: "pointer", border: "none",
}}
>
应用
</button>
</div>
</div>
)}代码逐行解释:
isLastMsg— 只在最后一条消息上显示输入框,不然每条消息都会有一个currentRule— 读取变量的当前值,显示在输入框上方让玩家知道现在的规则是什么ruleInput— React 状态,追踪输入框里正在输入的文字onKeyDown— 按回车键也能提交,不一定要点按钮api.setVariable("custom_rule", ...)— 把玩家输入的文字写入变量。下次 AI 回复时,条目里的{{custom_rule}}就会被替换成这个文字setRuleInput("")— 提交后清空输入框
为什么放在消息渲染器里?
在 Yumina 中,具有 surface: "app" 的组件会接管整个屏幕并替换聊天界面。在普通聊天模式下它们不会显示。因此,如果你想在聊天界面中添加交互元素(按钮、输入框),请将它们放在具有 surface: "message" 的组件(消息渲染器)中。
第 4 步:保存并测试
- 保存世界,开一个新会话
- 在最后一条消息下面,你会看到「世界规则:(未设置)」和一个输入框
- 输入「魔法被禁止使用」,点「应用」(或按回车)
- 输入框上方的文字立刻变成「世界规则:魔法被禁止使用」——说明变量已经改了
- 现在发一条消息(比如"我尝试施放火球术")——这时引擎构建提示词,
{{custom_rule}}被替换成「魔法被禁止使用」 - AI 的回复应该会反映这条规则(比如"你举起手准备施法,却发现魔力被某种力量封锁了")
- 再改一次规则(比如改成「只有火属性魔法可以使用」),再发一条消息,AI 会适应新规则
组合两种模式
你可以把开场白切换和条目修改结合起来。举个实际的例子:
角色创建 + 故事开场:
- 主问候语(index 0) 不写故事,而是显示一个角色创建表单——名字、职业、背景故事的输入框
- 玩家填完 → 通过
setVariable把输入内容存到变量里 → 条目里的{{player_name}}、{{player_class}}、{{player_backstory}}宏获取到这些值 - 玩家点「开始冒险」→
switchGreeting(1)跳转到真正的故事开场白 - 从第一条 AI 回复开始,AI 就知道玩家角色的名字、职业和背景了
速查表
| 你想做的事 | 怎么做 |
|---|---|
| 跳转到预写的开场白 | switchGreeting(index) — index 对应「首条消息」标签页里问候语的顺序(从 0 开始) |
| 让玩家输入内容改变 AI 行为 | 创建字符串变量 + 条目里写 {{variableId}} + UI 里调 setVariable() |
| 按钮只在第一条消息显示 | TSX 里判断 messageIndex === 0 |
| 按钮选了就消失 | 用变量追踪选择状态,TSX 里判断 hasChosen |
| 选路线后切换世界观 | 创建行为,用「启用知识条目」/「禁用知识条目」动作 |
| 切换时播放音效 | 在行为里加「播放音乐」或「播放音效」动作 |
| 切换时弹出通知 | 在行为里加「显示通知」动作 |
直接试试——可导入的示例世界
下载这个 JSON 文件,导入即可体验完整效果:
导入方法:
- 进入 Yumina → 我的世界 → 创建新世界
- 在编辑器顶部点「更多操作」→「导入包」
- 选择下载的
.json文件 - 世界会被创建,所有问候语、变量、行为和渲染器都已预配置好
- 开一个新会话试试看
包含内容:
- 3 个问候语(主开场 + 黑暗洞穴 + 阳光草地)
- 2 个变量(
current_route追踪路线,custom_rule玩家可编辑的规则) - 2 个动作行为(选择路线时开关世界观条目)
- 一个消息渲染器(路线选择按钮 + 规则编辑器)
- 一个使用
{{custom_rule}}宏的世界观条目
这是实战配方 #1
后续还会有更多配方——战斗系统、商店界面、任务追踪等等。每个配方都是把变量、条目、行为和 UI 组合起来,做出更强大的东西。
