今日速览

  • AI 代码不能直接进项目的真正原因:缺少「项目约定」上下文——命名、类型注解、节点引用方式、文件位置,没人告诉它,它只能猜。
  • Godot 官方场景组织的第一原则:子场景应当「零依赖」——自己需要什么就在自己内部备齐,需要外部交互时用信号(signal)+ 依赖注入,而不是 get_node("../../Player") 这种硬路径。
  • 信号命名用过去式:entered、item_collected、skill_activated——它是「已经发生的事」的通知,不是「去干活」的命令;要启动行为就用方法或 Callable 属性。
  • 风格指南不是审美问题:制表符缩进、class_name 声明、类型注解、数组末尾留逗号,这些细节决定了 diff 干不干净、重构会不会炸。
  • 可复用的解法:把上面这些规则写成一段固定前缀提示词(我叫它「项目宪法」),每次开新对话先贴一遍,后面只写需求。

核心学习内容

一、为什么 AI 生成的代码总是「差不多能跑」

让 DeepSeek 写一个 Player.gd,它通常会给你一份语法正确、逻辑通顺、但装不进你项目的代码:变量没类型注解、节点用魔法字符串路径、把父节点的职责写进子节点、缩进用空格(Godot 编辑器默认 Tab)。

问题不在模型,在信息缺口。人类新人进项目也要先读三样东西:目录结构、代码风格、架构约定。DeepSeek 每次对话都是「第一天上班的新人」,你不给,它就按互联网上最流行的写法猜——而最流行的写法往往是最老的教学写法。

二、把官方原则翻译成机器能执行的约束

Godot 文档里两条最值得写进提示词的规则:

  1. 场景零依赖(Scene organization):子场景不应知道自己被放在哪。需要外部数据就 @export 暴露出来由父级赋值;需要通知外部就 emit 信号。反例是子节点里写 get_node("../../UI/HUD")——一旦这个场景被复用到第二个地方,立刻报 Node not found。
  2. 风格的机械部分(GDScript style guide):Tab 缩进、UTF-8 无 BOM、LF 换行、class_name + extends 顺序、能在声明处推断的类型就写 :=、公开 API 写类型注解、数组/字典/枚举末行留逗号。

这两条的共同点是:都可以被判定对错。能被判定对错的东西,就适合写进提示词当验收标准;「写得优雅一点」这种话则毫无作用。

三、三段式工作流:宪法 → 生成 → 审查

我现在的固定流程是:

  • 宪法:一段 30 行左右的固定文本,描述引擎版本、语言、命名、架构红线。开场贴一次,全程有效。
  • 生成:需求 + 节点树快照 + 已有信号清单 + 输出格式(单文件、完整可粘贴、末尾附自检表)。
  • 审查:把生成结果原样贴回,让它按检查表逐条打勾并输出「必改 / 建议 / 可忽略」三级问题。

这套流程的收益不是「代码更好看」,而是返工次数下降。以前一轮对话要来回改 4 次才敢粘进编辑器,现在通常 1 次就能跑,第 2 次只是微调数值。

🎯 DeepSeek 提示词

提示词 1:项目宪法(每次新对话先贴这段)

适用场景:任何一次让 DeepSeek 写 Godot 代码之前。存到备忘录里,改项目时只改前 5 行。

你是我的 Godot 4 结对程序员。以下是本项目的硬性约定,之后所有回答都必须遵守,不需要我重复提醒。

【项目信息】
- 引擎:Godot 4.x(GDScript,不用 C#)
- 游戏类型:2D 俯视角动作(可替换)
- 目录:res://scenes/ 场景,res://scripts/ 脚本,res://assets/ 素材

【代码规范】
1. 文件首行写 class_name(可选)与 extends,顺序不可颠倒。
2. 缩进用 Tab,不用空格;换行 LF;UTF-8 无 BOM。
3. 能从赋值推断类型就用 :=,公开函数与导出变量写完整类型注解。
4. 私有成员与函数加下划线前缀(_state, _update_aim)。
5. 数组、字典、枚举最后一项后保留逗号。

【架构红线】
6. 子场景零依赖:不写 get_node("../../Xxx") 这类跨层硬路径。需要外部数据用 @export 暴露,由父级赋值;需要通知外部用 signal。
7. 信号名用过去式(entered / died / item_collected),信号只表示"已发生",不用来下达指令。
8. 一个脚本一个职责;超过 200 行时主动提示我拆分。
9. 不引入任何第三方插件,只用 Godot 内置节点与 API。

【回答格式】
- 先给完整可粘贴的代码块(不要省略号、不要"其余不变")。
- 再给 3-6 行说明:每个关键设计选择的理由。
- 最后给一份自检表,逐条回答:是否用了跨层硬路径 / 是否所有信号都连上了 / 是否有未处理的空值。

提示词 2:带上下文的功能生成

适用场景:新写一个角色/系统脚本。关键是「节点树 + 已有信号」这两块上下文,缺了它 AI 只能瞎猜。

基于上面的项目约定,帮我实现:[玩家冲刺 dash 功能]

【当前节点树】
Player (CharacterBody2D, 挂 player.gd)
├── Sprite2D
├── CollisionShape2D
├── AnimationPlayer(动画名:idle / run / dash)
├── DashTimer (Timer, one_shot=true, wait_time=0.15)
└── CooldownTimer (Timer, one_shot=true, wait_time=0.8)

【已有信号】
- health_changed(current, max)
- died()

【行为需求】
1. 按 dash 动作(InputMap 已配置)触发,持续 0.15 秒,速度 600,方向为当前朝向。
2. 冲刺期间无敌(用一个 is_invincible 标志,供 Hurtbox 判断)。
3. 冷却 0.8 秒,冷却中按了没反应但也不报错。
4. 冲刺开始 / 结束各 emit 一个信号 dash_started / dash_finished。
5. 冲刺期间不能再次冲刺,也不能被击退。

【输出要求】
- 单文件 player_dash.gd,作为 Player 的子节点脚本(Node),通过 @export 拿到 CharacterBody2D 引用,不要写死路径。
- 我只粘贴文件 + 在编辑器里挂节点,不需要改其他脚本;如需改动请单独列出"需要我手动做的事"。

提示词 3:代码审查与重构

适用场景:AI 给的代码能跑但你不放心,或接手了自己三个月前写的代码。

按项目约定审查下面的 GDScript。不要重写整个文件,请按三级输出问题:

【必改】会导致 bug 或违反架构红线的(跨层硬路径、空值未判、信号未连接、类型错误)
【建议】影响可读性/可维护性的(命名、过长函数、魔法数字)
【可忽略】纯风格偏好

每条格式:`行号 | 问题 | 为什么是问题 | 修改后的代码片段`

最后额外回答 3 个问题:
1. 这个脚本如果被实例化两次,会出什么问题?
2. 哪些状态是隐式的(靠 bool 组合表达),能否收敛成枚举?
3. 如果我要给它加存档支持,最小改动是什么?

代码:(在此处粘贴你的脚本,粘贴后紧跟在上面这段提示词后面发送)

学习心得

做了 77 天,最省时间的改变不是学会了某个 API,而是把规矩写下来。项目宪法这 30 行,等于把「我踩过的坑」变成了 AI 的前置知识——它不需要再犯错,我也不需要再纠正。对一个人的独立开发来说,这就是最划算的杠杆:你只写一次规则,AI 每天替你遵守。

下一步我打算把这份宪法直接落成仓库里的 AGENTS.md / CLAUDE.md,让它跟着项目走,而不是跟着我的记忆走。

明日关注

  • 把「项目宪法」写进仓库 AGENTS.md,验证新对话零提醒也能守规矩
  • 试一次「让它先写自检表再写代码」(反序生成),看 bug 率是否更低
  • 整理一份「Godot 报错 → 提示词」对照表,覆盖最常见的 10 条运行时报错

本文由自动化学习流水线每日生成,坚持即学即记。