Files
zeling_v2/Docs/Standards/AssetFolderSpec.md
Joywayer c88d2d0549 feat: Addressables rules/sync tools, UI fixes, AddressKeys update
- Add AddressableRules.cs: single source of truth for prefix->group and prefix->label rules
- Add AddressableRuleSyncWindow.cs: scan/fix/export-CSV tool (BaseGames > Addressables > Rule Sync)
- AddressableBatchTool.cs: delegate DeriveGroupName to AddressableRules, remove duplicate PrefixGroupMap
- AddressKeys.cs: add Labels constants (Preload, Poolable, Enemy, BGM, SFX, Charms, Config, Weapon)
- Docs/Standards/AddressablesLabelSpec.md: new label naming & assignment spec
- Docs/Standards/AssetFolderSpec.md: update Addressables group strategy section
- SplashScreenController.cs: fix MainMenu loading flow
- BootFlowSetupWizard.cs / SceneScaffoldTools.cs: scene scaffold fixes
- PlayerInputActions: set UI/Point to Pass-Through type
- Persistent.unity: add BootSequencer to auto-load MainMenu on play
- EditorBuildSettings.asset: register scenes for build

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-20 11:10:31 +08:00

708 lines
37 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 资源文件夹目录规划与管理规范
> **版本**1.1
> **创建日期**2026-05
> **适用范围**`Assets/` 目录下所有非代码资源(美术、数据、预制体、场景等)
> **资源管理系统**Unity Addressables禁止使用 `Resources.Load`
> **关联文档**`Architecture/01_ProjectStructure.md`、`Architecture/13_AssetPoolModule.md`
---
## 目录
1. [为什么使用 `_Game/` 父目录](#0-为什么使用-_game-父目录)
2. [整体目录结构总览](#1-整体目录结构总览)
3. [Art/ 美术资源规范](#2-art-美术资源规范)
4. [Data/ ScriptableObject 规范](#3-data-scriptableobject-规范)
5. [Prefabs/ 预制体规范](#4-prefabs-预制体规范)
6. [Scenes/ 场景规范](#5-scenes-场景规范)
7. [Shaders/ 着色器规范](#6-shaders-着色器规范)
8. [UI Toolkit/ 规范](#7-ui-toolkit-规范)
9. [Addressables 管理规范](#8-addressables-管理规范)
10. [Import Settings 规范](#9-import-settings-规范)
11. [资源新增工作流](#10-资源新增工作流)
12. [禁止行为清单](#11-禁止行为清单)
---
## 0. 为什么使用 `_Game/` 父目录
### 核心矛盾
Unity 项目的 `Assets/` 根目录通常同时存放**第一方游戏内容**和**第三方插件目录**。两者混杂会导致:
- Project 窗口难以快速定位自有资产(需要在 `Art/``Feel/``Opsive/` 之间来回跳转)
- 插件升级时不清楚哪些目录是项目本身的
- 构建分析工具(如 Addressables Analyze、Build Layout的输出路径噪音多
### 解决方案:`_Game/` 父目录
将**所有第一方资产**归入 `Assets/_Game/`,下划线前缀使其在字母排序中始终置顶。
```
Assets/
├── _Game/ ← 所有第一方资产Art、Data、Prefabs、Scenes、Scripts 等)
├── AddressableAssetsData/ ← Unity 托管,必须保持在 Assets/ 根
├── Feel/ ← 第三方(只读)
├── Opsive/ ← 第三方(只读)
├── PathBerserker2d/ ← 第三方(只读)
├── Plugins/ ← 第三方(只读)
└── Tests/ ← 测试(可独立于 _Game/
```
### 迁移注意事项
| 项目 | 说明 |
|------|------|
| `AddressableAssetsData/` | **必须留在 `Assets/` 根目录**Unity Addressables 硬编码此路径 |
| `Settings/` | URP asset、Input Actions 等 Unity 项目设置文件可随 `_Game/` 迁移 |
| `Resources/` | 如存在,应逐步清空并删除;过渡期可临时留在根目录 |
| `StreamingAssets/` | 必须留在 `Assets/` 根目录Unity 特殊文件夹) |
| asmdef 路径 | 迁移 `Scripts/` 后需在 Unity 中重新扫描asmdef 引用由 GUID 追踪,路径变更不影响依赖 |
---
## 1. 整体目录结构总览
```
Assets/
├── _Game/ ← 所有第一方游戏资产
│ ├── Art/ 美术源文件Sprites、Animations、Materials、Atlases
│ │ ├── Characters/ 角色美术(玩家 / 敌人 / NPC
│ │ │ ├── Player/ 按形态 ID 分子目录Player01、Player02…
│ │ │ ├── Enemies/ 按敌人 ID 分子目录E001、E002…
│ │ │ └── NPCs/ 非战斗 NPC按角色名分子目录
│ │ ├── Environment/ 场景环境Tilesets、Backgrounds、Props
│ │ ├── Effects/ 特效美术Sprites、Materials、Atlases
│ │ ├── UI/ UI 专用图片Icons、Frames、Backgrounds、Atlases、Materials
│ │ └── Shared/ 跨模块复用基础资产Palettes、Textures、Materials
│ │
│ ├── Data/ ScriptableObject 资产(按模块分类)
│ │ ├── Events/ 事件频道 SO按模块分子目录
│ │ ├── Player/
│ │ ├── Combat/
│ │ ├── Enemies/
│ │ ├── Progression/
│ │ ├── Audio/
│ │ ├── World/
│ │ ├── UI/
│ │ └── Settings/
│ │
│ ├── Prefabs/ 预制体
│ │ ├── Player/
│ │ ├── Enemies/
│ │ ├── Combat/
│ │ ├── Effects/
│ │ ├── Environment/ 场景骨架结构Tilemap 层、视差背景层)
│ │ ├── World/
│ │ ├── UI/
│ │ └── Persistent/ Persistent 场景专属 Prefabs
│ │
│ ├── Scenes/ 场景文件
│ │ └── Testings/ 测试专用场景(不进入 Addressables 构建)
│ │
│ ├── Scripts/ 游戏代码(见 Architecture/01_ProjectStructure.md
│ │
│ ├── Shaders/ 自定义着色器 & HLSL include
│ │ ├── BaseASE/ Shader Graph / ASE 材质图
│ │ └── Includes/ 共享 HLSL 函数库ColorUtils、NoiseUtils 等)
│ │
│ ├── UI Toolkit/ UITK 资产UXML、USS、主题
│ │ ├── Layouts/
│ │ ├── Styles/
│ │ └── UnityThemes/
│ │
│ └── Settings/ URP 配置、Input Actions 等项目设置资产
├── AddressableAssetsData/ ← Unity 托管,必须在 Assets/ 根(不可移动)
├── StreamingAssets/ ← Unity 特殊文件夹(如有 FMOD bank
├── Feel/ ← MoreMountains Feel 插件(只读)
├── Opsive/ ← Behavior Designer 插件(只读)
├── PathBerserker2d/ ← 寻路插件(只读)
├── Plugins/ ← 其他第三方插件(只读)
└── Tests/ ← EditMode / PlayMode 测试(可在 _Game/ 外独立维护)
```
> **强制约束**`AddressableAssetsData/` 和 `StreamingAssets/` 是 Unity 硬编码路径,**永远不得移入 `_Game/`**。所有第三方插件目录**不修改内部结构**,定制内容一律放入 `_Game/` 下的对应模块目录。
---
## 2. Art/ 美术资源规范
### 2.1 目录结构
```
Art/
├── Characters/ 角色美术资产(玩家 / 敌人 / NPC
│ ├── Player/ 玩家角色,按形态 ID 分子目录
│ │ └── {FormID}/ 例Player01、Player02
│ │ ├── Sprites/ 像素帧图 (.png) + Emission 自发光贴图PPU=32, Filter=Point · PLY_{FormID}_{Action}.png
│ │ ├── Animations/ 动作片段 (.anim) + 状态机控制器 (.controller)12fps · {Action}.anim / PLY_{FormID}_Animator.controller
│ │ ├── Materials/ 渲染材质 (.mat),引用角色 Shader控制 Emission、受击闪白参数· PLY_{FormID}.mat
│ │ └── Atlases/ Sprite 图集 (.spriteatlas),合并同形态所有帧,减少 DrawCall · Atlas_Player_{FormID}.spriteatlas
│ ├── Enemies/ 敌人,按 ID 分子目录ID 与 AddressKeys ENM_ 前缀对应)
│ │ └── {EnemyID}/ 例E001、E002
│ │ ├── Sprites/ 像素帧图 (.png) + Emission 贴图PPU=32 · {ID}_{Name}_{Action}.png
│ │ ├── Animations/ 动作片段 (.anim) + 状态机控制器 (.controller) · {Action}.anim / {ID}_{Name}_Animator.controller
│ │ ├── Materials/ 渲染材质 (.mat),引用角色 Shader · {ID}.mat
│ │ └── Atlases/ Sprite 图集 (.spriteatlas),含主体与 Emission 帧 · Atlas_Enemy_{ID}.spriteatlas
│ └── NPCs/ 非战斗 NPC按角色名分子目录
│ └── {NPCName}/
│ ├── Sprites/ 像素帧图 (.png) · {NPCName}_{Action}.png
│ ├── Animations/ 动作片段 (.anim) + 状态机控制器 (.controller) · {Action}.anim / {NPCName}_Animator.controller
│ ├── Materials/ 渲染材质 (.mat) · NPC_{NPCName}.mat
│ └── Atlases/ Sprite 图集 (.spriteatlas) · Atlas_NPC_{NPCName}.spriteatlas
├── Environment/ 场景环境资产
│ ├── Tilesets/ 瓦片纹理 (.png),配合 RuleTile 实现地形自动拼接
│ │ └── {Region}/ 按区域分子目录Forest、Dungeon、Cave· TILE_{Region}_{Name}.png
│ ├── Backgrounds/ 视差滚动背景图 (.png),按 Far / Mid / Near 分层渲染
│ │ └── {Region}/ 按区域分子目录 · BG_{Region}_{Layer}.png
│ └── Props/ 可复用场景道具纹理(箱子、灯柱、机关等,无逻辑纯视觉)
│ └── {Category}/ 按类别分子目录Furniture、Traps、Lights· PROP_{Category}_{Name}.png
├── Effects/ 特效美术资产Prefab 见 Prefabs/Effects/,此处仅存源图与材质)
│ ├── Sprites/ VFX 帧序列图 (.png)PPU=32用于粒子 / 帧动画特效 · VFX_{Description}.png
│ ├── Materials/ 特效专用材质 (.mat),引用 Effects Shader支持扭曲、混合· VFX_{Description}.mat
│ └── Atlases/ 高频特效 Sprite 图集 (.spriteatlas),减少特效渲染批次 · Atlas_Effects_{Name}.spriteatlas
├── UI/ UI 专用图片(布局 / 样式见 UI Toolkit/ 目录)
│ ├── Icons/ 图标按子类分目录,统一 32x32 或 64x64 规格
│ │ ├── Skills/ 技能图标,用于技能栏 / 技能选择界面 · IC_Skills_{Name}.png
│ │ ├── Items/ 道具 / 护身符图标,用于物品栏 · IC_Items_{Name}.png
│ │ └── Status/ 状态效果图标(中毒、燃烧等),用于角色状态栏 · IC_Status_{Name}.png
│ ├── Frames/ 面板框架、血条框、对话框边框等 (.png) · FRAME_{Description}.png
│ ├── Backgrounds/ 界面背景图、全屏半透明遮罩、渐变填充图 (.png) · UIBG_{Description}.png
│ └── Atlases/ UI 图标与框架图集 (.spriteatlas),减少 UI 渲染批次 · Atlas_UI_{Category}.spriteatlas
└── Shared/ 跨模块复用的基础资产,不属于任何具体角色或场景
├── Palettes/ 色板参考文件 (.png / .aco),仅供美术设计参考,不用于运行时
└── Textures/ 通用基础纹理:噪点图、渐变纹理、光晕贴图,供 Shader 采样使用
```
### 2.2 美术文件命名规则
| 资产类型 | 存放位置 | 命名格式 | 示例 |
|---------|---------|---------|------|
| 敌人 Sprite Sheet | `_Game/Art/Characters/Enemies/{EnemyID}/Sprites/` | `{ID}_{Name}_{Action}.png` | `E001_CaoZhi_Idle.png` |
| 敌人 Emission 贴图 | `_Game/Art/Characters/Enemies/{EnemyID}/Sprites/` | `{ID}_{Name}_{Action}_Emission.png` | `E001_CaoZhi_Idle_Emission.png` |
| 敌人 AnimationClip | `_Game/Art/Characters/Enemies/{EnemyID}/Animations/` | `{Action}.anim` | `Idle.anim``Skill_Start.anim` |
| 敌人 AnimatorController | `_Game/Art/Characters/Enemies/{EnemyID}/Animations/` | `{ID}_{Name}_Animator.controller` | `E001_CaoZhi_Animator.controller` |
| 敌人材质 | `_Game/Art/Characters/Enemies/{EnemyID}/Materials/` | `{ID}.mat` | `E001.mat` |
| 玩家 Sprite Sheet | `_Game/Art/Characters/Player/{FormID}/Sprites/` | `PLY_{FormID}_{Action}.png` | `PLY_Player01_Run.png` |
| 玩家 AnimationClip | `_Game/Art/Characters/Player/{FormID}/Animations/` | `{Action}.anim` | `Idle.anim``Attack01.anim` |
| 玩家材质 | `_Game/Art/Characters/Player/{FormID}/Materials/` | `PLY_{FormID}.mat` | `PLY_Player01.mat` |
| 特效 Sprite Sheet | `_Game/Art/Effects/Sprites/` | `VFX_{Description}.png` | `VFX_HitSpark_Sheet.png` |
| 特效材质 | `_Game/Art/Effects/Materials/` | `VFX_{Description}.mat` | `VFX_HitSpark.mat` |
| 瓦片纹理 | `_Game/Art/Environment/Tilesets/{Region}/` | `TILE_{Region}_{Name}.png` | `TILE_Forest_Ground.png` |
| 背景层 | `_Game/Art/Environment/Backgrounds/{Region}/` | `BG_{Region}_{Layer}.png` | `BG_Forest_Far.png` |
| 场景道具 | `_Game/Art/Environment/Props/{Category}/` | `PROP_{Category}_{Name}.png` | `PROP_Furniture_Chest.png` |
| UI 图标 | `_Game/Art/UI/Icons/{SubType}/` | `IC_{Category}_{Name}.png` | `IC_Skills_SoulBlade.png` |
| UI 框架 | `_Game/Art/UI/Frames/` | `FRAME_{Description}.png` | `FRAME_HealthBar.png` |
| UI 背景 | `_Game/Art/UI/Backgrounds/` | `UIBG_{Description}.png` | `UIBG_PauseMenu.png` |
| 色板参考 | `_Game/Art/Shared/Palettes/` | `PAL_{Name}.png` | `PAL_Forest.png` |
### 2.3 Sprite Atlas 策略
| Atlas 文件 | 覆盖内容 | 存放位置 |
|-----------|---------|--------|
| `Atlas_Player_{FormID}.spriteatlas` | 该形态玩家所有 Sprite | `_Game/Art/Characters/Player/{FormID}/Atlases/` |
| `Atlas_Enemy_{EnemyID}.spriteatlas` | 该敌人所有 Sprite含 Emission| `_Game/Art/Characters/Enemies/{EnemyID}/Atlases/` |
| `Atlas_NPC_{NPCName}.spriteatlas` | 该 NPC 所有 Sprite | `_Game/Art/Characters/NPCs/{NPCName}/Atlases/` |
| `Atlas_Effects_Common.spriteatlas` | 通用高频特效 Sprite | `_Game/Art/Effects/Atlases/` |
| `Atlas_UI_Icons.spriteatlas` | 所有 UI 图标(技能/道具/状态)| `_Game/Art/UI/Atlases/` |
| `Atlas_UI_Frames.spriteatlas` | 面板框架、血条框等 | `_Game/Art/UI/Atlases/` |
**规则**
- Atlas 文件放在**被打包 Sprite 的同目录下的 `Atlases/` 子文件夹**,而非集中到单独目录
- **Atlas 本身不注册 Addressable**,通过所属 Prefab/Material 间接引用,由 Unity 自动处理依赖
- 区域特有的敌人或环境资产可单独建 Atlas避免跨区域 Atlas 导致整体加载
- Atlas 内不混入不同生命周期的资产(例如:角色 Sprite 与 UI 图标不合并)
---
## 3. Data/ ScriptableObject 规范
### 3.1 完整目录结构
```
Data/
├── Events/ 所有事件频道 SO每个事件独立一个文件
│ ├── Core/ 游戏状态、场景加载等核心事件
│ ├── Player/ 玩家相关事件
│ ├── Combat/ 战斗相关事件
│ ├── Enemies/ 敌人相关事件
│ ├── World/ 世界交互事件
│ ├── UI/ UI 显隐事件
│ ├── Audio/ 音频播放事件
│ ├── Progression/ 进度成长事件
│ ├── Dialogue/ 对话事件
│ ├── Quest/ 任务事件
│ ├── Boss/ Boss 相关事件
│ └── Difficulty/ 难度调整事件
├── Player/
│ ├── Forms/ 各形态配置
│ └── Input/ 输入配置
├── Combat/
│ ├── DamageSources/ 伤害源配置
│ └── Weapons/ 武器配置
├── Enemies/
│ └── {EnemyID}/ 每个敌人的数据目录
├── Progression/
│ ├── Skills/ 技能配置
│ ├── Spells/ 法术配置
│ ├── Charms/ 护身符配置
│ └── Abilities/ 能力配置
├── Audio/
│ ├── BGM/ 背景音乐配置
│ └── SFX/ 音效配置
├── World/
│ ├── Map/ 地图与房间配置
│ └── Shop/ 商店配置
├── UI/
│ └── Panels/ UI 面板配置
└── Settings/ 全局设置与难度配置
```
### 3.2 SO 资产命名规则
格式:`{SystemPrefix}_{描述}.asset`,参考 `Architecture/01_ProjectStructure.md §3` 前缀表。
| 前缀 | 系统 | 示例 |
|------|------|------|
| `EVT_` | 事件频道 | `EVT_PlayerDied.asset` |
| `PLY_` | 玩家 | `PLY_PlayerStats.asset` |
| `CMB_` | 战斗 | `CMB_DamageSource_Sword.asset` |
| `ENM_` | 敌人 | `ENM_E001_Stats.asset` |
| `WPN_` | 武器 | `WPN_SkyBlade.asset` |
| `SKL_` | 技能/法术 | `SKL_SoulBlade.asset` |
| `CHM_` | 护身符 | `CHM_GhostMantis.asset` |
| `SHP_` | 商店 | `SHP_Inventory_Forest.asset` |
| `MAP_` | 地图 | `MAP_RoomData_Forest_01.asset` |
| `AUD_` | 音频 | `AUD_BGMPlaylist_Forest.asset` |
| `UI_` | UI 配置 | `UI_PanelConfig_HUD.asset` |
| `SET_` | 设置 | `SET_GlobalSettings.asset` |
| `ABL_` | 能力 | `ABL_DoubleJump.asset` |
### 3.3 事件频道 SO 特别规则
- 每个事件频道**独立一个文件**,禁止在同一 `.cs` 脚本中定义多个频道类(防止 Script 引用丢失)
- 文件名与类型名严格对应:`EVT_PlayerDied.asset` 对应 `PlayerDiedEventChannelSO`
- **事件频道 SO 不注册 Addressable**,通过 Inspector 直接引用
---
## 4. Prefabs/ 预制体规范
### 4.1 目录结构
```
Prefabs/
├── Player/ 玩家顶级 Prefab含控制器 / 动画 / 碰撞体AddressablePLY_Player· PLY_Player.prefab
├── Enemies/
│ └── {EnemyID}/ 敌人顶级 Prefab含 AI 行为树 / 动画 / 碰撞体AddressableENM_{Name})· ENM_{Name}.prefab
├── Combat/
│ ├── HitBox/ 主动攻击碰撞盒,嵌套于角色 Prefab 内,随角色一同打包,不单独注册 Addressable
│ ├── HurtBox/ 受击判定盒,同上规则,不单独注册 Addressable
│ └── Projectiles/ 抛射物顶级 Prefab独立实例化纳入对象池标签 Poolable· PROJ_{Name}.prefab
├── Effects/ 特效顶级 Prefab粒子系统 / 帧动画),纳入对象池,标签 Poolable · VFX_{Name}.prefab
├── Collectibles/ 可收集物件(灵魂碎片、道具、恢复球),纳入对象池,标签 Poolable · COL_{Name}.prefab├── Environment/ 场景骨架结构 Prefab由关卡场景直接引用不注册 Addressable
│ ├── Tilemaps/ Tilemap 层 GameObject Prefab含 Grid / TilemapRenderer / Collider2D每种地形层独立一个 Prefab · ENV_Tilemap_{Layer}.prefab
│ └── Backgrounds/ 视差滚动背景层 Prefab含 SpriteRenderer + ParallaxScroller每区域按 Far / Mid / Near 分层 · ENV_BG_{Region}_{Layer}.prefab├── World/
│ ├── Interactables/ 场景可交互物件宝箱、NPC 对话触发器、传送门),含交互逻辑组件 · WLD_{Name}.prefab
│ ├── Traps/ 机关陷阱(刺、摆锤、喷火),含周期性伤害触发逻辑 · WLD_{Name}.prefab
│ └── Props/ 纯视觉场景道具(无逻辑),用于场景布景装饰 · WLD_{Name}.prefab
├── UI/ UI 面板顶级 Prefab由 UIManager 通过 Addressable 实例化UI· UI_{PanelName}.prefab
└── Persistent/ 持久场景全局管理器,随 Persistent 场景加载,全程不销毁 · SYS_{ManagerName}.prefab
```
### 4.2 Prefab 命名规则
| 类型 | 前缀 | 示例 |
|------|------|------|
| 玩家 | `PLY_` | `PLY_Player.prefab` |
| 敌人 | `ENM_` | `ENM_GruntWarrior.prefab` |
| 抛射物 | `PROJ_` | `PROJ_Arrow.prefab` |
| 特效 | `VFX_` | `VFX_HitSpark.prefab` |
| UI | `UI_` | `UI_HUD.prefab` |
| 收集物 | `COL_` | `COL_HPOrb.prefab` |
| 世界物件 | `WLD_` | `WLD_Torch.prefab` |
| 武器 | `WPN_` | `WPN_SkyBlade.prefab` |
| 持久对象 | `SYS_` | `SYS_GameManager.prefab` |
| 环境结构 | `ENV_` | `ENV_Tilemap_Ground.prefab``ENV_BG_Forest_Far.prefab` |
### 4.3 Prefab 嵌套规则
- Prefab 内的子物件HitBox、HurtBox、骨骼节点**不单独注册 Addressable**
- 武器 Prefab 嵌套在角色 Prefab 内时,通过 Nested Prefab 引用,不用 Addressables 动态加载
- 只有**顶级可实例化对象**才注册 Addressable 地址
---
## 5. Scenes/ 场景规范
### 5.1 目录结构
```
Scenes/
├── Persistent.unity 常驻场景,承载全局管理器 PrefabGameManager / AudioManager / UIManager永不卸载
├── MainMenu.unity 主菜单场景,游戏启动后首先加载,含主题音乐与过场动画
├── Room_{Region}_{Index:D2}.unity 普通关卡房间,含静态地形与区域触发器(敌人 / 道具由 Spawner 动态实例化)· 例Room_Forest_01.unity
├── Boss_{Name}.unity Boss 专属战斗场景,含专属 BGM、Boss AI 与阶段触发逻辑 · 例Boss_CaoZhi.unity
└── Testings/ 开发测试专用场景,不注册 Addressable不进入正式构建流程
```
### 5.2 场景命名规则
| 类型 | 命名格式 | Addressable 地址 |
|------|---------|----------------|
| 常驻场景 | `Persistent.unity` | `Scene_Persistent` |
| 主菜单 | `MainMenu.unity` | `Scene_MainMenu` |
| 关卡房间 | `Room_{Region}_{Index:D2}.unity` | `Room_Forest_01` |
| Boss 战 | `Boss_{Name}.unity` | `Boss_CaoZhi` |
| 测试场景 | 任意(在 Testings/ | **不注册 Addressable** |
### 5.3 场景内容规范
- **Persistent 场景**:只放全局管理器 Prefab`SYS_GameManager``SYS_AudioManager``SYS_UIManager`),其余全部通过 Addressables 动态加载
- **关卡场景**:只放该关卡的静态地形与触发器;角色、敌人由 Spawner 通过 Addressables 实例化
- 禁止在场景中直接拖拽引用 `Prefabs/` 下的动态对象(改用 Spawner + Addressable Key
---
## 6. Shaders/ 着色器规范
### 6.1 目录结构
```
_Game/Shaders/
├── BaseASE/ Shader Graph 图,每个文件对应一个渲染目的,不合并不相关效果 · {Category}_{Purpose}.shadergraph
│ ├── Character/ 角色类主材质Emission 自发光 + 受击闪白参数)、描边、溶解 / 死亡效果
│ ├── Environment/ 环境类Tilemap 地形着色、多层视差背景、水面 / 液体流动效果
│ ├── Effects/ 特效类VFX Sprite 通用Alpha 混合 + 颜色偏移)、热浪扭曲
│ └── UI/ UI 类:默认 UI 渲染、灰度效果(用于禁用状态的技能 / 道具图标)
└── Includes/ 跨 Shader 共享 HLSL 函数库,通过相对路径 #include 引用 · {FunctionGroup}.hlsl
```
### 6.2 Shader 命名规则
| 类型 | 命名格式 | 示例 |
|------|---------|------|
| Shader Graph | `{Category}_{Purpose}.shadergraph` | `Character_Main.shadergraph` |
| HLSL include | `{FunctionGroup}.hlsl` | `ColorUtils.hlsl` |
| Shader Variant Collection | `SVC_{Category}.shadervariants` | `SVC_Characters.shadervariants` |
### 6.3 Shader 管理规则
- Shader 资产**不注册 Addressable**,由 Material 直接引用Material 随 Prefab 打包
- HLSL include 文件放在 `Shaders/Includes/`,使用**相对路径** `#include` 引用
- 每个渲染目的对应一个 `.shadergraph`**不合并多个不相关效果到同一 Graph**
- Shader 变体数量需受控:通过 `Shader Variant Collection` 预热,避免运行时卡顿
---
## 6.5 Material 管理规范
### 6.5.1 Material 存放原则
Material`.mat`**紧邻使用它的资产存放**,不设全局集中目录:
| 使用对象 | Material 存放位置 | 命名格式 | 示例 |
|---------|----------------|---------|------|
| 玩家角色 | `_Game/Art/Characters/Player/{FormID}/Materials/` | `PLY_{FormID}.mat` | `PLY_Player01.mat` |
| 敌人 | `_Game/Art/Characters/Enemies/{EnemyID}/Materials/` | `ENM_{ID}.mat` | `ENM_E001.mat` |
| NPC | `_Game/Art/Characters/NPCs/{NPCName}/Materials/` | `NPC_{Name}.mat` | `NPC_Merchant.mat` |
| 特效 VFX | `_Game/Art/Effects/Materials/` | `VFX_{Description}.mat` | `VFX_HitSpark.mat` |
| 环境/Tilemap | `_Game/Art/Environment/Tilesets/{Region}/Materials/` | `TILE_{Region}_{Name}.mat` | `TILE_Forest_Ground.mat` |
| 环境背景 | `_Game/Art/Environment/Backgrounds/{Region}/Materials/` | `BG_{Region}.mat` | `BG_Forest.mat` |
| 场景道具 | `_Game/Art/Environment/Props/{Category}/Materials/` | `PROP_{Name}.mat` | `PROP_Chest.mat` |
| UI 专用 | `_Game/Art/UI/Materials/` | `UI_{Description}.mat` | `UI_HealthBar.mat` |
| 共享/通用 | `_Game/Art/Shared/Materials/` | `MAT_{Description}.mat` | `MAT_Dissolve.mat` |
### 6.5.2 Material 命名规则
- 同一对象有多个 Material 时加 `_{Variant}` 后缀区分:
- `ENM_E001.mat`(主材质)
- `ENM_E001_Emission.mat`(发光变体,若需要单独材质)
- `ENM_E001_Flash.mat`(受击闪白材质,通过代码切换)
- Emission 贴图与主贴图共享同一 Material通过 Shader 属性 `_EmissionMap` 关联,**无需单独 Material**
- 受击闪白效果推荐通过 Shader 属性(如 `_FlashAmount`)在运行时控制,避免 Material 实例爆炸
### 6.5.3 Material 实例化规则
```csharp
// ✅ 推荐:通过 MaterialPropertyBlock 修改,不产生 Material 实例
var mpb = new MaterialPropertyBlock();
mpb.SetFloat("_FlashAmount", 1f);
renderer.SetPropertyBlock(mpb);
// ⚠ 避免:直接修改 renderer.material每次调用都创建新实例造成内存泄漏
renderer.material.SetFloat("_FlashAmount", 1f); // ⚠ 产生实例
// ✅ 允许:需要持久独立状态时显式用 Instantiate并在 OnDestroy 中手动 Destroy
_matInstance = Instantiate(renderer.sharedMaterial);
renderer.material = _matInstance;
// ... OnDestroy: Destroy(_matInstance);
```
---
## 7. UI Toolkit/ 规范
```
UI Toolkit/
├── PanelSettings.asset 面板渲染配置Scale Mode、Sort Order、Reference Resolution全局唯一不得创建多个
├── UnityThemes/ Unity 编辑器内置主题文件,不修改
├── Layouts/ UXML 布局文件,每个面板对应一个文件,与 Prefabs/UI/ 同名 · {PanelName}.uxml
└── Styles/ USS 样式表Variables.uss全局 CSS 变量、Common.uss通用控件样式、{PanelName}.uss面板专属样式
```
- `PanelSettings.asset` 全局唯一,不得创建多个
- UXML 和 USS 按界面功能命名,与对应的 `Prefabs/UI/` 同名
---
## 8. Addressables 管理规范
### 8.1 分组Group划分策略
| 组名 | 包含内容 | Build 类型 | 加载时机 |
|------|---------|-----------|---------|
| `Default Local Group` | 常驻 PrefabGameManager、UIManager 等)、全局配置 SO | Local | 启动时自动加载 |
| `UI` | 所有 UI PrefabHUD、菜单、弹框等 | Local | 启动时预加载 |
| `Player` | 玩家 Prefab、武器 Prefab | Local | 游戏开始时加载 |
| `VFX_Common` | 通用高频特效HitSpark、BloodSplat 等) | Local | 启动时预加载 |
| `Collectibles` | 收集物 PrefabGeo、Item、HPOrb | Local | 启动时预加载 |
| `Projectiles` | 抛射物 Prefab | Local | 启动时预加载 |
| `Enemies_{Region}` | 该区域的敌人 Prefab | Local | 进入区域时加载 |
| `Room_{Region}` | 该区域的关卡场景 + 区域专属资产 | Local | 进入区域时加载 |
| `Boss_{Name}` | Boss 专属 Prefab + 场景 | Local | Boss 战开始前加载 |
| `Audio_Music` | BGM 音频FMOD bank 引用) | Remote可选| 按需流式加载 |
| `Config` | 运行时需要动态加载的配置 SO | Local | 按需加载 |
**划分原则**
1. **生命周期相同的资源放同一组**——一起加载、一起卸载
2. **不同区域的资源绝对隔离**——防止 Region A 的资产随 Region B 打包
3. **高频小资产合入 Common 组**——避免大量小 handle 的运行时开销
4. **场景文件与其依赖资产放同一组**——确保 SceneManager 加载时依赖已在本地
### 8.2 Address 命名规则
地址字符串格式:`{SystemPrefix}_{描述}``{Category}/{描述}`
```
# Prefab 类(无路径前缀)
PLY_Player
ENM_GruntWarrior
ENM_SkullArcher
PROJ_Arrow
VFX_HitSpark
UI_HUD
COL_HPOrb
# 场景类
Scene_Persistent
Scene_MainMenu
Room_Forest_01
Boss_CaoZhi
# 配置数据类(带路径前缀区分)
Config/FootstepCatalog
Config/DifficultyEasy
```
**强制要求**:所有 Address 必须在 `AddressKeys.cs` 中定义对应常量,**禁止在代码中硬编码字符串**。
### 8.3 Label标签使用规范
> 完整定义见 `Standards/AddressablesLabelSpec.md`。
| 标签 | 用途 | 相关常量 |
|------|------|---------|
| `Preload` | 游戏启动时通过 `DownloadDependenciesAsync` 预热下载依赖 | `AddressKeys.Labels.Preload` |
| `Poolable` | 纳入 `GlobalObjectPool` 对象池管理的 PrefabVFX、投射物、收集物等| `AddressKeys.Labels.Poolable` |
| `Enemy` | 所有敌人 Prefab用于区域 Spawner `LoadAssetsAsync` 批量加载 | `AddressKeys.Labels.Enemy` |
| `BGM` | BGM 音频 AudioClip / FMOD bank 引用 SO | `AddressKeys.Labels.BGM` |
| `SFX` | 音效 AudioClip / SFX 配置 SO | `AddressKeys.Labels.SFX` |
| `Charms` | 所有护身符配置 SO供 EquipmentManager 批量加载列表 | `AddressKeys.Labels.Charms` |
| `Config` | 运行时动态加载的配置类 SO | `AddressKeys.Labels.Config` |
| `Weapon` | 所有武器 Prefab玩家换形态时批量加载 | `AddressKeys.Labels.Weapon` |
- 一个资产可附加多个标签(例如 `VFX_HitSpark` 同时有 `Poolable``Preload` 标签)
- 新增标签前确认是否有批量加载的实际需求,避免标签膨胀(决策流程见 `AddressablesLabelSpec.md §6`
### 8.4 AddressKeys.cs 维护流程
文件路径:`Assets/_Game/Scripts/Core/Assets/AddressKeys.cs`
**工作流(添加新资产)**
```
1. 在 Project 中创建/导入资产
2. 在 AddressKeys.cs 中添加对应 const 字符串
3. 在 AddressableBatchTool菜单 BaseGames → Tools → Addressable Batch Tool
a. 切换到 "① 同步 AddressKeys" 标签
b. 点击 "Scan" 找到未注册的 Key
c. 选择目标 Group点击 "Register" 完成注册
4. 运行 AddressKeyValidator菜单 BaseGames → Verification → Validate Address Keys
确认无 Missing / Mismatch 警告
5. 提交 AddressKeys.cs 和 AddressableAssetSettings.asset 的修改
```
**工作流(删除/重命名资产)**
```
1. 先在 AddressKeys.cs 中删除/修改对应常量
2. 全局搜索该常量的所有引用并更新
3. 在 Addressables Groups 窗口手动删除或重命名对应条目
4. 重新运行 AddressKeyValidator 验证
5. 提交所有修改(.cs + .asset 文件)
```
### 8.5 AssetLoader 使用规范
封装类路径:`Assets/_Game/Scripts/Core/Assets/AssetLoader.cs`
```csharp
// ✅ 正确:使用 AssetLoader + AddressKeys 常量
var (prefab, handle) = await AssetLoader.LoadAsync<GameObject>(AddressKeys.PrefabPlayer);
_tracker.Track(handle); // 注册到 AssetReleaseTracker场景卸载时自动 Release
// ✅ 正确:对象池中不直接 Release由池管理器统一处理
// ❌ 禁止:直接调用 Addressables API绕过封装层
var handle = Addressables.LoadAssetAsync<GameObject>("PLY_Player"); // ❌
// ❌ 禁止:加载后不 Release内存泄漏
var (prefab, _) = await AssetLoader.LoadAsync<GameObject>(AddressKeys.PrefabPlayer); // ❌ 忘记 Track
```
### 8.6 加载/释放生命周期
```
场景加载时:
SceneManager 激活 → 场景根 GO 上挂 AssetReleaseTracker
→ 各 Spawner/Manager 调用 AssetLoader.LoadAsync → Track handle
场景卸载时:
AsyncOperation.completed → AssetReleaseTracker.OnDestroy 自动 Release 所有 handle
→ 内存归零(无需手动清理)
对象池:
Pool 持有 handle不 Track 到 AssetReleaseTracker
→ Pool 销毁时显式 ReleasePool 自身管理生命周期)
```
### 8.7 Build 策略
| 构建目标 | 命令 | 说明 |
|---------|------|------|
| 开发测试 | `Build > New Build > Default Build Script` | 全量本地构建,输出到 `Library/com.unity.addressables/` |
| 内容更新 | `Build > Update a Previous Build` | 仅重打修改的 Group需保留上次 catalog |
| 生产发布 | CI 流水线触发全量构建 | Remote Group 上传 CDNLocal Group 打入安装包 |
**注意**`AddressableAssetsData/` 目录下的文件由 Unity 自动维护,**不得手动修改 .asset 内容**,只通过 Addressables Groups 窗口操作。
---
## 9. Import Settings 规范
### 9.1 Sprite / Texture Import 规范
| 设置项 | 规范值 | 说明 |
|-------|-------|------|
| Texture Type | `Sprite (2D and UI)` | 角色/特效/UI 图 |
| Sprite Mode | `Multiple`Sprite Sheet`Single` | Sprite Sheet 用 Multiple |
| Pixels Per Unit | `32` | 与项目像素密度保持统一 |
| Filter Mode | `Point (no filter)` | Pixel Art 项目固定值 |
| Compression | `None`(移动平台用 `ASTC 6x6`| 开发阶段 None发布时切换 |
| Generate Mip Maps | `关闭` | 2D 游戏不需要 Mip Maps |
| Read/Write Enabled | `关闭`(除非代码需要像素读写) | 减少内存占用 |
| Max Size | 角色 `2048`UI `1024`,特效 `512` | 按实际尺寸设置上限 |
> **Emission 贴图**`_Emission.png`)使用 **相同 Import 设置**,与主贴图保持一致的 PPU 和 Filter Mode。
### 9.2 AnimationClip Import 规范
- 直接创建的 `.anim` 文件(非 FBX 内嵌)无需额外 Import 设置
- Loop Time循环动作Idle、Move、Skill_Loop开启非循环Death、Hit、Skill_Start关闭
- Sample Rate`12` fpsPixel Art 动画标准帧率)
### 9.3 Audio Import 规范
| 设置项 | SFX | BGM |
|-------|-----|-----|
| Load Type | `Decompress On Load` | `Streaming` |
| Compression Format | `Vorbis` | `Vorbis` |
| Quality | `70%` | `60%` |
| Load In Background | `关闭` | `开启` |
> **注**:若使用 FMODAudio 资产由 FMOD Studio 管理Unity 侧只保留 FMOD Bank 引用 SO不直接导入 AudioClip。
---
## 10. 资源新增工作流
### 10.1 新增敌人
```
1. _Game/Art/Characters/Enemies/ 下创建 {EnemyID}/ 目录,内含 Sprites/ Animations/ Materials/ Atlases/
2. 导入 Sprite Sheet 到 Sprites/,配置 Import SettingsPPU=32, Filter=Point, Multiple
3. 在 Sprite Editor 中切割 Sprite
4. 在 Animations/ 下创建动画片段(.anim和 Animator Controller
5. 在 Materials/ 下创建材质(引用 _Game/Shaders/ 中的角色 Shader关联主纹理和 Emission 贴图)
6. 在 Atlases/ 下创建 Atlas_Enemy_{ID}.spriteatlas包含该敌人所有 Sprite
7. 在 _Game/Data/Enemies/{EnemyID}/ 下创建 ENM_*_Stats.asset 等 SO
8. 在 _Game/Prefabs/Enemies/{EnemyID}/ 下创建 ENM_{Name}.prefab
9. 在 AddressKeys.cs 中添加 PrefabEnemy{Name} 常量
10. 使用 AddressableBatchTool 注册到 Enemies_{Region} 组,添加 Enemy 标签
11. 运行 AddressKeyValidator 验证
```
### 10.2 新增 VFX
```
1. 在 _Game/Art/Effects/Sprites/ 下导入特效 Sprite Sheet配置 Import SettingsPPU=32, Filter=Point
2. 在 _Game/Art/Effects/Materials/ 下创建特效材质
3. 在 _Game/Prefabs/Effects/ 下创建 VFX_{Name}.prefab
4. 在 AddressKeys.cs 中添加 PrefabVFX{Name} 常量
5. 使用 AddressableBatchTool 注册到 VFX_Common 组,添加 Poolable 标签
6. 在 AssetReleaseTracker 配套的 Pool 中注册(纳入对象池)
```
### 10.3 新增 UI 界面
```
1. 在 _Game/UI Toolkit/Layouts/ 下创建 {PanelName}.uxml
2. 在 _Game/UI Toolkit/Styles/ 下创建或复用对应 .uss
3. 在 _Game/Prefabs/UI/ 下创建 UI_{PanelName}.prefab
4. 在 _Game/Data/UI/Panels/ 下创建 UI_PanelConfig_{PanelName}.asset如需配置 SO
5. 在 AddressKeys.cs 中添加 PrefabUI{PanelName} 常量
6. 使用 AddressableBatchTool 注册到 UI 组
```
### 10.4 新增关卡场景
```
1. 在 _Game/Scenes/ 下创建 Room_{Region}_{Index:D2}.unity
2. 在 AddressKeys.cs 中添加地址常量(复用 SceneRoomPrefix + 动态拼接或新增独立常量)
3. 将场景注册到 Room_{Region} 组
4. 在 _Game/Data/World/Map/ 下创建 MAP_RoomData_{Region}_{Index:D2}.asset
```
---
## 11. 禁止行为清单
| 禁止行为 | 原因 | 正确做法 |
|---------|------|---------|
| `Resources.Load<T>("path")` | 绕过 Addressables无法热更 | `AssetLoader.LoadAsync<T>(AddressKeys.Xxx)` |
| 在代码中硬编码 Address 字符串 | 重构困难,易拼写错误 | 使用 `AddressKeys` 常量 |
| 直接调用 `Addressables.LoadAssetAsync` | 绕过封装,难以追踪泄漏 | 使用 `AssetLoader.LoadAsync` |
| 加载后不 Release / 不 Track | 内存泄漏 | `_tracker.Track(handle)` 或显式 Release |
| 在场景中直接引用动态对象 Prefab | 导致场景与 Prefab 耦合,阻碍动态加载 | Spawner + Addressable Key |
| 手动修改 `AddressableAssetsData/*.asset` | 破坏 Addressables 内部状态 | 只通过 Groups 窗口 / AddressableBatchTool 操作 |
| 将 Test 场景注册 Addressable | 污染构建内容 | Test 场景放 `Scenes/Testings/` 并排除于所有 Group |
| 在同一 Prefab 中跨模块直接引用 SO | 产生跨组依赖,导致资产重复打包 | 通过 Addressables 按需加载配置 SO |
| 美术和数据混放在同一目录 | 职责不清,影响构建分析 | 美术在 `_Game/Art/`,数据在 `_Game/Data/`,预制体在 `_Game/Prefabs/` |
| 第三方插件目录内创建自定义资产 | 插件升级时被覆盖 | 自定义内容放在 `_Game/` 下对应模块目录 |
| 将 `_Game/` 内的资产移到 `Assets/` 根目录 | 破坏第一方/第三方隔离原则 | 所有自有资产必须在 `_Game/` 内 |
| 将 `AddressableAssetsData/` 移入 `_Game/` | Unity 硬编码此路径,移动后 Addressables 完全失效 | 永远保留在 `Assets/` 根目录 |