关于 dialogue-manager
Dialogue Manager 是面向 Godot 开发者和编剧的对话插件,使用可读的 .dialogue 文本编写分支对话。它内置条件、状态变更及 CSV 或 PO 翻译流程,并提供编辑器标签页和 C# 支持。
主要功能
- 01脚本式 .dialogue 格式
- 02分支回复与条件判断
- 03状态变更和随机台词
- 04CSV 与 PO 翻译流程
- 05编辑器对话标签页
- 06官方 C# 支持
适用场景
- 01以文本编写游戏角色对话
- 02为对话选项设置条件分支
- 03在对话中更新游戏状态
- 04导出对话文本以进行翻译
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jame581/ godotprompter --skill dialogue-manager安装后在 Claude Code 中输入/dialogue-manager确认已加载。
详细介绍
Dialogue Manager
相关技能: 手动编写对话数据结构可使用 dialogue-system,翻译台词可使用 localization,完整的冒险游戏工作流可使用 popochiu。
插件: Dialogue Manager · 版本
v3.10.4· Godot 4.6 · MIT · 源码:https://github.com/nathanhoad/godot_dialogue_manager · GDScript,并官方支持 C#。
1. 何时使用 Dialogue Manager
| 方案 | 最适合 |
|---|---|
dialogue-system 技能(手动编写 Resource 数据) | 完全掌控数据结构、不依赖插件、小型对话树 |
| Dialogue Manager | 类脚本的 .dialogue 文本格式、分支式回复、条件/状态变更、翻译流程、可视化编辑器标签页、官方 C# 封装 |
| 完整的冒险游戏框架 | 需要将房间/物品栏/角色与对话打包在一起的点击式游戏——Dialogue Manager 只负责对话层 |
如果编剧希望以易读的脚本文本(而非通过 Inspector 编辑的 Resource)编写分支对话,并且你希望免费获得内置条件、状态变更、随机台词和翻译工作流(CSV 或 PO),请选择 Dialogue Manager。如果你需要完全掌控的强类型 Resource 对话数据,请改用 dialogue-system。Dialogue Manager 只处理对话,并非完整的冒险/任务框架,因此较大型的点击式游戏框架仍需负责管理房间、物品栏和角色。
2. 安装与设置
- Godot AssetLib → 搜索“Dialogue Manager”→ 下载。或者将 GitHub 仓库中的
addons/dialogue_manager/复制到res://addons/dialogue_manager/。 - Project → Project Settings → Plugins → 启用“Dialogue Manager”。这会添加一个 Dialogue 主屏幕标签页(位于 2D/3D/Script 旁边),并自动注册
DialogueManager自动加载项——无需手动设置自动加载。 addons/dialogue_manager/plugin.cfg:
[plugin]
name="Dialogue Manager"
description="A powerful nonlinear dialogue system"
author="Nathan Hoad"
version="3.10.4"
script="plugin.gd"
- C# 项目无需额外的 NuGet 包——使用
using DialogueManagerRuntime;即可(§4)。 - 设置位于 Project Settings → General → Dialogue Manager:特别是 State Autoload Shortcuts(可在对话中不加前缀直接使用的自动加载项名称)和 Balloon Path(
show_dialogue_balloon()打开的场景——留空则使用内置示例气泡)。
3. .dialogue 语法
从 Dialogue 编辑器选项卡打开或创建 .dialogue 文件。每行的格式为 Character: text 或纯文本
text(旁白)。支持 Godot 的 RichTextLabel BBCode,以及一些额外语法:[[A|B|C]](随机选择行内内容)、
[wait=N] / [wait="ui_accept"](暂停打字)、[speed=N]、[next=auto](自动推进)。
标题和跳转 — ~ name 标记标题;=> name 跳转到该标题;=> END 结束流程;=> END!
会强制结束流程,即使还有待处理的跳转并返回;=>< name 跳转并在该分支遇到 END 时返回此处一次:
~ start
Nathan: Well?
- First one
- Another one => another_title
- Start again => start
=> END
~ another_title
Nathan: Another one?
=> END
响应选项(- )按缩进嵌套,也可以在 [...] 中添加条件 — 将所有跳转放在最后:
Nathan: How many projects have you started and not finished?
- Just a couple
Nathan: That's not so bad.
- A lot [if SomeGlobal.some_property == true]
Nathan: Maybe you should finish one before another.
- Another one [if SomeGlobal.some_method()] => another_title
条件 — if / elif / else、布尔运算符 and/or/(), 以及 match/while 代码块:
if SomeGlobal.some_property >= 10
Nathan: That property is >= 10.
elif SomeGlobal.some_other_property == "some value"
Nathan: Or we might be in here.
else
Nathan: If neither are true, I'll say this.
行内条件:Nathan: I have done this [if already_done]once again[/if],也可以添加可选的
[else]。使用 ?. 进行空值安全的成员访问:if some_node_reference?.name == "SomeNode"。
状态变更 — set 赋值状态,do 调用方法或发出信号;两者都可以行内执行
([do wave()],使用 [do! wave()] 可禁用隐式 await):
if SomeGlobal.has_met_nathan == false
do SomeGlobal.animate("Nathan", "Wave")
Nathan: Hi, I'm Nathan.
set SomeGlobal.has_met_nathan = true
内置状态变更:do wait(float)、do debug(...)。在对话中发出信号:do SomeGlobal.some_signal.emit("arg")。
局部变量与额外游戏状态 — set locals.asked = true 会创建一个单次对话内的临时变量
(这是 example balloon 实现的惯例,并非 Dialogue Manager 核心功能)。传入
extra_game_states 数组的对象会直接按名称引用,其状态变更会在对话结束后保留 — 传入实例,而不是类
(GameStateClass.new(),不要直接传入类)。
随机行 — 前缀 % 表示权重相同,%N 表示相对权重;空行用于分隔随机组:
Nathan: I will say this.
%3 Nathan: This line has a 60% chance of being picked
%2 Nathan: This line has a 40% chance of being picked
变量和标签 — {{SomeGlobal.some_property}} 会将状态插入文本中(也可用作角色名称)。[#happy, #mood=calm] 用于附加标签,可通过 line.get_tag_value("mood") 读取。
4. 运行时 API 和示例气泡
加载 DialogueResource(.dialogue 文件),然后可以让 DialogueManager 为你打开气泡,或通过
await DialogueManager.get_next_dialogue_line(resource, title) 手动获取行。没有下一行时会返回 null — 在 GDScript 中检查假值(if not line: / while line:),在 C# 中检查
line != null。(该标签的 API.md 文档写着“空字典 {}”,但 v3.10.4 源码在每种对话结束路径上都会返回 null — 检查 line == {} 永远不会触发。)
GDScript
# npc.gd
extends Node2D
@export var dialogue_resource: DialogueResource
func _on_interact() -> void:
# Opens the configured balloon (Settings → Balloon Path), or the built-in example balloon.
DialogueManager.show_dialogue_balloon(dialogue_resource, "start")
func _manual_walk() -> void:
# Manual traversal — build a totally custom balloon around this loop.
var line: DialogueLine = await DialogueManager.get_next_dialogue_line(dialogue_resource, "start")
while line:
print("%s: %s" % [line.character, line.text])
if line.responses.is_empty():
line = await DialogueManager.get_next_dialogue_line(dialogue_resource, line.next_id)
else:
var chosen: DialogueResponse = line.responses[0] # replace with real UI selection
line = await DialogueManager.get_next_dialogue_line(dialogue_resource, chosen.next_id)
C#
// Npc.cs
using Godot;
using DialogueManagerRuntime;
public partial class Npc : Node2D
{
[Export] public Resource DialogueResource;
private void OnInteract()
{
DialogueManager.ShowDialogueBalloon(DialogueResource, "start");
}
private async void ManualWalk()
{
var line = await DialogueManager.GetNextDialogueLine(DialogueResource, "start");
while (line != null)
{
GD.Print($"{line.Character}: {line.Text}");
if (line.Responses.Count == 0)
{
line = await DialogueManager.GetNextDialogueLine(DialogueResource, line.NextId);
}
else
{
var chosen = line.Responses[0]; // replace with real UI selection
line = await DialogueManager.GetNextDialogueLine(DialogueResource, chosen.NextId);
}
}
}
}
其他 DialogueManager 方法:show_dialogue_balloon_scene(balloon_scene, resource, title)(打开特定气泡场景)、show_example_dialogue_balloon(resource, title)(强制使用内置气泡)、create_resource_from_text(text)(在运行时编译 .dialogue 字符串 — 遇到语法错误时会失败)。C# 方法名为相同的 PascalCase 形式:ShowDialogueBalloonScene、ShowExampleDialogueBalloon、
CreateResourceFromText。
get_next_dialogue_line 接受 mutation_behaviour 参数 — GDScript 使用 DMConstants.MutationBehaviour,C# 使用 MutationBehaviour(两者默认值均为 Wait,还支持 DoNotWait、Skip)。该枚举位于 DMConstants 上,不在 DialogueManager 自动加载项上。除非你清楚其他值的作用,否则请保留 Wait;示例气泡仅支持 Wait。
5. 信号和类型化访问
DialogueLine 字段:id、next_id、character、text、tags(PackedStringArray)、
translation_key、responses(元素类型未指定的 Array,包含 DialogueResponse)、concurrent_lines。DialogueResponse 在相同的 id/next_id/character/text/
tags/translation_key 字段基础上,还添加了 is_allowed: bool 和 condition_as_text: String。
GDScript
func _ready() -> void:
DialogueManager.dialogue_started.connect(_on_dialogue_started)
DialogueManager.dialogue_ended.connect(_on_dialogue_ended)
DialogueManager.got_dialogue.connect(_on_got_dialogue)
DialogueManager.mutated.connect(_on_mutated)
func _on_dialogue_started(resource: DialogueResource) -> void:
pass
func _on_dialogue_ended(resource: DialogueResource) -> void:
pass
func _on_got_dialogue(line: DialogueLine) -> void:
print(line.character, ": ", line.text, " tags=", line.tags)
func _on_mutated(mutation: Dictionary) -> void:
pass # fires before a `do`/inline mutation runs (not `set` lines)
C#
using DialogueManagerRuntime;
public override void _Ready()
{
DialogueManager.DialogueStarted += (Resource resource) => { };
DialogueManager.DialogueEnded += (Resource resource) => { };
DialogueManager.GotDialogue += (DialogueLine line) =>
{
GD.Print($"{line.Character}: {line.Text} tags={line.Tags}");
};
DialogueManager.Mutated += (Godot.Collections.Dictionary mutation) => { };
}
⚠️ 这些 C# 事件会在首次访问
DialogueManager.Instance时,延迟连接到底层 Godot 信号。仅在此处订阅、随后通过 GDScript 驱动对话的项目不会访问Instance,因此处理程序不会触发。至少要通过 C# 访问一次 API (例如_ = DialogueManager.Instance;,或通过 C# 调用GetNextDialogueLine/ShowDialogueBalloon来运行对话),以完成连接。
内置的响应菜单节点只将 response_selected 暴露为 Godot 信号,因此应使用
Connect + Callable 连接,而不要使用 C# 事件处理程序:
responsesMenu.Connect("response_selected", Callable.From((DialogueResponse response) =>
{
// advance using response.NextId
}));
每次遇到 ~ title 标记时,都会触发
passed_title(title)(GDScript)/ DialogueManager.PassedTitle += (string title) => { }(C#),
可用于分析或保存点书签。
6. 翻译
默认情况下,所有对话/响应文本都会通过 Godot 的 tr() 处理。DialogueManager.translation_source
属性用于选择后端——其枚举类型为 GDScript 的 DMConstants.TranslationSource / C#
的 TranslationSource(None、CSV、PO、默认值 Guess),而不是 DialogueManager.TranslationSource。
Guess 会检查项目设置中的区域设置,查找 PO 文件;如果找不到,则回退到 CSV。
每行的静态 ID(Nathan: Hi! I'm Nathan. [ID:HI_IM_NATHAN])可提供稳定的 translation_key,
用于匹配配音台词以及 CSV/PO 往返处理,而不是以字面文本作为键。.dialogue 文件会自动注册到 POT
生成列表中;对话行前的 ## 注释行会在导出的 PO/POT 中成为
#. TRANSLATORS: 注释。可从编辑器的 Translations 菜单(Dialogue 选项卡)导出/导入 CSV;
重新导入时,只会更新带有静态 ID 的行。
有关 TranslationServer、区域设置切换和 RTL,请参阅 localization 技能。Dialogue Manager
只负责生成翻译键以及导出 CSV/PO;将区域设置变更接入运行中的游戏则由
localization 负责。
实现检查清单
- 在 Project Settings → Plugins 中启用 Dialogue Manager 插件(会自动注册
DialogueManager自动加载项) - 通过
load()/preload()加载.dialogue资源,不要手动解析 - 每次调用
get_next_dialogue_line/GetNextDialogueLine时都使用await - 手动遍历循环通过
null返回值检测对话是否结束(GDScript 中使用if not line:,C# 中使用line != null)——绝不要与{}比较 - 响应行应将
[if condition]放在任何=> target跳转之前,绝不要放在之后 -
extra_game_states条目必须是实例(GameStateClass.new()),不能是裸类 - 对话读取/写入的 C# 状态属性都应带有
[Export] - C# 代码应使用
using DialogueManagerRuntime;和 PascalCase 成员(ShowDialogueBalloon、GetNextDialogueLine、DialogueStarted、GotDialogue) - 自定义气泡应在 Settings 中覆盖 Balloon Path,而不是分叉
show_dialogue_balloon调用点 - 为任何需要配音或稳定 CSV/PO 键的行添加静态翻译 ID(
[ID:KEY]) - 内联
do/await修改若不应阻塞调用方,则使用do!