Skip to content

任务追踪 ​

一个任务日志:每个任务显示完成了没有,玩家的金币显示在画面上,完成任务就发奖励、弹提示。任务完成没有由 AI 判断,奖励由行为发,而且只发一次,玩家界面里有一页显示任务日志。不用写代码。


你要做的东西 ​

  • 任务日志页:「采草药」和「打败森林狼」,每个都显示已完成或还没完成
  • 金币显示在画面上,奖励一到就更新
  • AI 来标记任务完成:剧情里真的发生了就算,不管玩家怎么说的
  • 奖励只发一次:草药 +30 金币,狼 +50 金币,同时弹一个提示
  • 进度计数:「完成 1 / 2」

怎么运作 ​

玩家把草药交给村长
  → AI 按照 Quests done 变量的行为规则,把「采草药」加进列表
  → 行为「奖励:采草药」在盯着 Quests done(变量变化)
  → 条件成立(Quests done 包含「采草药」),而且它还没触发过(最大触发次数 1)
  → Gold +30,弹出提示「任务完成:采草药(+30 金币)」,同时告诉 AI
  → 任务日志上「采草药」显示已完成

判断剧情里有没有发生某件事,AI 很拿手;只发一次钱,引擎很拿手。


一步一步来 ​

第 1 步:建变量 ​

点卡底下 还可以加 里的 + 变量,下面每个建一次。

Quests done ​

字段值
名称Quests done
类型列表/表格
初始值[]
行为规则见下面
玩家已经完成的任务,按名字记。任务有两个:「采草药」(把治伤的草药带给村长)和「打败森林狼」(杀掉那头一直叼羊的狼)。玩家在剧情里真的完成了其中一个,就把它的名字原样加进这个列表。没完成的任务不要加,同一个任务不要加两次。

Gold ​

字段值
名称Gold
类型数值
初始值0
范围0 到(不填)
行为规则玩家的金币。任务奖励会自动加上;捡到战利品、做买卖、被偷的时候你也可以改它。

点 放到玩家界面上,让金币一直显示在聊天里。

Quests completed(可选) ​

字段值
名称Quests completed
类型数值
公式(自动算)len({Quests done})

它自己数列表里有几个。AI 看得到,但改不了。


第 2 步:每个任务建一条奖励行为 ​

点 还可以加 里的 + 行为。

奖励:采草药 ​

部分设置
什么时候触发变量变化:Quests done
如果……Quests done 包含 采草药
效果修改变量:Gold 加上 30
显示通知:任务完成:采草药(+30 金币),样式选 成功
告诉 AI:玩家刚完成了「采草药」,拿到 30 金币。在回复里提一下。
最大触发次数1

奖励不会发两次,靠的是 最大触发次数 1:下一个任务加进来时列表又变了一次,那时「包含采草药」还是成立的,但这条行为已经触发过一次了。

告诉 AI 会给 AI 塞一句话,只管下一次回复,这样它就能写村长数钱给你的样子。

奖励:打败森林狼 ​

和上面一样,条件里换成 打败森林狼,Gold 加上 50,提示和给 AI 的那句话也对应改一下。

也可以用关键词触发

也可以靠对话里的词来完成任务:什么时候触发 选 AI 说出关键词 村长接过草药。关键词用逗号隔开,任意一个 出现就会触发,所以写成 打败, 狼 的话,「我看到一头狼」也会触发。要用只在那一刻才会出现的说法,再给每个任务配一个开关变量(在效果里设成开,在 如果…… 里检查),免得触发两次。像上面那样让 AI 来标记任务,更能应付玩家五花八门的说法。


第 3 步:做任务日志页 ​

点顶栏中间的 玩家界面。用 新建一页 加一页(选 空白页),起名叫「任务」。

  1. 加一个 文字 部件当标题,再加一个写着 完成 {Quests completed} / 2 的(用 插入变量的值…)
  2. 加一个 列表 部件。内容从哪来 选 自己写几行,加两行:标题 采草药,正文 把治伤的草药带给村长。奖励:30 金币;标题 打败森林狼,正文 杀掉那头一直叼羊的狼。奖励:50 金币
  3. 打开 每一行画成一张卡
  4. 在 锁住直到 里打开 没解锁的行显示成锁住,看哪个变量 选 Quests done,对照这一行的哪个字段 保持 title 不动。锁住时显示 写 还没完成。Quests done 里有这一行的标题,这一行就亮起来
  5. 加一个按钮,步骤选 跳到页面,跳回对话页;再在对话页上加一个「任务」按钮,跳到页面 → 任务

把 编辑 关掉,就能点着试试,不花额度。

整个界面的布局

如果想让金币和别的数值一直显示在画面上,而不是放在单独一页,可以用 整个界面 里的 养成面板 模板,它自带四条状态、金币和天数。


第 4 步:试玩 ​

点顶栏的 试玩。

  1. 打开 任务:两个任务都是「还没完成」,完成 0 / 2,Gold 0
  2. 把找草药、送回去这段剧情玩一遍。AI 标记任务的时候,提示弹出来,Gold 变成 30
  3. 打开 任务:采草药已完成,1 / 2
  4. 去把狼解决掉。Gold 变成 80,2 / 2

试玩右边的 这回合 能看到 Quests done 什么时候变了,每条奖励行为有没有触发。

如果出了问题:

现象可能的原因怎么改
剧情里任务做完了,但一直没标记AI 没把它加进列表在行为规则里把每个任务的名字写准,并说清楚什么情况才算完成
标记了,但没发奖励AI 加进去的名字和条件对不上列表里的名字、条件、列表行的标题,三处要是一样的字
奖励发了两次最大触发次数 不是 1每条奖励行为都设上
日志那一行一直锁着对照这一行的哪个字段 或 看哪个变量 选错了title,和 Quests done
AI 太早标记任务完成规则写得太松写清楚要发生什么:「只有草药交到村长手里才算」

写代码的做法:聊天下方的任务面板 ​

如果你自己写界面,就在界面代码里读同样的变量。上面做的东西都不用改。

面板 → 前端代码 → 打开 index.tsx,把默认的 return <Chat /> 换掉:

tsx
export default function MyWorld() {
  const api = useYumina();
  const msgs = api.messages || [];

  const done = Array.isArray(api.variables["Quests done"])
    ? api.variables["Quests done"].map(String)
    : [];
  const gold = Number(api.variables["Gold"] ?? 0);

  const quests = [
    { name: "采草药", reward: 30 },
    { name: "打败森林狼", reward: 50 },
  ].map((q) => ({ ...q, done: done.includes(q.name) }));

  const completedCount = quests.filter((q) => q.done).length;

  return (
    <Chat renderBubble={(msg) => {
      const isLastMsg = msg.messageIndex === msgs.length - 1;
      return (
        <div>
          <div
            style={{ color: "#e2e8f0", lineHeight: 1.7 }}
            dangerouslySetInnerHTML={{ __html: msg.contentHtml }}
          />

          {isLastMsg && (
            <div style={{
              marginTop: "16px",
              padding: "16px",
              background: "linear-gradient(135deg, rgba(30,41,59,0.8), rgba(15,23,42,0.9))",
              borderRadius: "12px",
              border: "1px solid #334155",
            }}>
              {/* 标题和金币 */}
              <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "14px" }}>
                <div style={{ fontSize: "15px", fontWeight: "bold", color: "#e2e8f0" }}>任务追踪</div>
                <div style={{
                  display: "flex", alignItems: "center", gap: "6px", padding: "4px 12px",
                  background: "rgba(234,179,8,0.15)", border: "1px solid rgba(234,179,8,0.3)", borderRadius: "20px",
                }}>
                  <span style={{ fontSize: "14px" }}>💰</span>
                  <span style={{ fontSize: "14px", fontWeight: "bold", color: "#fbbf24" }}>{gold}</span>
                </div>
              </div>

              <div style={{ fontSize: "12px", color: "#64748b", marginBottom: "12px" }}>
                已完成 {completedCount}/{quests.length}
              </div>

              {/* 任务列表 */}
              <div style={{ display: "flex", flexDirection: "column", gap: "8px" }}>
                {quests.map((quest) => (
                  <div key={quest.name} style={{
                    display: "flex", justifyContent: "space-between", alignItems: "center",
                    padding: "10px 14px", borderRadius: "8px",
                    background: quest.done ? "rgba(34,197,94,0.08)" : "rgba(30,41,59,0.5)",
                    border: quest.done ? "1px solid rgba(34,197,94,0.2)" : "1px solid #1e293b",
                  }}>
                    <span style={{
                      fontSize: "13px",
                      color: quest.done ? "#94a3b8" : "#e2e8f0",
                      textDecoration: quest.done ? "line-through" : "none",
                    }}>
                      {quest.name}
                    </span>
                    <div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
                      <span style={{ fontSize: "12px", color: quest.done ? "#4ade80" : "#64748b" }}>
                        {quest.done ? `+${quest.reward} 金币` : `${quest.reward} 金币`}
                      </span>
                      <span style={{
                        display: "inline-flex", alignItems: "center", justifyContent: "center",
                        width: "24px", height: "24px", borderRadius: "6px", fontSize: "13px", fontWeight: "bold",
                        background: quest.done ? "rgba(34,197,94,0.2)" : "rgba(239,68,68,0.15)",
                        color: quest.done ? "#4ade80" : "#f87171",
                        border: quest.done ? "1px solid rgba(34,197,94,0.3)" : "1px solid rgba(239,68,68,0.25)",
                      }}>
                        {quest.done ? "✓" : "✗"}
                      </span>
                    </div>
                  </div>
                ))}
              </div>

              {completedCount === quests.length && (
                <div style={{
                  marginTop: "12px", padding: "10px", textAlign: "center", fontSize: "13px", fontWeight: "600",
                  background: "rgba(34,197,94,0.1)", border: "1px solid rgba(34,197,94,0.25)",
                  borderRadius: "8px", color: "#4ade80",
                }}>
                  任务全部完成!
                </div>
              )}
            </div>
          )}
        </div>
      );
    }} />
  );
}

代码做了什么:

  • api.variables["Quests done"] 按变量名读这个列表,任务的名字在里面就算完成
  • api.variables["Gold"] 读金币
  • completedCount 数一数完成了几个,用在进度那一行和「全部完成」的横条上
  • 面板只出现在最后一条消息下面,变量一变就跟着更新

面板 → 前端代码 里文件旁边的状态应该是绿色的 正常。

不想自己写代码?

点画布右上角的 创作助手,说说你想要什么样的面板。


再往前走一步 ​

更多任务 ​

在 Quests done 的行为规则里加上新任务的名字和怎样算完成,加一条奖励行为,再在任务日志里加一行(或者在代码的 quests 数组里加一项)。如果用了公式,把日志页上的 / 2 改成新的总数。

AI 发出来的任务 ​

再加一个列表 Quests open,在它的行为规则里告诉 AI:有角色请玩家帮忙时,把这个任务加进来。在日志页上再放一个列表部件显示它,内容从哪来 选 读一个变量(Quests open),而不是自己写几行。

任务链 ​

想让「拯救村子」在草药之后才出现,就把新任务写成一条待命设定(等行为来叫,AI 才看得到)。然后加一条行为:变量变化 盯 Quests done,如果…… Quests done 包含 采草药,效果 启用词条 选那条设定,最大触发次数 1。从这以后 AI 就知道村长有了新请求。如果…… 里可以放好几个条件,选 全部满足 或 任一满足,用来做更长的链。待命设定见开场白和设定。

把金币花掉 ​

商店用的是同一个 Gold 变量:任务往里加,商店往外扣。


速查 ​

你想要怎么做
让 AI 判断任务什么时候完成一个列表/表格变量,行为规则里写清每个任务的名字
奖励只发一次行为:变量变化,如果…… 列表 包含 这个任务,最大触发次数 1
告诉玩家效果:显示通知,样式选 成功
告诉 AI效果:告诉 AI(只管下一次回复)
数一数完成了几个一个数值变量,公式填 len({Quests done})
显示任务日志一个列表部件,自己写几行,锁住直到 看 Quests done
金币一直显示在画面上在 Gold 上点 放到玩家界面上
靠一句原话完成任务玩家说出关键词 / AI 说出关键词(任意一个关键词出现就触发)
自己画的面板读同样变量的界面代码

自己试试:可导入的演示 ​

下载这个 JSON 导入进去,就能看到能跑的版本:

recipe-6-demo-zh.json

怎么导入:

  1. 点左边的 创建,选 空白项目,再选 画布
  2. 在顶栏点 ⋮ → 导入文件,选下载好的 .json 文件。它会替换掉这张新卡里的所有内容
  3. 点 保存,再点 试玩

演示用的是关键词触发的行为,每个任务一个开关变量,任务面板写在界面代码里。


这是食谱 #6

同一个循环(AI 发现某件事发生了,行为只发一次奖励,界面把它显示出来)也能用在剧情节点、支线任务树,还有成就上。