Day 4 · 共 10 节

NPC

让村子里出现第一个会说话的人:靠近、按键、弹出对白。故事从一张嘴开始。

学习目标

搭建 NPC 场景(外观 + Area2D 交互范围)
做出可复用的底部对话框 UI
打通靠近 → 按 E → 翻页对白 → 关闭的闭环

今日课程

今天怎么学

承接 Day3 可移动玩家。今天只加人说话,不做任务网。每节验收一个机关,最后焊成闭环。

Lesson 1 · 为什么世界需要 NPC

前三天,你有了厨房、院子和会走的演员。可如果院子里只有你一个人,故事很难开始。NPC(非玩家角色)就是村里的其他人:店小二、捕快、说书人。他们不一定会走路打架,但他们能说话、给线索、让世界显得有人烟

今天的完成定义请置顶:走进店小二附近,按下交互键,屏幕弹出至少一句对白;再按一次可关闭或翻到下一句。不做任务系统网、不做好感度、不做分支恋爱。先打通「靠近 → 按键 → 看见字」。

导演为什么要先做对话壳?因为 RPG 的「故事感」大量来自说话。战斗可以后天补,但一个会说话的人,立刻让 Demo 从技术验证变成可讲述的世界。AI 很擅长写对白,你要做的是搭好舞台机关,让字能出现在正确的时机。

村庄店小二对话场景
村里有了会说话的人,故事才开始
NPC 交互示意
靠近范围 → 按键 → 对话出现
放置 NPC 检测靠近 弹对话框
知识点

NPC 交互本质是「触发器 + UI」。触发器用 Area2D 最常见;UI 用 Control/Label/Panel。别把对白写死在十个地方,先打通一条链路,再扩展多段文本。

承接检查:玩家能走、墙基本不穿。若移动不稳,先回 Day3 补课。对话系统建立在可站位的世界之上,站不稳就谈不了话。

AI Prompt
我是零基础,Godot 4 古装 RPG。玩家已能在村庄移动。请用生活比喻说明 NPC 对话最小闭环是什么,并给我今天不超过 6 条的验收清单(每条可打勾)。不要引入任务系统。

把完成定义抄进备忘录。任何「顺便做任务栏」的冲动,都先写到以后。今日只为人民服务:让一个人开口。

记住:NPC 不是「更复杂的玩家」。他可以不会走、不会打,只要能在正确时机把话说出来。范围缩小,成功率上升。这是本课程反复训练的导演肌肉。

开工顺序固定:结构 → 检测 → 按键 → UI → 台词 → 联调。顺序乱了,排错成本会翻倍。把顺序贴在显示器下方也行。

还有一层意义:NPC 对话是你指挥 AI 写「内容」而不仅是「代码」的开始。以前你让 AI 写脚本,今天你让 AI 写台词,再把台词塞进机关。导演同时管技术组与编剧组,正是本课程的培养目标。

如果你害怕做 UI,把它想成手机底部弹一条通知。先丑后美,先有后好。丑而可用的对话框,胜过精美却永远「明天再接」的设计稿。

本节小练

写下今日 Done 一句话。写不清就还没准备好开工——开口的标准,比台词华丽更重要

Lesson 2 · NPC 用什么节点拼

一个最小会说话的 NPC,通常包含:根节点(Node2D 或 CharacterBody2D,今天静止可用 Node2D)+ Sprite2D(外观)+ Area2D(靠近检测)+ CollisionShape2D(挂在 Area 下,表示范围)。对话 UI 可以做在 NPC 里,但更推荐做成村庄/UI 层的通用对话框,被 NPC 调用。

为什么 Area2D?因为它擅长「有人进出我的范围就通知我」,不像 CharacterBody 那样负责走路。店小二今天可以站着不动;他是感应门 + 一张嘴。

命名建议:场景 npc_waiter.tscn,根名 NpcWaiter,子节点 Sprite2DInteractArea(Area2D)。名字写给人看,也写给 AI 看。

Node2D 根 Sprite 外观 Area2D 范围
知识点

交互范围的碰撞形状通常比精灵更大一点,让玩家不用脸贴脸才能说话。范围太小会「按了没反应」,被误判为脚本坏了。

AI Prompt
请为 Godot 4 设计一个静止古风店小二的最小节点树(含命名),说明每个节点职责。要求用 Area2D 做交互范围,并告诉我哪些碰撞层/掩码概念我今天需要知道(尽量少)。

外观素材:可复用 AI 出图流程(Day1),生成「古风店小二」PNG,放入 res://assets/characters/npc_waiter.png。没有完美图也可用色块占位,先通交互。

独立成场景再拖进村庄,和玩家同一套管理哲学:改蓝图,处处生效;炸了只影响 NPC 文件。

若你担心碰撞层概念,可以先让 AI 给「最懒但能用」的层配置,并要求它用中文注释每个勾选项。你不必一次成为物理层专家,但要留下可查的注释。

静止 NPC 用 Node2D 足够。若你以后想让他巡逻,再换成 CharacterBody2D 也不迟。今天把「会走」从需求里删掉,是为了保护「会说」。需求减法,是专业。

Area2D 的监控对象要勾对:监控身体(bodies)或面积。玩家是 CharacterBody2D 时,确保 Area 能监控到 body。这一勾错,信号永远沉默。

本节小练

在纸上画出 NPC 节点树三层。画得出来,才配让 AI 开工——结构先于台词

Lesson 3 · 创建 NPC 场景并摆进村

动手:新建 res://scenes/npcs/npc_waiter.tscn。根 Node2D → Sprite2D 挂图 → Area2D(命名 InteractArea)→ 其子 CollisionShape2D,Shape 用 CircleShape2D 或矩形,半径大约角色身高的 1.5–2 倍。保存后,把实例拖到村庄路边或村口内侧。

摆位原则:玩家路过自然遇见,不要藏死角;也不要堵死唯一村口。NPC 是路标,不是路障。Day2 的路是叙事动线,店小二就站在动线旁边。

运行游戏,确认能看见 NPC。还不能对话没关系——先「群众演员入画」。很多人一上来写信号连接,结果场景里根本没这个人。

NPC 放置
先入画,再接线,最后给词
知识点

Area2D 要能检测到玩家,双方的碰撞层/掩码需要匹配。若完全不懂,把场景树截图给 AI,说「让 InteractArea 能检测到 Player 的 CharacterBody2D」。今天以求通为先,层编号细节可后补文档。

AI Prompt
请用 Godot 4 逐步教我创建 npc_waiter.tscn(Node2D+Sprite2D+Area2D+CollisionShape2D),放到 village.tscn 路边,并配置让 Area 能检测到 CharacterBody2D 玩家。每步写成功现象;层设置请给最简方案。

备份项目。接下来要接信号与 UI,容易改乱。有备份,才敢让 AI「大胆一点」。

店小二素材没有时,用纯色矩形 Sprite 也成立。占位角色能帮你先验证系统。很多失败项目死于「等美术」。我们用占位换进度。

摆位后建议在编辑器里用玩家大概尺寸目测:走过去会不会自然停在范围边缘。太远要喊话、太近像脸贴脸,都尴尬。交互距离也是设计,不只是技术参数。

把 NPC 实例命名清楚,例如 NpcWaiter。村庄里以后会有多个实例,默认名 Node2D2 会让你和 AI 一起迷路。

本节小练

F5 看见店小二站在村里。看不见就先别写对话——群众演员没到场,台词没用

Lesson 4 · 检测玩家靠近

Area2D 提供信号:例如 body_entered / body_exited(检测到 CharacterBody 等物理身体时)。思路:玩家进入范围 → 记 player_in_range = true;离开 → 设回 false。只有为 true 时,按交互键才开对话。

在 NPC 脚本里连接信号(编辑器节点面板连,或代码 connect)。进入时还可以 print("有客官靠近") 做验收。输出出现这句,说明感应门工作了——这比直接做 UI 更能定位问题。

body_entered 可交互=true body_exited → false
知识点

先做「靠近提示」再做对话框:例如进入范围时显示「按 E 交谈」Label。提示是廉价的用户体验,也能证明范围逻辑正确。

AI Prompt
请给 Godot 4 店小二写靠近检测脚本:Area2D 的 body_entered/exited,设置 player_in_range,print 调试。说明如何把玩家加入 group「player」,以及如何在编辑器连接信号。不要做对话框。

建议给玩家根节点加入组 player,检测更稳,不怕改名。组是剧组名册:谁是主演,一查便知。

检测用 body 还是 area,取决于玩家碰撞类型。CharacterBody2D 通常走 body_entered。若你连了 area_entered 却永远不触发,先怀疑信号种类选错——这比改三小时逻辑更常见。

调试时把 Area 的 CollisionShape 显示出来(调试可见碰撞)。看见圈,才知道自己到底站进没站进。看不见的范围等于不存在的范围。

本节小练

走进走出范围,输出面板出现靠近/离开相关打印。通了再往下——感应门先亮,门铃后装

Lesson 5 · 交互键:何时允许开口

_process_unhandled_input 里检测按键:例如 E 或空格。仅当 player_in_range 为 true 时响应。推荐在 Input Map 新增动作 interact,绑定 E 与手柄键;脚本写 Input.is_action_just_pressed("interact")

just_pressed 而不是 pressed,避免按住时一帧开一百次对话。这是交互系统的经典坑,写进 Prompt 要求 AI 必须 just_pressed。

在范围内 just_pressed interact 打开对话
知识点

对话打开时,可暂时禁止玩家移动(可选)。最小做法:对话框显示时忽略移动输入,或设 GameState.talking = true。今天若来不及,允许边走边说,但要知道这不优雅,Day6 可收拾。

AI Prompt
请教我在 Godot 4 Input Map 添加 interact(绑定 E),并在 NPC 脚本中:仅 player_in_range 且 is_action_just_pressed("interact") 时 print「开始交谈」。给出编辑器步骤与脚本片段。

先打印「开始交谈」,确认按键逻辑。很多人对话框写了半天,其实按键根本没进分支。分段验收再次救命。

提示文案「按 E 交谈」可在进入范围时显示、离开时隐藏。哪怕只是一个 Label,玩家也不会在 NPC 脸上盲按。

键位冲突:UI 的「点击」与游戏的「交谈」可能抢输入。若发现按 E 也被别的系统吃掉,改用 _unhandled_input 或调整处理顺序。把它当排错清单一项即可。

把 interact 写进口令本:键位、动作名、脚本判断行。明天战斗若也占用按键,你会需要这张对照表来做仲裁。

为什么单独开一节讲按键?因为「检测到人」和「愿意开口」是两件事。有人靠近不等于自动弹剧情——除非你做自动触发。本课程默认主动按键,玩家更有控制感,也更少误触。

自动触发适合机关与过场;交谈适合社交。类型选错,体验会怪。今天我们做社交。

本节小练

靠近并按 E,输出出现「开始交谈」;远离再按不应触发。键位逻辑正确,本节过。

Lesson 6 · 对话框 UI

让字出现在屏幕上。常用结构:CanvasLayer → Panel(或 NinePatch)→ 角色名 Label → 正文 Label → 可选提示「按 E 继续」。CanvasLayer 保证 UI 叠在游戏画面之上,不跟着地图乱跑。

对话框 UI 示意
底部对话框:名字 + 正文 + 继续提示

UI 可以做成独立场景 dialogue_box.tscn,提供方法 show_line(speaker, text)hide_box()。NPC 只负责调用,不负责画面板。这是分工:化妆组与编剧分开,现场才不乱。

知识点

默认隐藏对话框(visible = false)。需要时再显示。打开时填充文本;关闭时清空或保留末句均可。别忘了在编辑器里把锚点放到底部,不同窗口尺寸更稳。

AI Prompt
请用 Godot 4 逐步创建一个底部对话框场景 dialogue_box.tscn(CanvasLayer+Panel+两个 Label),提供 show_line(speaker, text) 与 hide_box()。古风点到为止,不要复杂主题系统。说明如何把它实例进 village.tscn。

字体太小或颜色对比不足,会让人以为「没显示」。先用大字高对比,古风字体以后再说。可读,是 UI 的第一美德。

对话框请放在较高的 CanvasLayer layer,避免被 TileMap 或角色挡住。被挡住的字,玩家会发誓系统坏了。

本节小练

用临时按钮或 _ready 里调用 show_line 测试,确认字能显示。UI 通了,再接 NPC——舞台灯光先亮

Lesson 7 · 对话内容怎么存

最小存储:NPC 脚本里一个字符串数组,例如两条台词。按 E:若对话框关着就显示第 0 句;若已打开则索引 +1;超过末尾则关闭。这叫「翻页式对话」,足够 Day4。

进阶可把对白放到 .json / .tres,但今天禁止为了架构完美而延迟开口。先数组,后数据驱动。导演要的是今晚能演,不是明年能扩展到 MMO。

翻页对话示意
var lines := [
    "客官,酒旗刚挂上。",
    "听说竹林近来不太平……",
]
var index := 0
var talking := false

func try_talk() -> void:
    if not talking:
        talking = true
        index = 0
        dialogue.show_line("店小二", lines[index])
    else:
        index += 1
        if index >= lines.size():
            talking = false
            dialogue.hide_box()
        else:
            dialogue.show_line("店小二", lines[index])
知识点

对白文本请让 AI 写「能读出口的短句」,避免说明书腔。古风点到为止:一个语气词胜过堆砌之乎者也。

AI Prompt
请为古装村庄店小二写 3 句短对白(每句不超过 28 字),口语古风、能暗示「竹林有事」但不要展开任务。然后把它嵌进 Godot 4 的字符串数组,并说明翻页/关闭逻辑。

把对话框节点引用拿稳:可用组、唯一名称、或导出节点路径。找不到 dialogue 是第二常见崩点。打印 dialogue 是否 null,再叫冤。

对白请避免一次塞一段作文。游戏阅读环境是「边玩边看」,短句更强。让 AI 改写时加约束:「可朗读、可截图发群」。

数组索引越界是翻页常见崩溃。关闭条件写清楚:index >= size 就 hide。让 AI 加保护,别假设玩家按键次数守规矩。

对白数组可以先写占位:「第一句」「第二句」,链路通后再换成好词。很多人对着空系统硬憋文案,又累又慢。先通水管,再决定流酒还是流茶。

若你想要说话人名字也变化,把数据结构改成字典列表:speaker + text。需要时再升级,不需要时别提前抽象。

本节小练

不接 NPC 完整链路也行:写好 3 句数组。词库就位——没词,机关只是空壳

Lesson 8 · 把链路接死:靠近按键出字

现在把 Lesson 4–7 焊成一条管线:进入范围 → 显示「按 E 交谈」→ 按 E 调用 try_talk → 对话框显示/翻页/关闭 → 离开范围时若还在谈话可选择强制关闭。让 AI 按你当前场景树给出完整脚本,你负责粘贴与点连接。

验收时故意做坏事:远离再按 E(不应开);对话中途走开(定义你的规则并坚持);连按是否跳句正常。规则一致比规则完美重要。

靠近 按 E 翻页 关闭
知识点

信号没连、节点路径错、玩家不在检测层、UI 被别的 CanvasLayer 挡住——是四大元凶。排错时每次只验证一个假设,带着 print 与可见性检查前进。

AI Prompt
我是 Godot 4 零基础。请根据以下结构给完整可粘贴方案:npc_waiter(Area2D 靠近检测)+ dialogue_box(show_line/hide_box)+ interact 动作。要包含:提示Label、翻页对白、离开范围处理。先问我节点真实路径再写最终代码,避免猜错。

接通后保存两份:场景与脚本。录 5 秒:走近、说话、翻页、离开。这是 Day4 的技术样片。

若时间不够做提示 Label,至少保证对话框能出字。提示是体验,出字是生死线。

接线完成后,不要连续大改三处。改 UI 就只改 UI,测一次;改检测就只改检测,测一次。混改是制造幽灵 Bug 的快捷方式。

联调成功后,故意改错一个节点名再改回来,观察报错长什么样。认识报错,是为了以后不怕报错。

联调时打开远程场景或确保对话框实例就在当前运行场景树里。脚本拿着路径找不到节点,是「我写了但没挂上」的典型。print 节点路径,比盯着代码发呆有效。

把成功录像或截图留存。第二天你改战斗输入时若弄坏对话,这些样片能告诉你「昨天好的标准长什么样」。

链路通后,把「interact + player_in_range + show_line」写成你自己的三行口诀。口诀能背,排错就快。

本节小练

完整走通一次对话开合。通了才允许美化——先能演,再妆造

Lesson 9 · 村庄排练:一个真正的店小二

把完成定义当成场记表,逐项勾选:

村庄中看得到店小二
靠近出现可交互反馈(提示或光圈亦可)
按 E 弹出对白,至少 2 句可翻页
结束后对话框关闭,可再次交谈
远离无法误触发

加一点导演润色(可选,不超过 15 分钟):对白提到竹林;NPC 站在石板路旁;提示文字用「按 E 交谈」。润色超时立刻停——完美是明日的敌人。

对话验收
验收:能看见、能按出、能翻完、能关掉
AI Prompt · 验收教练
请当验收教练。我的 Godot 4 NPC 对话现状如下:【是否能靠近检测/是否有对话框/句数/已知 Bug】。判断是否达到「最小可玩对话」,并最多给 3 条修补建议。不要建议我做任务系统。
知识点

第二个 NPC 只是复制场景改对白与贴图。今天不必做。证明模板可复制,比堆角色重要。模板 = 生产能力。

备份项目。对话系统常在「再改一点」时崩。备份是彩排后的定妆照。

如果朋友试玩只会「乱按」,说明提示不够。加一行「按 E 交谈」往往比加一段教程动画更有效。提示是设计,不是示弱。

杀青标准是「可重复演示」。重启游戏再谈一次话,仍要成功。只成功一次的 Demo,叫巧合,不叫系统。

排练不止一次。重启游戏,换个方向走近,连按,慢按,走开放开。系统要在粗鲁操作下仍不崩溃。玩家不会按你的设计意图来按键,他们按自己的好奇心来。

勾完场记表后,才允许加第二个装饰性 NPC。第二个没有新系统,只是内容增量。系统未稳就增量,等于放大事故面。

演示给别人看时,先自己走位到 NPC 旁再交键盘。演示也是导演工作,不是纯技术工作。

场记表勾完后,把对话截图和三句台词一起塞进口令本。明天做战斗时,你可能暂时关掉对话输入,需要随时能找回「对话正常时的样子」。

若翻页偶发跳两句,检查是否同时在 _process 与按钮信号里处理了同一按键。只保留一条处理路径。

可重复演示三次,才算真正杀青。一次成功可能是运气。

本节小练

场记表全勾,截图对话框与场景树。交得出样片,才算杀青。

Lesson 10 · 总结

第四天,世界开始说话。把链路收进词典:

NPC 场景 Area 靠近 interact 按键 对话框翻页

你应该已经具备:

独立 NPC 场景与交互范围
通用对话框 UI(可被调用)
至少一段可翻页对白闭环
知道如何复制模板做下一个 NPC
课程理念

叙事系统最容易贪心:分支、任务、声望……AI 一鼓励,项目就胀死。导演要会说:「今天只让一个人说完三句话。」有限的说话,胜过无限的架构幻想。

明天 Day5 做战斗:世界将有冲突。对话给了动机,战斗给了代价。若今天对话不稳,明天别急着上刀,先让店小二能可靠地开口。

AI Prompt · 日复盘
我完成了 Godot 4 Day4 NPC 对话最小闭环。请写一段给 Day5(战斗)用的项目现状模板,并提醒我:战斗开始前,对话 UI 与输入要如何避免冲突(例如同一按键)。

更新口令本:NPC 路径、对话框路径、interact 键、三句对白。档案在,剧组就还在。

四天回顾:可进引擎、有地、能走、能说话。这已经是可演示的竖切片雏形。后面战斗与包装,是让竖切片更刺激、更好看,而不是推倒重来。

若只完成 80%,优先保证出字与关闭。提示、动画、打字机效果都是皮肤。皮肤可缺,嘴不能哑。

请用四句话复盘:谁在说话、怎样才算靠近、哪个键开始、怎样结束。说得顺,说明模型在你脑子里建好了。说得卡,回去对着场景树再指一遍。

内容生产者的你,已经可以批量生成「村口闲聊」。但批量前请锁定模板。模板不锁,批量只是批量制造债务。

从今天起,你的 AI 口令可以更「编剧化」:指定说话人身份、信息目标、禁止剧透。技术口令与内容口令分流,团队才像团队。

完成定义再读一遍:靠近、按键、出字、翻页、关闭。全满足就点标记完成;缺一项就补一项,不要用「差不多」骗自己。

口令本更新完毕后,才点「标记为已完成」。档案先于荣誉。

明天见:刀光之前,先确保今天这张嘴还在。

本节小练

向自己口述链路四步。说顺了,再点「标记为已完成」——说得清,才算真会

课堂练习

npc_waiter 场景存在且在村庄可见
靠近可交互,按 E 出对白
至少 2 句可翻页,结束后可关闭并再谈

今日作业

1)保存三句对白与 interact 键位到口令本。
2)截图:提示/对话框/场景树。
3)写一句:「我的对话关闭条件是 ___。」

本日总结

NPC = 感应门 + 嘴巴 + 对话框。胜利条件:村里有人能把话说完。