Files
Game/SPEC.md
T

105 lines
5.4 KiB
Markdown
Raw Normal View History

# 开发规范
注意:
1. **本项目是godot46创建的,虽说附近版本号可以向下兼容,但是最好不要改编辑器版本。**
2. **你可以写一个AGENTS.md指向该文件,但是注意最好不要用MCP,AI会把场景改得乱七八糟。**
3. **建议要用agent就装一个godot skills,要么就古法编程(No-vibe-coding万岁!!!)。**
## 目录该放什么
- components/ 游戏中的所有场景,除了world主场景,不要有其他场景散在工作区里
- Abstracts/ 抽象场景,虽然说godot的场景不能抽象但是看做抽象!
- Bullets/ 子弹,其下所有场景都继承于BaseBullet
- Characters/ 角色,其下所有场景都继承于BaseCharacter
- Interactables/ Menus/ 同理
- resources/ 资源文件,注意.tres .res不属于资源,这里更应该说是assets,就是Unity生态里的资产概念,就是媒体(图片、视频、音频)、字体、骨骼
- scripts/ 所有脚本,不要把脚本散在工作区
- Content/ 游戏内容的高层实现,比如交互体、角色等的AI
- Statemachine/ 状态机,不一定是某个模式FSM或者UtilityAI才算
- Abstract/ 抽象对象,但不一定是抽象类,比如说所有“角色”都有的复用代码
- BaseXXX.gd 比如BaseCharacter BaseMenu
- Component/ UI组件的状态机
- Data/ 继承RefCounted的类,就是纯数据不和引擎交互
- Struct/ 数据结构,但和Statemachine/Data不同,指的是可以作为游戏内容但是本身不参与节点树,也没有状态机说法的数据
- Util/ 这个就是工具类了
- [T].gd 类名就是class_name [T]Util,仅静态类,提供一些最底层的工具,比如随机从数组里选择
- Manager/ 管理器,一般要和节点树交互,也可以不交互
- [T]Manager.gd 类名就是class_name [T]Manager,只能继承Node(或者不继承),不能更深,否则请作为Statemachine
## 通用命名规范
1. 对于一切符号,用小驼峰命名法,就是写js用的那种,常量除外,采用全大写
2. 对于游戏内容[T extends Character|Bullet|Menu|...],其行为脚本的类名写成“内容名+[T]”,比如角色A的类名就是ACharacter
3. 对于资源文件,用连字符命名
4. 对于脚本文件,大多数情况下可以直接写类名,但是游戏内容`[N][T]`也可以写成N.gd
5. 对于着色器文件,用小驼峰写清楚实现的特效是什么
## 代码排序规范
### 对于行为类
1. extends
2. class_name
空行
3. @export
空行
4. @onready
5. 普通var
空行
6. 节点的虚方法_ready、_physics_process等
空行
7. 抽象方法(或者对基类抽象方法的实现)
空行
8. 工具方法(但不是工具类里的底层方法!!!是这个对象的高层实现,比如BaseCharacter里的getHealthPercent方法,而不要在这个类里写getRandomInt
### 对于管理器
如果这个管理器是一个Node而且是单例,推荐写一个static var instance: XXManager,然后在ready时赋值,注意单例类不要写其他静态变量
1. extends Node
2. class_name
空行
3. static var instance
空行
4. 其他变量
空行
5. 方法
## 代码编写规范
1. 必须用静态类型声明,禁止动态类型或者是用`:=`的语法糖
2. 少用`as`,如果你是已经确定一个对象的类型,但是编译器推不出来直接用if is,除非是编译器推断错了再用as
3. 对于节点名字或者Signal的名字,可以用`StirngName`特化性能,比直接用`Stirng`好一点
4. 缩进用Tab,不要用空格
### 模式风格
1. 对于一个函数有多个返回值通过数组或者字典返回,可以用Pattern Match(match data:[var a,var b])来解构,就可以少一点变量了
2. 保持函数的纯度,尽量多写纯函数,少点副作用
3. 解耦,这个很好理解,就是把数据逻辑和UI逻辑分离开之类的
## 关于节点
### 引用
**注意**:我这里指的是狭义的`Node.get_node`方法或者`$`的速记写法。
- 节点的命名规范:**场景根节点用大驼峰,下面的树用小驼峰,虽然可以用中文和连字符以及一些乱七八糟的其他字符,但是最好还是不要出现,就用英文字母+数字**。
- 在脚本里获取节点
- 场景里原本就有的,仅唯一的节点,请给一个unique id,不要在@onready时用绝对路径获取,就是写成@onready var colorRect: ColorRect = $%colorRect
- 对于要出现很多次的节点,比如角色,那就不用unique id了,直接get_node
## Best Practice
1. 所有直接继承Object或者没写继承的类不许去new,要么就继承RefCounted,不然会内存泄漏
2. 游戏对象的分类有很多,比如说角色A就写一个场景A.tscn,脚本就是A.gdclass ACharacter extends BaseCharacter(这个只是伪代码,建议写成一个单独的文件)
3. 比如玩家的技能(Skill),应该放在Struct里写成Skill.gd,类名BaseSkill,因为这个类只负责表示玩家的技能卡对应的行为,并不耦合于节点树和角色的行为类
## 冷知识
1. godot没有try-catch或者try-except语句,尽量少用assert除非你在调试
2. godot的异步函数不需要声明,但是他的Signal和Coroutine都可以用await等待,把Signal也看做是Coroutine就行了
3. 不要在@export变量里写Gradient等资源,而是用GradientTexture,否则编辑器会直接爆炸
4. godot的_ready顺序是先子节点再父节点,注意时序问题