Skip to content

点击 UI 跳转开场白与修改条目内容

点一个按钮 → 直接跳到另一个预写的开场白。在输入框打字 → 改变条目内容。这篇教你两种做法。


第一部分:按钮切换预写开场白

你要做的东西

一个有多个预写开场白的世界。玩家先看到「主开场」,上面有可点击的按钮。点击按钮后,聊天的第一条消息立刻切换成另一个预写的开场白——不需要 AI 重新生成,显示的就是你预先写好的原文。

原理

在 Yumina 中,你可以在编辑器的「首条消息」标签页里创建多个问候语。当玩家开始新会话时,所有问候语会被打包存成第一条消息的 swipes(左右滑切换)。玩家本来就可以在聊天界面通过左右滑手动切换,但我们要做的是:通过一个按钮,让玩家点一下就跳到指定的问候语

这就是 switchGreeting(index) 这个 API 的作用——它让自定义组件可以用代码直接跳到第 N 个问候语。

玩家点击「进入黑暗洞穴」按钮
  → 代码调用 api.switchGreeting(1)
  → 第一条消息的内容切换到第 2 个问候语(index 从 0 开始,所以 1 = 第二个)
  → 玩家立刻看到你预写的黑暗洞穴开场文字

一步步来

第 1 步:在「首条消息」里创建多个问候语

打开编辑器,在左侧边栏点击 首条消息 标签页。

这个标签页专门用来管理开场白。你可以创建多个问候语,每个问候语对应一个 swipe。

创建第一个问候语(主开场——呈现路线选择的场景):

点击「创建首条消息」按钮。在文本框中写入主开场的内容。这是玩家打开会话后最先看到的文字——在这里描述场景,引导玩家做选择:

*你在一片神秘森林的深处醒来。晨雾在古老的树木之间翻涌。*

你面前有两条路:

**左边**——一条狭窄的小径消失在黑暗中。空气变冷了,远处有回声。

**右边**——一条洒满阳光的小路,野花在微风中摇曳,鸟鸣声不断。

你要走哪边?

为什么这里只描述场景、不直接让 AI 回复?因为开场白是你预写的固定文字,不是 AI 生成的。你可以精确控制玩家看到的每一个字。

创建第二个问候语(黑暗洞穴开场):

点击底部的「添加问候语」按钮。你会看到底部出现了数字标签 12,点击 2 切换到第二个问候语的编辑框。写入黑暗洞穴路线的开场:

*你踏上了左边的小路。头顶的树冠越来越密,吞噬了光线。几分钟后,小径缩窄成一道岩壁上的裂缝——一个洞穴的入口。*

*冷风从里面涌出,带着潮湿石头和金属的气味。深处闪烁着微弱的蓝绿色光芒——那是附着在洞壁上的发光菌类。*

*你深吸一口气,走了进去。身后,最后一丝日光缩成一条苍白的线,然后消失了。*

你独自身处黑暗之中。

这段文字只有在玩家点击了「进入黑暗洞穴」按钮之后才会显示。在那之前,玩家看到的是第一个问候语(主开场)。

创建第三个问候语(阳光草地开场):

再点一次「添加问候语」。切换到标签 3,写入阳光草地路线的开场:

*你选择了右边的路。树木渐渐稀疏,温暖的阳光倾泻而入。几分钟后,森林让位于一片延伸到地平线的广阔草地。*

*各种颜色的野花在微风中轻轻摇曳。远处有一条溪流在阳光下闪闪发光。附近某处,一只鸟唱着你从未听过的旋律。*

*你感觉肩膀上的紧张感融化了。不管这是什么地方,它让人感到安全。*

欢迎来到永绽草地。

问候语的顺序就是 index

底部数字标签的顺序就是 switchGreeting() 的 index 参数。标签 1 = index 0(默认显示),标签 2 = index 1,标签 3 = index 2。后面写按钮代码时会用到这个 index。

现在你有了 3 个问候语。保存世界后,新会话会默认显示第一个(主开场)。接下来我们要做按钮,让玩家点击后切换到第二个或第三个。


第 2 步:创建路线追踪变量

我们需要一个变量来记录「玩家选了哪条路线」。这个变量有两个用途:

  • 让按钮在选择后消失(TSX 代码检查这个变量,如果不是 "none" 就不显示按钮)
  • 让后续对话知道当前路线(行为规则可以根据这个变量切换世界观条目)

编辑器 → 左侧边栏 → 变量 标签页 → 点击「添加变量」

字段填什么为什么这样填
显示名称当前路线给你自己看的,方便识别
IDcurrent_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」→ 粘贴以下代码:

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 步:保存并测试

  1. 点击编辑器顶部的「保存」
  2. 点击「开始游戏」或回到首页开一个新会话
  3. 你会看到主开场文字,下面有两个按钮
  4. 点「进入黑暗洞穴」——第一条消息立刻变成你预写的洞穴开场,按钮消失
  5. 发几条消息和 AI 对话——如果你做了第 3 步,AI 的回复会受到洞穴世界观的影响
  6. 想测试另一条路线?回到首页再开一个新会话,这次点另一个按钮

如果遇到问题:

现象可能的原因解决方法
看不到按钮消息渲染器代码没保存或有语法错误检查消息渲染器底部的编译状态,应该显示绿色「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 步:创建一个字符串变量

这个变量用来存放玩家输入的内容。

编辑器 → 变量 标签页 → 「添加变量」

字段填什么为什么这样填
显示名称自定义规则给你自己看的
IDcustom_rule条目里的 {{custom_rule}} 宏会找这个 ID
类型字符串因为内容是玩家输入的任意文字
默认值留空,或写一个默认值如 所有魔法都可以使用留空 = 新会话开始时这条规则为空;写了默认值 = 一开始就有一条规则
行为规则不要修改这个变量。它由玩家通过 UI 设置。告诉 AI 不要自己改这个变量

第 2 步:在条目里用宏占位

现在创建一个条目,把 {{custom_rule}} 写进去。引擎会在构建提示词时自动替换它。

编辑器 → 知识库 标签页 → 新建条目

字段填什么为什么这样填
名称世界规则给你自己看的
区域预设预设区的条目每次都会发给 AI

内容:

[世界规则]
以下规则在这个世界中生效,必须始终遵守:
{{custom_rule}}

发生了什么? 引擎每次构建提示词时,会扫描所有条目内容里的 {{...}}。如果花括号里的文字匹配一个变量 ID,就用那个变量的当前值替换。所以 {{custom_rule}} 会被替换成变量 custom_rule 的值。

如果变量值是空字符串,这一行就是空的——AI 看到的是「以下规则在这个世界中生效,必须始终遵守:」后面什么都没有。如果变量值是「魔法被禁止使用」,AI 看到的就是「以下规则在这个世界中生效,必须始终遵守:魔法被禁止使用」。


第 3 步:在消息渲染器里加输入框

我们需要在聊天界面里放一个输入框,让玩家能输入新规则。这个输入框放在消息渲染器里,只在最后一条消息的下方显示(避免每条消息都重复显示一个输入框)。

在你的消息渲染器 TSX 代码里,加上以下内容。如果你已经有第一部分的代码,就把这段加在 return 里面、消息文字后面:

tsx
// 在 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 步:保存并测试

  1. 保存世界,开一个新会话
  2. 在最后一条消息下面,你会看到「世界规则:(未设置)」和一个输入框
  3. 输入「魔法被禁止使用」,点「应用」(或按回车)
  4. 输入框上方的文字立刻变成「世界规则:魔法被禁止使用」——说明变量已经改了
  5. 现在发一条消息(比如"我尝试施放火球术")——这时引擎构建提示词,{{custom_rule}} 被替换成「魔法被禁止使用」
  6. AI 的回复应该会反映这条规则(比如"你举起手准备施法,却发现魔力被某种力量封锁了")
  7. 再改一次规则(比如改成「只有火属性魔法可以使用」),再发一条消息,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 文件,导入即可体验完整效果:

recipe-1-demo-zh.json

导入方法:

  1. 进入 Yumina → 我的世界 → 创建新世界
  2. 在编辑器顶部点「更多操作」→「导入包」
  3. 选择下载的 .json 文件
  4. 世界会被创建,所有问候语、变量、行为和渲染器都已预配置好
  5. 开一个新会话试试看

包含内容:

  • 3 个问候语(主开场 + 黑暗洞穴 + 阳光草地)
  • 2 个变量(current_route 追踪路线,custom_rule 玩家可编辑的规则)
  • 2 个动作行为(选择路线时开关世界观条目)
  • 一个消息渲染器(路线选择按钮 + 规则编辑器)
  • 一个使用 {{custom_rule}} 宏的世界观条目

这是实战配方 #1

后续还会有更多配方——战斗系统、商店界面、任务追踪等等。每个配方都是把变量、条目、行为和 UI 组合起来,做出更强大的东西。