Skip to content

昼夜循环

构建一个自动推进的时间系统——每 3 个回合,时间向前推进(早晨 → 中午 → 傍晚 → 夜晚 → 早晨)。不同时段激活不同的知识条目和背景音乐,使 AI 的写作氛围随之变化。玩家不需要做任何事——时间自然流逝。


你将构建什么

一个嵌入在聊天中的昼夜循环系统:

  • 自动计数 — 每个对话回合,内部计数器加 1。到第 3 个回合时,时间推进一个时段
  • 四个时段 — 早晨 → 中午 → 傍晚 → 夜晚 → 早晨(如此循环)
  • 氛围切换 — 每个时段有自己的知识条目,描述该时段的光线、温度、NPC 行为等。当时段变化时,旧条目被禁用,新条目被启用
  • BGM 切换 — 早晨播放鸟鸣,夜晚切换到蛐蛐和蛙鸣。过渡使用淡入淡出,不会突然中断
  • 时间徽章 — 聊天中最后一条消息上的小图标(☀️🌤️🌅🌙),让玩家随时知道现在是什么时间

工作原理

整个系统归结为:每回合计数器 +1 → 计数器到 3 → 切换时段 → 重置计数器 → 切换条目和音乐

玩家发送消息 → AI 回复 → 回合结束
  → 「每回合 +1」行为触发:turn_counter 从 0 变为 1
  → 下一回合:turn_counter 从 1 变为 2
  → 下一回合:turn_counter 从 2 变为 3
  → 「变量越过阈值」行为触发:turn_counter 超过 2
  → 动作执行:time_period 设为下一个时段,turn_counter 重置为 0
  → 旧时段条目禁用,新时段条目启用
  → 淡入淡出切换到新时段的 BGM
  → Root Component 读取变量并更新时间徽章

有两种实现方式。结果相同,思维模型不同:

方式使用的触发器行为数量适合
方式 A:每回合 +1 + 变量越过阈值every-turn + variable-crossed2想理解底层机制的人
方式 B:直接每 N 回合触发turn-count (everyNTurns=3)1只想把事情做完的人

本教程教的是方式 A(更灵活,帮助你理解行为如何链式连接)。方式 B 在最后简要介绍。


分步教程

第 1 步:创建变量

我们需要 2 个变量——一个追踪当前时段,一个作为计数器。

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

变量 1:当前时段

字段原因
名称Current Time Period供你自己参考
IDtime_period行为和 Root Component 通过这个 ID 读写
类型String因为值是文本("Morning""Noon""Evening""Night"
默认值Morning新会话从早晨开始
分类Custom时间系统专用分类
行为规则Do not modify this variable. It is controlled automatically by the day-night cycle system. Its current value represents the in-game time period.告诉 AI 不要自行改变时间——只有行为可以

变量 2:回合计数器

字段原因
名称Turn Counter供你自己参考
IDturn_counter每回合递增,到 3 时重置
类型Number需要算术运算
默认值0从 0 开始计数
分类Custom时间系统专用分类
行为规则Do not modify this variable. It is controlled automatically by the day-night cycle system.防止 AI 篡改

为什么用计数器而不是直接每 3 回合触发?

计数器 + 变量越过阈值的方式更灵活。假设你以后想要「白天 3 回合,夜晚 5 回合」——只需在行为中添加条件检查。turn-count 触发器更简单但适应性更低。两种方式各有优势;选择适合你需求的即可。


第 2 步:创建四个时段知识条目

每个时段需要一个知识条目来描述该时段的氛围。只有「Morning」默认启用;其他三个默认禁用。

编辑器 → 条目标签页 → 逐一创建条目

条目 1:早晨氛围

字段原因
名称Morning Atmosphere供你自己参考
段落Presets预设段落中的条目每次都会发送给 AI
启用(开启开关)游戏从早晨开始,所以这个默认启用

内容:

[Current Time Period: Morning]
It is early morning. Reflect the following atmosphere when describing the scene:
- Soft morning light spills in from the east; the air is fresh and cool
- Dewdrops cling to blades of grass and flower petals, refracting tiny glints of light
- Birds sing in the branches; a rooster crows in the distance
- NPCs are just waking up, shops are opening one by one, foot traffic is picking up
- The overall mood is peaceful and full of hope

条目 2:中午氛围

字段原因
名称Noon Atmosphere供你自己参考
段落Presets预设段落
启用(关闭开关)行为会在时段切换时启用它

内容:

[Current Time Period: Noon]
It is midday. Reflect the following atmosphere when describing the scene:
- The sun beats down directly overhead; the light is searing and bright, the ground reflecting a blinding white glare
- The air is stifling; distant scenery shimmers and warps in the heat haze
- Most people have retreated to the shade to rest; the streets are quieter than morning
- Taverns and eateries are at their busiest, the smell of food drifting through the air
- The overall mood is languid and sweltering

条目 3:傍晚氛围

字段原因
名称Evening Atmosphere供你自己参考
段落Presets预设段落
启用(关闭开关)行为会启用它

内容:

[Current Time Period: Evening]
It is dusk. Reflect the following atmosphere when describing the scene:
- The setting sun paints the sky in shades of orange-red and purple, clouds rimmed with gold
- Long shadows stretch from buildings and trees
- Flocks of birds sweep across the sky heading home; wisps of cooking smoke rise from rooftops
- NPCs are wrapping up for the day, heading home; children chase each other through the streets
- The overall mood is warm, nostalgic, tinged with a gentle melancholy

条目 4:夜晚氛围

字段原因
名称Night Atmosphere供你自己参考
段落Presets预设段落
启用(关闭开关)行为会启用它

内容:

[Current Time Period: Night]
It is deep night. Reflect the following atmosphere when describing the scene:
- Moonlight and starlight are the only natural sources of illumination, casting a silvery glow over everything
- Most buildings have gone dark; the occasional window glows with dim candlelight
- A cool night breeze carries the chorus of crickets and frogs
- The streets are nearly deserted; night-watch guards pace slowly by, torches in hand
- Danger may lurk in the shadows — wild beasts, thieves, or something stranger still
- The overall mood is mysterious, hushed, and laced with hidden peril

为什么只有「Morning」默认启用? 因为游戏从早晨开始。如果四个条目同时启用,AI 会同时收到早晨、中午、傍晚和夜晚的描述,不知道该遵循哪一个。一次只启用一个可以确保 AI 锁定正确的氛围。


第 3 步:(可选)上传各时段 BGM

如果你想让每个时段有自己的背景音乐,先上传音频文件。

编辑器 → 音频标签页 → 添加曲目

曲目 ID名称类型循环淡入淡出
bgm_morningMorning ThemeBGM2s2s
bgm_noonAfternoon ThemeBGM2s2s
bgm_eveningDusk ThemeBGM2s2s
bgm_nightNight ThemeBGM2s2s

没有音频文件? 跳过这一步。昼夜循环的核心是知识条目切换——BGM 是锦上添花。你随时可以后续添加。

在 BGM 播放列表中,将 autoPlay 设为 true,默认播放 bgm_morning。后续时段切换时,行为会使用 crossfade 动作平滑过渡曲目。


第 4 步:创建行为

这是系统的核心。我们需要 2 个行为——实际上是 6 个。继续阅读。

编辑器 → 行为标签页 → 逐一添加行为

行为 1:每回合计数

这个非常简单——每个对话回合后,计数器加 1。

WHEN(何时检查):

字段原因
触发类型Every turn每次玩家-AI 交互后自动触发

DO(做什么):

动作类型设置效果
修改变量turn_counter add 1计数器 +1

这是唯一的动作。没有条件,没有额外配置。它忠实地每回合加 1。

为什么不在这里就检查「到 3 了吗?」 因为行为系统的设计哲学是一个行为做一件事。递增计数器是一个行为的工作;检查是否到了 3 是另一个行为的工作。引擎会自动链接它们——计数器递增后,如果值越过了阈值,另一个行为就会触发。


行为 2:推进时段

这个行为在计数器达到 3 时触发,执行所有切换逻辑。

WHEN(何时检查):

字段原因
触发类型Variable crossed thresholdturn_counter 超过阈值时触发
变量turn_counter我们监控的变量
方向Rises above当值从低于阈值变为高于阈值时触发
阈值2当 turn_counter 从 2 变为 3 时触发(超过 2)

为什么阈值是 2 而不是 3? variable-crossed 中的「rises above」方向检测值从 <= 阈值变为 > 阈值的时刻。当 turn_counter 从 2 变为 3 时,它「rises above 2」——即从 <=2 变为 >2。如果你把阈值设为 3,则需要 turn_counter 从 3 变为 4 才会触发,那不是我们想要的。

DO(做什么):

这个行为需要做很多事。按顺序添加这些动作:

#动作类型设置效果
1修改变量turn_counter set to 0重置计数器,开始下一个 3 回合倒计时
2禁用知识条目Morning Atmosphere关闭所有时段条目
3禁用知识条目Noon Atmosphere全部关闭
4禁用知识条目Evening Atmosphere全部关闭
5禁用知识条目Night Atmosphere全部关闭

等等——这把四个都关了。它怎么知道该启用哪一个?

好问题。这就是 ONLY IF 条件用于分支的地方。但单个行为只能有一组动作。所以我们将「推进时段」拆成 5 个行为:1 个重置计数器并禁用所有条目,4 个启用对应的时段。

让我重新组织:


完整行为列表(共 6 个):

行为 1:每回合计数

(同上——不重复。)

行为 2:推进——早晨 → 中午

WHEN:

字段
触发类型Variable crossed threshold
变量turn_counter
方向Rises above
阈值2

ONLY IF:

变量运算符
time_period等于 (eq)Morning

DO:

#动作类型设置效果
1修改变量turn_counter set to 0重置计数器
2修改变量time_period set to Noon推进到下一个时段
3禁用知识条目Morning Atmosphere关闭旧时段条目
4启用知识条目Noon Atmosphere开启新时段条目
5播放音乐bgm_noon,操作:crossfade,时长 3s淡入淡出切换到中午 BGM
6Tell AI内容:Time has advanced from Morning to Noon. Naturally reflect this time change in your upcoming descriptions.让 AI 平滑过渡

行为 3:推进——中午 → 傍晚

WHEN: 与行为 2 相同(variable crossed threshold,turn_counter rises above 2)

ONLY IF:

变量运算符
time_period等于 (eq)Noon

DO:

#动作类型设置效果
1修改变量turn_counter set to 0重置计数器
2修改变量time_period set to Evening推进到傍晚
3禁用知识条目Noon Atmosphere关闭中午条目
4启用知识条目Evening Atmosphere开启傍晚条目
5播放音乐bgm_evening,操作:crossfade,时长 3s淡入淡出切换 BGM
6Tell AI内容:Time has advanced from Noon to Evening. Naturally reflect this time change in your upcoming descriptions.AI 过渡

行为 4:推进——傍晚 → 夜晚

WHEN: 同上

ONLY IF:

变量运算符
time_period等于 (eq)Evening

DO:

#动作类型设置效果
1修改变量turn_counter set to 0重置计数器
2修改变量time_period set to Night推进到夜晚
3禁用知识条目Evening Atmosphere关闭傍晚条目
4启用知识条目Night Atmosphere开启夜晚条目
5播放音乐bgm_night,操作:crossfade,时长 3s淡入淡出切换 BGM
6Tell AI内容:Time has advanced from Evening to Night. Naturally reflect this time change in your upcoming descriptions.AI 过渡

行为 5:推进——夜晚 → 早晨

WHEN: 同上

ONLY IF:

变量运算符
time_period等于 (eq)Night

DO:

#动作类型设置效果
1修改变量turn_counter set to 0重置计数器
2修改变量time_period set to Morning循环回到早晨
3禁用知识条目Night Atmosphere关闭夜晚条目
4启用知识条目Morning Atmosphere开启早晨条目
5播放音乐bgm_morning,操作:crossfade,时长 3s淡入淡出切换 BGM
6Tell AI内容:Time has advanced from Night to Morning — a new day begins. Naturally reflect this time change in your upcoming descriptions.AI 过渡

为什么拆成 4 个行为? 因为每个时段过渡需要启用不同的条目和播放不同的 BGM。单个行为只能有一组条件和一组动作——不支持 if-else 分支。所以我们用 4 个带有不同 ONLY IF 条件的行为来模拟分支:当同一个触发器触发时(计数器超过 2),引擎检查所有行为,但只有 time_period 匹配的那个会执行。

行为 6:会话初始化

这个行为在会话开始时设置初始状态,确保变量对新会话或重新进入都有正确的起始值。

WHEN:

字段原因
触发类型Session start (session-start)新会话开始时自动触发一次

DO:

#动作类型设置效果
1修改变量time_period set to Morning确保从早晨开始
2修改变量turn_counter set to 0重置回合计数器

为什么需要会话初始化行为? 变量默认值只在首次创建时生效。如果玩家中途退出并开始新会话,变量可能保留之前的值(例如 time_period 停在「Night」,turn_counter 停在 2)。会话初始化行为确保每个新会话都从早晨开始,计数器为 0。

行为优先级

所有 4 个推进行为可以保持默认优先级(0)。它们的 ONLY IF 条件互斥——当前时段只能匹配其中一个,所以不会冲突。


第 5 步:在 Root Component 中添加时间徽章

在聊天中最后一条消息上显示当前时段的图标,让玩家一眼就能知道现在是什么时间。

编辑器 → Custom UI 部分 → 打开 index.tsx → 粘贴以下内容(替换默认的 return <Chat />):

tsx
export default function MyWorld() {
  const api = useYumina();

  // ---- 读取变量 ----
  const timePeriod = String(api.variables.time_period || "Morning");

  // ---- 时段图标和颜色映射 ----
  const timeConfig = {
    "Morning": { icon: "☀️", label: "Morning", color: "#fbbf24", bg: "rgba(251,191,36,0.15)" },
    "Noon": { icon: "🌤️", label: "Noon", color: "#f59e0b", bg: "rgba(245,158,11,0.15)" },
    "Evening": { icon: "🌅", label: "Evening", color: "#f97316", bg: "rgba(249,115,22,0.15)" },
    "Night": { icon: "🌙", label: "Night", color: "#818cf8", bg: "rgba(129,140,248,0.15)" },
  };

  const current = timeConfig[timePeriod] || timeConfig["Morning"];
  const msgs = api.messages || [];

  return (
    <Chat renderBubble={(msg) => {
      const isLastMsg = msg.messageIndex === msgs.length - 1;

      return (
        <div>
          {/* 正常渲染消息文本——contentHtml 是已经渲染好的 HTML */}
          <div
            style={{ color: "#e2e8f0", lineHeight: 1.7 }}
            dangerouslySetInnerHTML={{ __html: msg.contentHtml }}
          />

          {/* 时间徽章——只在最后一条消息上显示 */}
          {isLastMsg && (
            <div style={{
              display: "inline-flex",
              alignItems: "center",
              gap: "6px",
              marginTop: "12px",
              padding: "4px 12px",
              background: current.bg,
              border: `1px solid ${current.color}33`,
              borderRadius: "999px",
              fontSize: "13px",
              color: current.color,
              fontWeight: "600",
            }}>
              <span style={{ fontSize: "16px" }}>{current.icon}</span>
              <span>{current.label}</span>
            </div>
          )}
        </div>
      );
    }} />
  );
}

逐行说明:

  • api.variables.time_period — 读取当前时段变量
  • timeConfig — 一个查找表,将每个时段映射到图标、文本标签和颜色。你可以随意更改颜色以匹配你的世界风格
  • isLastMsg — 只在最后一条消息上显示徽章,不是每条消息都显示
  • 徽章使用 inline-flex + border-radius: 999px 实现药丸形状——简洁但一目了然

想在每条消息上都显示时间?

去掉 {isLastMsg && ...} 检查,直接把徽章放在 return 中。这样每条消息都会带有时间标记,就像聊天记录中的时间戳一样。


第 6 步:保存并测试

  1. 点击编辑器顶部的保存
  2. 点击开始游戏或返回主页打开新会话
  3. 正常与 AI 聊天。前 2 个回合,时间徽章显示「☀️ Morning」,没有变化
  4. 第 3 个回合后——时间推进到「🌤️ Noon」,AI 的下一个回复会自然反映时间变化
  5. 再过 3 个回合——推进到「🌅 Evening」
  6. 再过 3 个回合——推进到「🌙 Night」。如果你设置了 BGM,应该能听到淡入淡出的过渡
  7. 再过 3 个回合——循环回到「☀️ Morning」,新的一天开始

如果出了问题:

症状可能原因解决方法
时间永远不变「每回合计数」行为没有触发检查行为 1 的触发器是否设为「Every turn」且行为已启用
第 3 回合没有切换阈值设置错误确认「variable crossed threshold」的阈值是 2(不是 3),方向是「rises above」
切换后条目没变条目名称不匹配检查行为中的「Enable lore entry」/「Disable lore entry」动作引用的条目名称是否正确
4 个行为同时触发缺少 ONLY IF 条件每个推进行为必须有 ONLY IF 条件限制当前 time_period 的值
时间徽章不可见Root Component 语法错误检查 Custom UI 面板底部的编译状态——应该显示绿色的「OK」
BGM 没切换曲目 ID 不匹配或没有上传音频确认行为中的 trackId 与音频标签页中的曲目 ID 一致

方式 B 对比:使用 turn-count 触发器

如果方式 A 觉得行为太多,可以使用更简单的方式 B。

区别:

方式 A(本教程)方式 B
触发器every-turn + variable-crossedturn-count (everyNTurns=3)
需要 turn_counter 变量
行为数量6(1 个计数 + 4 个推进 + 1 个可选初始化)4(4 个推进)
灵活性高(间隔可以动态调整)低(间隔固定为 N)
适合需要动态时间流速的世界固定节奏的世界

方式 B 怎么做:

删除 turn_counter 变量和「每回合计数」行为。将所有 4 个推进行为的触发器改为:

字段
触发类型Every N turns
everyNTurns3

其他所有内容(ONLY IF 条件、DO 动作)与方式 A 完全相同。turn-count 触发器每 3 回合自动触发——不需要手动计数。

turn-count 触发器如何工作: 引擎维护一个内部的全局回合计数。当你设置 everyNTurns: 3 时,引擎在第 3、6、9、12 回合等自动触发行为。你不需要自己管理计数器变量。


快速参考

你想做什么怎么做
每回合做某事行为触发器:「Every turn」(every-turn)
每 N 回合做某事行为触发器:「Every N turns」(turn-count),设置 everyNTurns
检测变量越过某个值行为触发器:「Variable crossed threshold」(variable-crossed),设置变量、方向和阈值
切换知识条目动作:「Enable lore entry」/「Disable lore entry」
淡入淡出音乐动作:「Play music」,操作:crossfade,设置淡入淡出时长
让 AI 知道发生了什么动作:「Tell AI」,写一条临时系统指令
在消息上显示状态徽章在 Root Component 的 <Chat renderBubble> 中读取变量并用 JSX 渲染
模拟 if-else 分支多个行为共享同一触发器 + 不同的 ONLY IF 条件

试试看——可导入的演示世界

下载这个 JSON 并作为新世界导入,查看完整效果:

recipe-9-demo.json

如何导入:

  1. 前往 Yumina → 我的世界创建新世界
  2. 在编辑器中,点击更多操作导入包
  3. 选择下载的 .json 文件
  4. 新世界会被创建,所有变量、条目、行为和 Root Component 都已预配置
  5. 开始新会话并试试看

包含内容:

  • 2 个变量(time_period 追踪当前时段,turn_counter 作为回合计数器)
  • 4 个知识条目(早晨 / 中午 / 傍晚 / 夜晚氛围,只有早晨默认启用)
  • 6 个行为(1 个每回合计数 + 4 个时段推进 + 1 个会话初始化)
  • 一个 Root Component(<Chat renderBubble> 中的时段图标徽章)
  • 4 个 BGM 曲目(你需要上传自己的音频文件来替换 URL)

这是教程 #9

本教程展示了行为系统的链式连接能力——通过简单的计数器 + 阈值触发器 + 条件分支,你可以构建一个全自动的时间系统。相同的模式适用于天气变化、季节循环、NPC 情绪波动,或任何「按节奏自动变化」的功能。