今日速览
- 对话系统两大路线:装 Dialogic 插件 vs 自研轻量系统。独立游戏建议先自研,代码不到 200 行,完全可控,剧情用 JSON 存,改剧情不用碰代码。
- 数据驱动是核心思想:对话内容(谁说的、说什么、有哪些选项、跳去哪)全部放 JSON,逻辑层只负责「读数据 → 显示 → 等输入 → 跳转」,DeepSeek 最擅长批量产出这种结构化数据。
- 打字机效果用
visible_ratio:RichTextLabel 不用逐字 append,设置visible_ratio(0→1)配合 Timer 就能平滑逐字显示,性能好且天然支持 BBCode 着色。 - 分支 = 按钮数组:解析到
choices字段时动态生成 Button,点击后按next_id跳转,用 Godot 信号把「对话结束」广播给游戏世界。 - DeepSeek 是对话流水线的「编剧 + 校对」:生成 JSON 剧情、把大白话剧本转成分支结构、检查 next 引用是否断裂,三件事都能干。
核心学习内容
1. 先想清楚:对话数据长什么样
对话系统最忌讳把台词硬编码在代码里。推荐按「节点图」设计 JSON:每一段话是一个节点,有唯一 id;普通对话只有一个 next;有选项的对话带 choices 数组,每个选项指向下一个节点。示例:
{
"nodes": {
"intro": {
"speaker": "旅店老板娘",
"text": "外乡人,[b]深山里[/b]最近不太平,劝你别往东边走。",
"choices": [
{ "text": "多谢提醒,我偏要去看看。", "next": "brave" },
{ "text": "那……往西边有什么?", "next": "west" }
]
},
"brave": { "speaker": "老板娘", "text": "唉,又一个不听劝的。", "next": "end" },
"west": { "speaker": "老板娘", "text": "西边是矿场,工头老周欠我酒钱,你顺道帮我讨?", "next": "quest_offer" }
},
"start": "intro"
}
text 里可以直接写 BBCode(如 [b] 加粗),RichTextLabel 原生解析;speaker 留给头顶名牌或立绘;还能扩展 event 字段(如 {"event": "give_item", "item": "钥匙"})让对话触发游戏逻辑——数据层只描述「发生了什么」,由代码去执行。
2. 打字机效果:RichTextLabel + Timer
Godot 官方类文档里 RichTextLabel 支持 BBCode 与逐段排版,做打字机最优雅的方式不是逐字 append_text,而是:
@onready var label: RichTextLabel = $RichTextLabel
@onready var type_timer: Timer = $TypeTimer
func show_line(text: String, chars_per_sec: float = 40.0) -> void:
label.text = text # 一次全设进去
label.visible_ratio = 0.0 # 先全部隐藏
type_timer.wait_time = 1.0 / chars_per_sec
type_timer.start()
func _on_type_timer_timeout() -> void:
label.visible_ratio += 0.05 # 每次 tick 多显示 5%
if label.visible_ratio >= 1.0:
type_timer.stop()
玩家按键时若 visible_ratio < 1.0 就一次拉满(跳过打字),否则才推进下一句——这是所有商业 RPG 都用的「点击跳过 / 点击继续」双态逻辑。
3. 分支选项与对话管理器
用 JSON 解析(Godot JSON 类:JSON.parse_string() 一次拿回 Dictionary;出错时 JSON.new().parse() 能拿到 get_error_line() 精确定位)读入数据后,一个 DialogueManager(Autoload 单例)负责状态机:current_id → 查表 → 显示文本 → 若有 choices 清空并生成按钮(把整段对话 UI 的 process_mode 设为暂停游戏也可行,或走信号让玩家暂时无法移动):
# DialogueBox.gd(挂在对话 UI 场景根节点)
signal dialogue_finished
func _ready() -> void:
for child in %Choices.get_children():
child.queue_free()
func show_choices(choices: Array) -> void:
for c in choices:
var btn := Button.new()
btn.text = c.text
btn.pressed.connect(func(): _go_to(c.next))
%Choices.add_child(btn)
func _go_to(next_id: String) -> void:
if next_id == "end":
hide()
dialogue_finished.emit()
return
var node: Dictionary = data.nodes[next_id]
# 更新名牌、显示文本、必要时 show_choices(node.get("choices", []))
关键 API 一句话总结:数据查表用 Dictionary、打字用 visible_ratio、分支用动态 Button、跨场景通知用信号——四件套凑齐,一个够用的对话系统就落地了。
🎯 DeepSeek 提示词
提示词 1:生成对话 JSON(编剧模式)
你是游戏剧情编剧,擅长写 RPG 对话。请为以下设定生成 Godot 对话数据:
设定:{旅店老板娘警告主角东边有危险,主角可选择「强行东行」或「打听西边矿场」,两条线各延伸 2 句后汇合到任务发放}
要求:
1. 输出严格 JSON,结构为 { "nodes": { 节点id: {speaker, text, next?} 或 {speaker, text, choices:[{text,next}]} }, "start": "起始id" }
2. 节点 id 用英文小写短横线命名;text 可用 [b][i][color=...] 等 BBCode 增强语气
3. choices 每个选项必须指向真实存在的 next;对话结束用 next 值 "end"
4. 台词口语化、有性格,不要书面腔;总量 8-12 个节点
5. 只输出 JSON,不要解释,不要 markdown 代码块包裹
使用说明:把「设定」换成你的场景,产出直接存成 dialogue/chapter1.json。注意要求「只输出 JSON」,方便你直接复制文件,省去手删解释文字。
提示词 2:写对话管理器代码(程序员模式)
你是资深 Godot 4 开发者。请用 GDScript 为我的对话系统写一个 DialogueManager(Autoload 单例)+ 配套 UI 脚本:
功能需求:
1. load_dialogue(path) 用 JSON 类加载对话文件,解析失败时打印行号(get_error_line)
2. start(id) 从指定节点开始播放;每句显示在 RichTextLabel,用 visible_ratio + Timer 实现打字机,40 字/秒
3. 左键/空格:打字中→立即显示全文;打字完→无选项则推进到 next,有 choices 则在 VBoxContainer 生成 Button
4. 说话时通过信号 player_input_locked(true/false) 通知主角控制器锁输入
5. 对话结束发 dialogue_finished 信号并隐藏 UI
约束:只用 Godot 4.x 内置节点,不依赖插件;每个类文件顶部注明用途;关键处写中文注释;先输出完整代码再给 3 行使用说明。
使用说明:一次拿到「管理器 + UI + 输入锁」全套代码。建议让 DeepSeek 分两个文件输出,或你手动把类拆开,保持 Godot「一场景一脚本」的工程习惯。
提示词 3:对话 JSON 查错(校对模式)
你是代码审查员。请检查下面这份 Godot 对话 JSON 的逻辑错误,只报告问题,不要重写整个文件:
[在此粘贴你的 dialogue JSON]
检查项:
1. choices[].next 和 next 是否都指向真实存在的节点 id("end" 除外)
2. 是否存在无法从 start 到达的「死节点」(孤儿剧情)
3. 是否存在循环死锁(A→B→A 永无出口)
4. speaker 字段是否缺失或为空
输出格式:每条错误一行:节点id | 错误类型 | 建议修复方式;没有错误就输出「未发现问题」。
使用说明:对话文件一大,人工查漏必然眼花。每次用 DeepSeek 扩写剧情后都跑一遍这条提示词,能把「玩家卡死在对话里」这类低级事故消灭在上线前。
学习心得
做 Steam 游戏,剧情向玩法几乎是单人开发者的「性价比之王」——不需要海量美术,靠文字和分支就能撑起几十小时的体验。而对话系统恰好是「AI 协作最顺」的模块:数据结构是我定的(JSON 规范),内容生产是 DeepSeek 的(编剧情、写台词),代码逻辑是模板化的(查表 + 打字机 + 按钮)。今天学的这套四件套(Dictionary 查表 / visible_ratio 打字 / 动态 Button 分支 / 信号广播),可以直接套进任何一个剧情游戏原型里;以后想升级成 Dialogic 插件或加立绘、配音,也只是在数据层加字段的事。
明日关注
- 给对话系统加「任务发放」联动:JSON 加
event字段后,如何用信号把事件派发给任务系统(复习第 16 天 Signal/Autoload) - 研究对话中插入立绘/头像切换:TextureRect 换图 + 简单的淡入 Tween 动效
- 用提示词 1 给「自己正在做的游戏」写一章完整开场对话,验证 JSON 规范是否够用
本文由自动化学习流水线每日生成,坚持即学即记。
评论