消息渲染器 vs 应用组件
两种自定义 UI 的方式,看起来很像,但用法完全不同。这篇帮你搞清楚它们的区别,以及什么时候该用哪个。
一句话总结
- 消息渲染器(在编辑器中称为 Message Template / 消息模板)(
surface: "message"):改变每条 AI 消息的样子。用在普通聊天模式。 - 应用组件(
surface: "app"):接管整个屏幕,做独立的游戏界面。
两者都存储在同一个 customUI 数组中——由 surface 字段决定组件属于哪种模式。
它们的关系
每个自定义组件都有一个 surface 字段——值为 "message" 或 "app":
surface: "message"
→ 玩家看到:普通聊天界面
→ 你的组件用自定义样式渲染每条消息
→ 每个世界只能有一个 message surface 组件
surface: "app"
→ 玩家看到:全屏自定义界面(没有聊天框、没有输入框)
→ 你的组件占满整个屏幕
→ 每个世界仅能有 1 个 app surface 组件如果任何可见的组件的 surface 为 "app",世界会自动进入全屏模式。否则,使用默认聊天界面,消息渲染器生效。
详细对比
消息渲染器(surface: "message") | 应用组件(surface: "app") | |
|---|---|---|
| 在编辑器哪里设置 | 编辑器 → 组件 → 添加 → 选择 Message | 编辑器 → 组件 → 添加 → 选择 App |
| 数量 | 只能有 1 个 | 仅 1 个 |
| 什么时候显示 | 普通聊天模式(没有应用组件时) | 当任何应用组件存在且可见时 |
| 显示位置 | 替换每条消息的渲染 | 占满整个屏幕 |
| 聊天界面 | 正常显示(消息列表 + 输入框) | 完全隐藏 |
| 能拿到的数据 | 每条消息的 content、role、messageIndex + useYumina() | 只有 useYumina()(没有单条消息数据) |
| 典型用途 | 气泡对话、状态面板、战斗日志、交互按钮 | 视觉小说全屏界面、完整游戏 UI |
消息渲染器:怎么工作
消息渲染器是一段 TSX 代码,替换默认的 Markdown 渲染。每当 AI 发了一条消息,引擎不再用纯文字显示,而是把消息内容交给你的代码来决定怎么画。
它是逐条调用的。 聊天里有 10 条消息,你的渲染器就会被调用 10 次,每次拿到不同的 content 和 messageIndex。
收到的数据
export default function Renderer({
content, // 这条消息的文字(已去掉指令)
role, // "user" 或 "assistant"
messageIndex, // 这是第几条消息(从 0 开始)
renderMarkdown, // 内置函数:把 Markdown 转成 HTML
isStreaming, // AI 是否正在打字
}) {
const api = useYumina(); // 还可以拿到游戏状态和交互 API
// ...
}适合做什么
- 在消息上方/下方加状态面板(HP 条、金币显示)
- 把消息改成气泡对话样式
- 在第一条消息上放路线选择按钮
- 在最后一条消息下面放交互输入框
- 战斗日志、带颜色标记的文字
- 任何需要和聊天消息共存的 UI
关键限制
- 只能有 1 个 — 不能同时用多个渲染器
- 有应用组件时不工作 — 如果任何可见的
surface: "app"组件存在,渲染器不会被调用 - 每条消息都调用 — 如果你只想在某条消息上显示内容,需要自己用
messageIndex或isLastMsg判断
应用组件:怎么工作
应用组件是完全独立的 UI 面板。它不替换消息渲染,而是接管整个屏幕——聊天消息列表、输入框、甚至顶部导航栏全部消失,只剩你的组件。
有可见的应用组件时会自动激活。 只要任何组件的 surface 为 "app" 且可见,世界就会进入全屏模式。不需要手动切换。
收到的数据
export default function MyComponent() {
const api = useYumina();
// api.variables — 游戏状态
// api.sendMessage() — 发消息(触发 AI 回复)
// api.setVariable() — 改变量
// api.executeAction() — 触发行为
// api.messages — 所有消息历史
// api.isStreaming — AI 是否在打字
// api.streamingContent — AI 正在打的内容
// ...
}注意:它没有 content、role、messageIndex、renderMarkdown 这些参数。因为它不是在渲染某条消息——它是一个独立的全屏界面。
适合做什么
- 视觉小说全屏界面(背景 + 立绘 + 对话框 + 选项)
- 完整的游戏 UI(地图 + 状态栏 + 物品栏 + 聊天窗口全部自己画)
- 任何不想要默认聊天界面的场景
关键限制
- 聊天界面消失 — 玩家看不到消息列表和输入框,你需要自己用
api.messages和api.sendMessage()来处理聊天 - ESC 会退出世界 — 全屏模式下按 ESC 不是退出全屏,而是离开这个世界
怎么选?
你想做的事 → 用哪个
─────────────────────
在消息上加 HP 条 → 消息渲染器 (surface: "message")
在消息下加按钮 → 消息渲染器 (surface: "message")
改变消息的外观(气泡、颜色) → 消息渲染器 (surface: "message")
做一个全屏视觉小说 → 应用组件 (surface: "app")
做一个完整的游戏界面 → 应用组件 (surface: "app")
保留聊天界面但加个侧边栏 → 目前不支持(应用组件只有全屏模式)大多数创作者只需要消息渲染器。 应用组件是给想要完全掌控界面的高级创作者准备的。如果你不确定用哪个,先用消息渲染器。
在编辑器里怎么设置
消息渲染器
- 打开编辑器 → 组件 区域
- 点击「添加组件」→ 选择 Message
- 在代码编辑器里写 TSX 代码
- 底部显示「编译状态:正常」就说明语法没问题
- 保存,开新会话测试
应用组件
- 打开编辑器 → 组件 区域
- 点击「添加组件」→ 选择 App
- 写 TSX 代码
- 当任何应用组件可见时,世界会自动进入全屏模式
- 保存,开新会话测试
常见误解
"我写了应用组件但看不到"
检查组件的 visible 开关是否已在编辑器中打开。应用组件只有在存在且标记为可见时才会渲染。同时确认组件的 surface 设置为 "app"——"message" surface 的组件不会触发全屏模式。
"我添加了应用组件后聊天消失了"
这是正常行为。应用组件激活时会接管整个屏幕——聊天界面不再渲染。如果你想保留聊天功能,需要在应用组件里自己用 api.messages 和 api.sendMessage() 来实现。
"我能同时用两个吗?"
在当前版本中,不能同时让两者对玩家可见:
- 没有应用组件时 → 消息渲染器生效
- 有应用组件时 → 只有应用组件渲染
如果你想在全屏游戏界面里嵌入一个聊天窗口,需要在应用组件里自己实现。
"它们用同一个 API 吗?"
是的。两者都可以调用 useYumina(),拿到完全相同的 API:sendMessage、setVariable、executeAction、switchGreeting、playAudio、stopAudio、variables、messages 等。
唯一的区别是消息渲染器额外收到单条消息的数据(content、role、messageIndex、renderMarkdown),而应用组件没有这些。
速查表
| 你想做的 | 用什么 | Surface |
|---|---|---|
| 改变消息外观 | 消息渲染器 | "message" |
| 在消息上加按钮/输入框 | 消息渲染器 | "message" |
| 做气泡对话/战斗日志 | 消息渲染器 | "message" |
| 做全屏视觉小说 | 应用组件 | "app" |
| 做完整游戏界面 | 应用组件 | "app" |
| 加状态面板在消息旁边 | 消息渲染器 | "message" |
| 自己实现整个聊天界面 | 应用组件 | "app" |
