[docs][refactor]: 将旧 01~06 中文教程合并整理为 modules/frameworks 文件夹结构
- 删除 01~05 旧中文教程目录 - 将 03 核心功能教程拆分为各 framework 模块的子文档 - 06 工具脚本移入 modules/frameworks/编辑器工具/UI工具/ - 非框架内容合并进 rules/20-开发规则.md 与 rules/40-工作流清单.md - 更新所有交叉链接与文档关联表 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
95
modules/frameworks/Hotfix启动.md
Normal file
95
modules/frameworks/Hotfix启动.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# Hotfix 启动
|
||||
|
||||
## 分类
|
||||
|
||||
- 核心框架
|
||||
|
||||
## 目的
|
||||
|
||||
记录 hotfix 侧根入口和功能目录聚合,梳理游戏启动链路。
|
||||
|
||||
---
|
||||
|
||||
## 核心路径
|
||||
|
||||
- `Assets/Game/GameHFScripte/GameFunction/GameRoot/`
|
||||
- 关键文件:`HFGameStart.cs`、`HFFunCatalogue_GameManager.cs`
|
||||
- 程序集:`GameHF`
|
||||
|
||||
---
|
||||
|
||||
## 启动调用链
|
||||
|
||||
```text
|
||||
HFGameStart.Init(GameSetting)
|
||||
-> HFFunCatalogue.SetGameSetting
|
||||
-> HFFunCatalogue.ChangeLanguage(enHFLanguageClassify.EN)
|
||||
-> HFGameStartAssetsCheckManager.CheckFirstDownLoadWindowAsset
|
||||
-> HFGameStart.LoadDB
|
||||
-> Hotfix_ZYXlsx_GoogleProtobuf_Helper.LoadAllXlsx
|
||||
-> HFFunCatalogue.Scene.LoadScene(enHFSceneName.Game)
|
||||
-> HFGameStart.InitManager
|
||||
-> HFFunCatalogue.UIManager.OpenWindow<登录窗口>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## InitManager 初始化单例
|
||||
|
||||
`HFGameStart.InitManager()` 负责初始化各业务/框架管理器,例如:
|
||||
|
||||
- `HFSeverManager`
|
||||
- `HFMapManager`
|
||||
- `HFDamageManager`
|
||||
- `HFPopupTextManager`
|
||||
- 各业务管理器(如背包、登录等,按项目实际)
|
||||
|
||||
---
|
||||
|
||||
## HFFunCatalogue 聚合入口
|
||||
|
||||
`HFFunCatalogue` 提供统一访问入口:
|
||||
|
||||
- `UIManager` — UI 窗口
|
||||
- `Asset` — 资源加载
|
||||
- `Scene` — 场景加载
|
||||
- `Sprite` — 图集/精灵
|
||||
- `EntityPool` — 实体池
|
||||
- `LinePool` / `SoundPool` — 其他对象池
|
||||
- `Event` — 事件系统
|
||||
- `GameConstant` — 游戏常量
|
||||
- `NDB` — 配置表读取
|
||||
- `Bag` / `Tips` — 业务相关入口(按项目实际)
|
||||
|
||||
---
|
||||
|
||||
## 使用规则
|
||||
|
||||
- 业务代码通过 `HFFunCatalogue` 访问核心库,禁止直接实例化核心库内部类。
|
||||
- `HFGameStart` 是 hotfix 侧启动入口,不要绕过它直接初始化管理器。
|
||||
- 启动顺序修改需谨慎,确保配置表、资源、场景按正确顺序加载。
|
||||
|
||||
---
|
||||
|
||||
## 相关模块
|
||||
|
||||
- [UI 窗口系统](UI窗口系统/README.md)
|
||||
- [资源管理](资源管理/README.md)
|
||||
- [场景系统](场景系统.md)
|
||||
- [数据/配置框架](数据配置框架.md)
|
||||
|
||||
---
|
||||
|
||||
## 文档关联
|
||||
|
||||
> 当本文档发生变更时,应同步检查以下关联文档,避免信息不一致。
|
||||
|
||||
| 文档 | 关联原因 |
|
||||
|---|---|
|
||||
| [modules/模块索引.md](../模块索引.md) | 模块索引是项目知识总入口 |
|
||||
| [modules/frameworks/UI窗口系统/README.md](UI窗口系统/README.md) | 启动后打开登录窗口依赖 UI 框架 |
|
||||
| [modules/frameworks/资源管理/README.md](资源管理/README.md) | 启动流程涉及资源检查 |
|
||||
| [modules/frameworks/场景系统.md](场景系统.md) | 启动流程涉及场景加载 |
|
||||
| [modules/frameworks/数据配置框架.md](数据配置框架.md) | 启动流程涉及配置表加载 |
|
||||
| [rules/10-架构说明.md](../../rules/10-架构说明.md) | 架构说明定义总体分层和依赖方向 |
|
||||
| [rules/30-模块登记规则.md](../../rules/30-模块登记规则.md) | 模块登记规则定义分类和状态口径 |
|
||||
129
modules/frameworks/UI窗口系统/README.md
Normal file
129
modules/frameworks/UI窗口系统/README.md
Normal file
@@ -0,0 +1,129 @@
|
||||
# UI 框架
|
||||
|
||||
## 分类
|
||||
|
||||
- 项目框架
|
||||
|
||||
## 目的
|
||||
|
||||
记录 UIWindow 管理、窗口基类、UIMono 桥接、窗口资源加载和 UI 生命周期。
|
||||
|
||||
## 已知线索
|
||||
|
||||
- 管理路径:`Assets/Game/GameHFScripte/CopyToHF/UIWindowMgr/`
|
||||
- 窗口实现:`Assets/Game/GameHFScripte/GameFunction/UIWindows/`
|
||||
- Mono 桥接:`Assets/Game/GameMono/Scripts/UIMono/`
|
||||
- 资源路径:`Assets/Game/GameHFResource/CN/UIWindows/`
|
||||
|
||||
## 已确认链路
|
||||
|
||||
UI manager 初始化:
|
||||
|
||||
```text
|
||||
HFUIWindowMgr.OnAfterCreateInstance()
|
||||
-> OnInitializeCanvasAndCamera()
|
||||
-> 为 enHFUICanvasLayer 创建 Canvas
|
||||
-> 为 ScreenSpaceCamera Canvas 创建 UI Camera
|
||||
-> 创建 CacheSiblingIndex Canvas
|
||||
-> 创建或接管 EventSystem / StandaloneInputModule
|
||||
```
|
||||
|
||||
打开窗口:
|
||||
|
||||
```text
|
||||
HFFunCatalogue.UIManager.OpenWindow<T>()
|
||||
-> HFUIWindowMgr.OnOpenWindow<HFUIWindowSetting>()
|
||||
-> 创建 HFDefaultTask / HFUIWindow_RespondTask
|
||||
-> 读取窗口类型上的 HFAssetBundlePathAttribute 和 HFUIWindowLayerAttribute
|
||||
-> OnSettingWindowSiblingIndex()
|
||||
-> 从缓存队列复用窗口实例,或 Activator.CreateInstance 创建窗口逻辑实例
|
||||
-> OnPreloadWindowInMemory()
|
||||
-> HFABMgr.Instance.LoadInMemory(windowPrefabPath)
|
||||
-> win.BindGameObject(prefab)
|
||||
-> OnAttachWindow(win)
|
||||
-> win.SyncWindowState()
|
||||
-> AbsHFUIWindow.OnOpen()
|
||||
```
|
||||
|
||||
关闭窗口:
|
||||
|
||||
```text
|
||||
CloseWindow<T>() / AbsHFUIWindow.CloseWindow()
|
||||
-> HFUIWindowMgr.OnCloseWindow()
|
||||
-> siblingIndexLayer.ToggleWindowActive(false)
|
||||
-> 放入 mCacheWindowInstanceMaping
|
||||
-> 处理 backtrack 自动恢复窗口
|
||||
-> win.SyncWindowState()
|
||||
-> AbsHFUIWindow.OnClose()
|
||||
```
|
||||
|
||||
窗口资源绑定:
|
||||
|
||||
- 窗口类通过 `[HFAssetBundlePath(...)]` 标记 prefab 路径。
|
||||
- 窗口类通过 `[HFUIWindowLayer(...)]` 标记 Canvas/Layer 和是否允许多开。
|
||||
- `AbsHFUIWindowSetting.SetWindowType` 会读取上述 attribute,生成 `winId`、`winTypeId` 和资源路径。
|
||||
|
||||
## 使用规则
|
||||
|
||||
- 新窗口必须继承 `AbsHFUIWindow`。
|
||||
- 新窗口必须配置 `HFAssetBundlePath` 和 `HFUIWindowLayer`。
|
||||
- 打开窗口统一走 `HFFunCatalogue.UIManager.OpenWindow<T>()`。
|
||||
- 关闭窗口统一走 `CloseWindow()` 或 `HFFunCatalogue.UIManager.CloseWindow<T>()`。
|
||||
- 在 `OnOpen` 中注册的事件必须在 `OnClose` 中移除。
|
||||
- 不直接 `Instantiate` / `Destroy` UI 窗口 prefab。
|
||||
- 不绕过 `HFUIWindowMgr` 手动修改窗口 active、父节点或 siblingIndex。
|
||||
- 普通业务窗口默认使用 `Dynamic`,提示类优先使用 `Tooltip`,强交互弹窗优先使用 `Popup`,`Highest` 仅用于明确的顶层兜底。
|
||||
- UI prefab 放在 `Assets/Game/GameHFResource/<语言>/UIWindows/<模块>/`,代码中的 `HFAssetBundlePath` 使用相对 `UIWindows/...` 的路径。
|
||||
- 优先使用项目已有 UIMono / ZYUGUI 绑定流程,不手写大量 `transform.Find`。
|
||||
- 列表、循环列表、网格优先使用已有 ZYUGUI / SuperScrollView 组件和编辑器转换工具。
|
||||
- 异步加载或动画结束后再调用完成回调时,必须保证失败/取消路径不会卡住窗口状态。
|
||||
|
||||
## 新增 UI 窗口清单
|
||||
|
||||
1. 确认窗口所属功能模块。
|
||||
2. 创建窗口类,继承 `AbsHFUIWindow`。
|
||||
3. 添加 `HFAssetBundlePath` 和 `HFUIWindowLayer`。
|
||||
4. 创建或更新 prefab。
|
||||
5. 使用 UIMono / ZYUGUI 生成或绑定 UI 引用。
|
||||
6. 用 `HFFunCatalogue.UIManager.OpenWindow<T>()` 接入入口。
|
||||
7. 在 `OnOpen` / `OnClose` 对称处理事件、计时器、临时数据。
|
||||
8. 验证打开、关闭、重复打开、切场景或语言变化时的表现。
|
||||
9. 如果新增模块或改变 UI 架构,更新相关模块文档。
|
||||
|
||||
## 梳理任务
|
||||
|
||||
- [x] 梳理窗口基类和打开/关闭 API。
|
||||
- [x] 梳理窗口 prefab 路径绑定方式。
|
||||
- [ ] 梳理 UIMono 自动绑定和 partial 生成关系。
|
||||
- [x] 梳理窗口生命周期:Open/Close/状态同步。
|
||||
- [x] 梳理 UI 事件订阅和释放规范。
|
||||
- [x] 梳理功能 UI 和框架 UI 的边界。
|
||||
|
||||
## 待确认
|
||||
|
||||
- 常规入口已确认是 `HFFunCatalogue.UIManager` / `HFUIWindowMgr`,是否存在绕过路径待继续扫描。
|
||||
- UI 资源会经过 `HFAssetBundlePathAttribute` 和语言路径规则,哪些 UI 真正有 CN/EN 差异待确认。
|
||||
- UIMono 是否由编辑器工具生成。
|
||||
- `OnOpen`/`OnClose` 外是否还有业务窗口统一订阅/释放规范待确认。
|
||||
|
||||
## 相关模块
|
||||
|
||||
- [资源框架](../资源管理/README.md)
|
||||
- [事件框架](../事件系统/README.md)
|
||||
- [ZYUGUI Tools](../../模块索引.md#编辑器工具)
|
||||
|
||||
---
|
||||
|
||||
## 文档关联
|
||||
|
||||
> 当本文档发生变更时,应同步检查以下关联文档,避免信息不一致。
|
||||
|
||||
| 文档 | 关联原因 |
|
||||
|---|---|
|
||||
| [modules/模块索引.md](../../模块索引.md) | ZYUGUI Tools |
|
||||
| [modules/frameworks/事件系统/README.md](../事件系统/README.md) | 事件框架 |
|
||||
| [modules/frameworks/资源管理/README.md](../资源管理/README.md) | 资源框架 |
|
||||
| [rules/10-架构说明.md](../../../rules/10-架构说明.md) | 架构说明定义总体分层和依赖方向 |
|
||||
| [rules/20-开发规则.md](../../../rules/20-开发规则.md) | 开发规则与框架/生成规则互相引用 |
|
||||
| [rules/30-模块登记规则.md](../../../rules/30-模块登记规则.md) | 模块登记规则定义分类和状态口径 |
|
||||
| TODO:补充依赖或使用本框架的模块文档 | 如依赖本框架的功能模块 |
|
||||
231
modules/frameworks/UI窗口系统/开发指南.md
Normal file
231
modules/frameworks/UI窗口系统/开发指南.md
Normal file
@@ -0,0 +1,231 @@
|
||||
# StrayFog 框架 UI 开发指南
|
||||
|
||||
## 一、窗口开发
|
||||
|
||||
### 1.1 创建模板
|
||||
```csharp
|
||||
[HFAssetBundlePath(enHFLanguageRootFolder.ASSETS_GAME_GAMEHFRESOURCE, "UIWindows/MyWindow/MyWindow.prefab")]
|
||||
[HFUIWindowLayer(enHFUICanvasLayer.UIWindow, enHFUIWindowLayer.Dynamic)]
|
||||
public class MyWindow : AbsHFUIWindow
|
||||
{
|
||||
private SFUI_Button BtnClose;
|
||||
|
||||
protected override void OnRunAwake()
|
||||
{
|
||||
base.OnRunAwake();
|
||||
BtnClose = FindUICtrlByName<SFUI_Button>("BtnClose");
|
||||
BtnClose.OnAddClickListener += OnBtnClose;
|
||||
}
|
||||
|
||||
protected override void OnOpen(Action _onOpenComplete)
|
||||
{
|
||||
base.OnOpen(_onOpenComplete);
|
||||
RefreshUI();
|
||||
}
|
||||
|
||||
private void OnBtnClose(GameObject go) => CloseWindow();
|
||||
private void RefreshUI() { }
|
||||
}
|
||||
```
|
||||
|
||||
### 1.2 生命周期
|
||||
|
||||
| 方法 | 调用时机 | 推荐操作 |
|
||||
|-----|---------|---------|
|
||||
| `OnRunAwake` | 创建时一次 | 绑定 UI 事件、初始化滚动列表 `BindEvent` |
|
||||
| `OnOpen` | 每次打开 | 注册 TC 事件、调用 `ShowView()` 刷新数据 |
|
||||
| `OnClose` | 每次关闭 | 取消 TC 事件 |
|
||||
|
||||
**关键原则**:
|
||||
- **绑定类操作**(如 `BindEvent`)放在 `OnRunAwake`,只需执行一次
|
||||
- **刷新类操作**(如 `ShowView`)放在 `OnOpen`,每次打开都需执行
|
||||
|
||||
### 1.3 窗口操作
|
||||
```csharp
|
||||
// 打开
|
||||
HFFunCatalogue.UIManager.OpenWindow<MyWindow>();
|
||||
// 关闭
|
||||
HFFunCatalogue.UIManager.CloseWindow<MyWindow>();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、事件绑定
|
||||
|
||||
| 事件类型 | 绑定位置 | 是否需要 `-=` |
|
||||
|---------|---------|--------------|
|
||||
| UI 按钮/拖拽 | `OnRunAwake` | 不需要 |
|
||||
| 全局 TC 事件 | `OnOpen`/`OnClose` | 需要 |
|
||||
|
||||
---
|
||||
|
||||
## 三、常用组件
|
||||
|
||||
### 3.1 按钮
|
||||
```csharp
|
||||
SFUI_Button btn = GetComponent<SFUI_Button>();
|
||||
btn.OnAddClickListener += OnBtnClick;
|
||||
btn.SetSelfGray(true);
|
||||
```
|
||||
|
||||
### 3.2 文本
|
||||
```csharp
|
||||
SFUI_TextMeshProUGUI txt = GetComponent<SFUI_TextMeshProUGUI>();
|
||||
txt.text = "Hello";
|
||||
```
|
||||
|
||||
### 3.3 图片
|
||||
```csharp
|
||||
SFUI_Image img = GetComponent<SFUI_Image>();
|
||||
img.HFSetSprite(/* atlasName */, "Icon_Player", /* languageRoot */);
|
||||
```
|
||||
|
||||
### 3.4 滚动列表(核心)
|
||||
|
||||
**生命周期正确划分示例**:
|
||||
|
||||
```csharp
|
||||
private SFUI_ScrollRect m_ScrollList;
|
||||
private List<ItemData> m_ItemList;
|
||||
|
||||
protected override void OnRunAwake()
|
||||
{
|
||||
base.OnRunAwake();
|
||||
m_ScrollList = FindUICtrlByName<SFUI_ScrollRect>("ScrollList");
|
||||
|
||||
// ✅ 绑定事件放在 Awake(一次绑定,永久生效)
|
||||
m_ScrollList.BindEvent(
|
||||
() => m_ItemList.Count, // 数据源闭包
|
||||
(idx) => itemPrefab.gameObject, // 生成预制体
|
||||
OnUpdateItem, // 更新回调
|
||||
(item) => item.gameObject.HFGetOrAddSimulateBehaviour<ItemSlot>() // 获取组件
|
||||
);
|
||||
}
|
||||
|
||||
protected override void OnOpen(Action _onOpenComplete)
|
||||
{
|
||||
base.OnOpen(_onOpenComplete);
|
||||
m_ItemList = GetDataFromServer(); // 获取最新数据
|
||||
m_ScrollList.ShowView(); // ✅ 刷新显示放在 Open
|
||||
}
|
||||
|
||||
private void OnUpdateItem(int index, AbsHFUIWindow window)
|
||||
{
|
||||
ItemSlot slot = window as ItemSlot;
|
||||
slot.SetData(m_ItemList[index]);
|
||||
}
|
||||
```
|
||||
|
||||
**设计要点**:
|
||||
- `BindEvent` 在 `OnRunAwake` 中执行,建立绑定机制
|
||||
- `ShowView` 在 `OnOpen` 中执行,触发数据刷新
|
||||
- 数据源通过闭包引用,`ShowView` 时自动获取最新数据
|
||||
|
||||
---
|
||||
|
||||
## 四、对象池
|
||||
|
||||
| 场景 | 推荐方式 |
|
||||
|-----|---------|
|
||||
| 滚动列表 | `SFUI_ScrollRect.BindEvent`(内置池) |
|
||||
| 固定栏(装备/技能) | `HFSimulateBehaviourPool<T>` |
|
||||
|
||||
---
|
||||
|
||||
## 五、子组件
|
||||
|
||||
### 5.1 多态基类
|
||||
```csharp
|
||||
public abstract class AbsListItem<T> : AbsHFUIWindow
|
||||
{
|
||||
protected SFUI_Button m_BtnItem;
|
||||
protected Action<T> m_OnSelect;
|
||||
|
||||
protected override void OnRunAwake()
|
||||
{
|
||||
base.OnRunAwake();
|
||||
m_BtnItem = gameObject.GetComponent<SFUI_Button>();
|
||||
m_BtnItem.OnAddClickListener += OnBtnClick;
|
||||
}
|
||||
|
||||
public virtual void SetData(T data, Action<T> onSelect = null)
|
||||
{
|
||||
m_OnSelect = onSelect;
|
||||
OnDataChanged(data);
|
||||
}
|
||||
|
||||
protected abstract void OnDataChanged(T data);
|
||||
private void OnBtnClick(GameObject go) => m_OnSelect?.Invoke(m_CurrentData);
|
||||
}
|
||||
|
||||
// 子类
|
||||
public class CardItem : AbsListItem<CardData>
|
||||
{
|
||||
protected override void OnDataChanged(CardData data) { }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、开发规范
|
||||
|
||||
### 6.1 文件路径
|
||||
|
||||
#### 6.1.1 核心资源路径
|
||||
|
||||
| 类型 | 路径 | 说明 |
|
||||
|-----|------|------|
|
||||
| **窗口脚本** | `Assets/Game/GameHFScripte/GameFunction/UIWindows/{WindowName}/` | 窗口逻辑脚本存放目录 |
|
||||
| **窗口预制体** | `Assets/Game/GameHFResource/CN/UIWindows/{WindowName}/` | UI 窗口预制体存放目录 |
|
||||
| **图标图集** | `Assets/Game/GameHFResource/CN/SpriteAtlas/{AtlasName}/` | SpriteAtlas 图集资源 |
|
||||
| **字体资源** | `Assets/Game/GameHFResource/CN/Fonts/` | 字体文件存放目录 |
|
||||
|
||||
#### 6.1.2 工具脚本路径
|
||||
|
||||
| 工具名称 | 路径 | 职责 |
|
||||
|---------|------|------|
|
||||
| **UIPrefabGenerator** | `Assets/Editor/Command/UIPrefabGenerator.cs` | JSON 转 UI 预制体 |
|
||||
| **UIPrefabToJson** | `Assets/Editor/Command/UIPrefabToJson.cs` | 预制体转 JSON |
|
||||
| **RemoteCommand** | `Assets/Editor/Command/RemoteCommand.cs` | 外部命令接口 |
|
||||
| **ExternalCommandListener** | `Assets/Editor/Command/ExternalCommandListener.cs` | 外部命令监听器 |
|
||||
|
||||
#### 6.1.3 文档路径
|
||||
|
||||
| 文档名称 | 路径 |
|
||||
|---------|------|
|
||||
| UI 开发指南 | [UI窗口系统/开发指南.md](开发指南.md) |
|
||||
| UI 界面拼接设计指南 | [UI窗口系统/界面拼接.md](界面拼接.md) |
|
||||
|
||||
#### 6.1.4 命令触发路径
|
||||
|
||||
- **触发文件**: `{ProjectRoot}/Trigger_Command.txt`
|
||||
- **JSON 配置**: `Assets/Editor/UI/{WindowName}.json`
|
||||
|
||||
### 6.2 性能优化
|
||||
- 使用对象池复用 UI 元素
|
||||
- 将图标打包到 SpriteAtlas
|
||||
- 滚动列表用 `SFUI_ScrollRect.BindEvent`
|
||||
|
||||
---
|
||||
|
||||
**版本**: v1.1
|
||||
**适用**: StrayFog 框架 UI 开发人员
|
||||
|
||||
---
|
||||
|
||||
## 文档关联
|
||||
|
||||
> 当本文档发生变更时,应同步检查以下关联文档,避免信息不一致。
|
||||
|
||||
| 文档 | 关联原因 |
|
||||
|---|---|
|
||||
| [UI 窗口系统入口](README.md) | 本文件是 UI 窗口系统模块的子文档 |
|
||||
| [UI 界面拼接设计指南](界面拼接.md) | 界面拼接是 UI 开发的补充设计指南 |
|
||||
| [UI 拼接工具](../编辑器工具/UI拼接工具.md) | JSON 驱动 UI 生成工具说明 |
|
||||
| [事件系统](../事件系统/README.md) | UI 事件订阅和释放规范 |
|
||||
| [对象池与运行时对象管理](../内存对象管理/README.md) | UI 元素复用相关 |
|
||||
| [资源管理](../资源管理/README.md) | UI 资源加载依赖资源管理 |
|
||||
| [modules/模块索引.md](../../模块索引.md) | 模块索引是项目知识总入口 |
|
||||
| [rules/10-架构说明.md](../../../rules/10-架构说明.md) | 架构说明定义总体分层和依赖方向 |
|
||||
| [rules/20-开发规则.md](../../../rules/20-开发规则.md) | UI 命名与编码规范互相引用 |
|
||||
| [rules/30-模块登记规则.md](../../../rules/30-模块登记规则.md) | 模块登记规则定义分类和状态口径 |
|
||||
192
modules/frameworks/UI窗口系统/界面拼接.md
Normal file
192
modules/frameworks/UI窗口系统/界面拼接.md
Normal file
@@ -0,0 +1,192 @@
|
||||
# UI 界面拼接设计指南
|
||||
|
||||
## 一、界面结构设计原则
|
||||
|
||||
### 1.1 模块化拆分
|
||||
|
||||
将界面按功能划分为独立模块:
|
||||
|
||||
| 模块类型 | 功能定位 | 示例 |
|
||||
|---------|---------|------|
|
||||
| **信息展示区** | 显示玩家状态、资源等静态信息 | 顶部信息栏、左侧状态栏 |
|
||||
| **功能入口区** | 提供功能按钮入口 | 中央功能区、底部快捷栏 |
|
||||
| **交互面板区** | 可展开/收起的功能面板 | 右侧功能面板、顶部按钮区 |
|
||||
| **输入输出区** | 用户输入和系统反馈 | 聊天输入框、提示信息 |
|
||||
|
||||
### 1.2 经典布局模式
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ TopArea (信息展示 + 可收起功能按钮) │
|
||||
├──────────┬───────────────────────┬──────────────────┤
|
||||
│ Left │ │ Right │
|
||||
│ Area │ CenterArea │ Area │
|
||||
│ (状态) │ (核心功能入口) │ (功能面板) │
|
||||
│ │ │ │
|
||||
├──────────┴───────────────────────┴──────────────────┤
|
||||
│ BottomArea (快捷功能 + 状态信息 + 输入) │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、组件拆分方法
|
||||
|
||||
### 2.1 基础组件识别
|
||||
|
||||
| 组件类型 | 识别特征 | 处理方式 |
|
||||
|---------|---------|---------|
|
||||
| **按钮** | 可点击、有交互反馈 | 使用 `SFUI_Button` |
|
||||
| **文本** | 静态/动态文字显示 | 使用 `SFUI_TextMeshProUGUI` |
|
||||
| **图片** | 图标、背景图 | 使用 `SFUI_Image` |
|
||||
| **进度条** | HP、MP、经验等 | 使用 `SFUI_Slider` |
|
||||
| **列表** | 可滚动的项目列表 | 使用 `SFUI_ScrollRect` |
|
||||
|
||||
### 2.2 复合组件封装
|
||||
|
||||
将功能相关的基础组件组合为复合组件,便于复用和维护:
|
||||
|
||||
**设计原则**:
|
||||
- 将同一功能模块的组件封装在一起(如玩家头像+血条+蓝条)
|
||||
- 提供统一的数据设置接口
|
||||
- 保持组件之间的逻辑独立性
|
||||
|
||||
---
|
||||
|
||||
## 三、预制体层级规范
|
||||
|
||||
### 3.1 标准层级结构
|
||||
|
||||
```
|
||||
WindowName (RectTransform)
|
||||
├── ModuleName (RectTransform) # 模块容器
|
||||
│ ├── ComponentName (SFUI_*) # 功能组件
|
||||
│ ├── SubModule (RectTransform) # 子模块
|
||||
│ └── CollapsibleArea (RectTransform) # 可收起区域
|
||||
└── BtnCollapse (SFUI_Button) # 收起控制按钮
|
||||
```
|
||||
|
||||
### 3.2 命名规范
|
||||
|
||||
| 类型 | 命名规则 | 示例 |
|
||||
|-----|---------|------|
|
||||
| 模块容器 | PascalCase + Area | `TopArea` |
|
||||
| 功能组件 | PascalCase | `BtnCollapseTop` |
|
||||
| 可收起区域 | PascalCase + FuncArea | `TopFuncArea` |
|
||||
| 状态变量 | m_ + PascalCase | `m_IsTopCollapsed` |
|
||||
|
||||
---
|
||||
|
||||
## 四、收起/展开功能设计
|
||||
|
||||
### 4.1 功能定义
|
||||
|
||||
| 按钮 | 控制区域 | 用途 |
|
||||
|-----|---------|------|
|
||||
| `BtnCollapseTop` | 顶部功能按钮区 | 收起/展开顶部按钮群 |
|
||||
| `BtnCollapseRight` | 右侧功能面板区 | 收起/展开右侧功能面板 |
|
||||
|
||||
### 4.2 设计要点
|
||||
|
||||
**状态管理**:每个可收起区域需要一个布尔状态变量来记录当前收起/展开状态。
|
||||
|
||||
**交互逻辑**:
|
||||
1. 点击收起按钮时,切换对应区域的显示/隐藏状态
|
||||
2. 同时切换按钮图标(收起时显示展开图标,展开时显示收起图标)
|
||||
|
||||
**布局要求**:
|
||||
- 收起按钮应放置在对应区域的边缘位置,便于用户找到
|
||||
- 收起区域内的所有元素应作为一个整体进行显示/隐藏控制
|
||||
- 收起状态应在窗口生命周期内保持记忆
|
||||
|
||||
---
|
||||
|
||||
## 五、实际案例:主界面拼接
|
||||
|
||||
### 5.1 模块化设计思想
|
||||
|
||||
**核心原则**:将界面按功能边界划分为独立模块,模块内可嵌套子模块。
|
||||
|
||||
**模块归类方法**:
|
||||
|
||||
1. **一级模块**:按空间位置划分(顶部、左侧、中央、右侧、底部)
|
||||
2. **二级模块**:按功能类型划分(信息展示区、功能按钮区、输入输出区)
|
||||
3. **三级模块**:按具体功能划分(玩家头像、资源显示、功能按钮群)
|
||||
|
||||
**层级关系示例**:
|
||||
|
||||
```
|
||||
Window
|
||||
├── TopArea (一级:位置)
|
||||
│ ├── InfoPanel (二级:信息展示)
|
||||
│ │ └── TxtWelcome (三级:具体组件)
|
||||
│ └── FuncButtons (二级:功能按钮)
|
||||
│ └── BtnUpload (三级:具体按钮)
|
||||
└── LeftArea (一级:位置)
|
||||
└── PlayerPortrait (二级:复合组件)
|
||||
├── ImgAvatar (三级:头像)
|
||||
└── BarHP (三级:血条)
|
||||
```
|
||||
|
||||
**设计要点**:
|
||||
- 同一功能域内的元素归为同一模块(如头像、血条、蓝条归为玩家状态模块)
|
||||
- 独立交互功能作为独立模块(如收起按钮单独作为控制模块)
|
||||
- 可收起区域作为独立子模块,便于统一控制显示/隐藏
|
||||
|
||||
---
|
||||
|
||||
## 六、界面拼接流程
|
||||
|
||||
### 6.1 设计阶段
|
||||
1. **需求分析**:明确界面功能和交互需求
|
||||
2. **模块划分**:将界面拆分为独立功能模块
|
||||
3. **层级设计**:设计预制体层级结构
|
||||
|
||||
### 6.2 开发阶段
|
||||
1. **组件创建**:创建基础组件和复合组件
|
||||
2. **预制体搭建**:按层级结构搭建预制体
|
||||
3. **脚本编写**:实现交互逻辑和收起/展开功能
|
||||
|
||||
### 6.3 测试阶段
|
||||
1. **功能测试**:验证所有按钮和交互功能
|
||||
2. **收起测试**:测试收起/展开功能正常工作
|
||||
3. **适配测试**:验证不同分辨率下的显示效果
|
||||
|
||||
---
|
||||
|
||||
## 七、注意事项
|
||||
|
||||
1. **组件复用**:相同功能的组件尽量复用,减少冗余
|
||||
2. **状态管理**:收起状态需要正确保存和恢复
|
||||
3. **图标反馈**:收起按钮图标应随状态变化
|
||||
4. **性能优化**:可收起区域使用 `SetActive` 而非销毁重建
|
||||
5. **命名规范**:统一的命名规则便于维护和查找
|
||||
|
||||
---
|
||||
|
||||
## 八、JSON 配置驱动的 UI 生成方式
|
||||
|
||||
本项目支持通过 JSON 配置快速生成 UI 预制体,详细工具说明、命令格式和部署步骤见 [UI 拼接工具](../编辑器工具/UI拼接工具.md)。
|
||||
|
||||
UI 相关人员核心工作流程:读取设计效果图和碎片图片,生成 JSON 配置文件,再通过工具自动生成 Unity 预制体。
|
||||
|
||||
---
|
||||
|
||||
**版本**: v1.2
|
||||
**适用**: StrayFog 框架 UI 拼接开发人员
|
||||
|
||||
---
|
||||
|
||||
## 文档关联
|
||||
|
||||
> 当本文档发生变更时,应同步检查以下关联文档,避免信息不一致。
|
||||
|
||||
| 文档 | 关联原因 |
|
||||
|---|---|
|
||||
| [UI 窗口系统入口](README.md) | 本文件是 UI 窗口系统模块的子文档 |
|
||||
| [UI 开发指南](开发指南.md) | 窗口开发与界面拼接互相补充 |
|
||||
| [UI 拼接工具](../编辑器工具/UI拼接工具.md) | JSON 驱动 UI 生成工具说明 |
|
||||
| [modules/模块索引.md](../../模块索引.md) | 模块索引是项目知识总入口 |
|
||||
| [rules/10-架构说明.md](../../../rules/10-架构说明.md) | 架构说明定义总体分层和依赖方向 |
|
||||
| [rules/20-开发规则.md](../../../rules/20-开发规则.md) | UI 命名与资源规则互相引用 |
|
||||
| [rules/30-模块登记规则.md](../../../rules/30-模块登记规则.md) | 模块登记规则定义分类和状态口径 |
|
||||
122
modules/frameworks/事件系统/README.md
Normal file
122
modules/frameworks/事件系统/README.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# 事件系统
|
||||
|
||||
## 分类
|
||||
|
||||
- 核心框架
|
||||
|
||||
## 目的
|
||||
|
||||
提供客户端内部事件(CTC)和服务器消息事件(STC)的统一派发、监听和释放机制。
|
||||
|
||||
---
|
||||
|
||||
## 核心路径
|
||||
|
||||
- 框架层:`Assets/Game/GameHFScripte/CopyToHF/Event/`
|
||||
- 功能层:`Assets/Game/GameHFScripte/GameFunction/Event/`
|
||||
- STC 事件参数:`Assets/Game/GameHFScripte/GameFunction/Event/STCEventArg/`
|
||||
|
||||
---
|
||||
|
||||
## 核心类
|
||||
|
||||
| 类 | 职责 |
|
||||
|---|---|
|
||||
| `HFEventManager` | 事件管理器单例,继承 `AbsHFSingleRunTime<HFEventManager>` |
|
||||
| `TCEvent` | CTC 事件句柄 |
|
||||
| `STCEvent` | STC 事件句柄 |
|
||||
| `CTCHandle` | CTC 事件处理器定义位置 |
|
||||
| `AbsHFEventArg<K,T>` | 事件参数基类 |
|
||||
|
||||
---
|
||||
|
||||
## 事件派发链路
|
||||
|
||||
### CTC 事件
|
||||
|
||||
```text
|
||||
AbsHFEventArg<enHFCTCEvent>.Dispatch()
|
||||
-> HFEventManager.Instance.TCEvent.Dispatch(arg)
|
||||
```
|
||||
|
||||
CTC 事件来源:
|
||||
|
||||
- 功能代码手动创建事件参数对象。
|
||||
- 设置字段后调用 `.Dispatch()`。
|
||||
- 示例:业务管理器派发初始化事件、背包更新事件。
|
||||
|
||||
### STC 事件
|
||||
|
||||
```text
|
||||
AbsHFEventArg<enHFSTCEvent>.Dispatch()
|
||||
-> HFEventManager.Instance.STCEvent.Dispatch(arg)
|
||||
```
|
||||
|
||||
STC 事件来源:
|
||||
|
||||
```text
|
||||
WSSession 收到 protobuf S2C
|
||||
-> 反射创建 HFEvent_STC_{MessageTypeName}
|
||||
-> 写入 Request / ErrorCode
|
||||
-> Dispatch 到 STCEvent
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 监听与派发机制
|
||||
|
||||
```text
|
||||
AddListener<TEventArg>(eventId, callback)
|
||||
-> 按 eventId 分组
|
||||
-> 按事件参数类型 hash 分组
|
||||
|
||||
Dispatch(arg)
|
||||
-> 根据 arg.eventId 找事件组
|
||||
-> 根据 arg.GetType() 找具体类型监听
|
||||
-> Invoke callbacks
|
||||
-> arg.Recycle()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 使用规则
|
||||
|
||||
- UI 窗口在 `OnOpen` 中注册的事件,必须在 `OnClose` 中移除。
|
||||
- 管理器监听 STC 事件通常在初始化时添加,在 `Recycle` 或销毁时移除。
|
||||
- 不要直接监听与当前模块无关的事件。
|
||||
- 事件参数对象由框架自动回收,业务代码不要重复释放。
|
||||
|
||||
---
|
||||
|
||||
## 错误响应
|
||||
|
||||
当服务器返回错误响应时:
|
||||
|
||||
```text
|
||||
messagePage.IsError = true
|
||||
-> 解析 ErrorResponse
|
||||
-> 派发 HFEvent_ErrorCode
|
||||
-> 同时仍创建对应 HFEvent_STC_* 并写入 ErrorCode
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 相关模块
|
||||
|
||||
- [网络系统](../网络系统.md)
|
||||
- [UI 窗口系统](../UI窗口系统/README.md)
|
||||
- [实体系统](../实体系统/README.md)
|
||||
|
||||
---
|
||||
|
||||
## 文档关联
|
||||
|
||||
> 当本文档发生变更时,应同步检查以下关联文档,避免信息不一致。
|
||||
|
||||
| 文档 | 关联原因 |
|
||||
|---|---|
|
||||
| [modules/模块索引.md](../../模块索引.md) | 模块索引是项目知识总入口 |
|
||||
| [modules/frameworks/网络系统.md](../网络系统.md) | S2C 消息是 STC 事件来源 |
|
||||
| [modules/frameworks/UI窗口系统/README.md](../UI窗口系统/README.md) | UI 窗口订阅和释放事件 |
|
||||
| [rules/10-架构说明.md](../../../rules/10-架构说明.md) | 架构说明定义总体分层和依赖方向 |
|
||||
| [rules/30-模块登记规则.md](../../../rules/30-模块登记规则.md) | 模块登记规则定义分类和状态口径 |
|
||||
168
modules/frameworks/事件系统/使用指南.md
Normal file
168
modules/frameworks/事件系统/使用指南.md
Normal file
@@ -0,0 +1,168 @@
|
||||
# 事件系统使用指南
|
||||
|
||||
## 一、事件系统概述
|
||||
|
||||
事件系统分为两类:
|
||||
- **STCEvent**:Server To Client(服务器到客户端消息)
|
||||
- **CTCEvent**:Client To Client(客户端内部消息)
|
||||
|
||||
---
|
||||
|
||||
## 二、定义事件
|
||||
|
||||
### 2.1 定义事件枚举和参数
|
||||
|
||||
```csharp
|
||||
// 事件 ID 枚举
|
||||
public enum GameEventId
|
||||
{
|
||||
PlayerLevelUp,
|
||||
EnemyKilled,
|
||||
GoldChanged
|
||||
}
|
||||
|
||||
// 事件参数基类
|
||||
public class GameEventArgs : IHFEventArg<GameEventId>, IHFRecycle
|
||||
{
|
||||
public GameEventId eventId { get; set; }
|
||||
|
||||
public void Recycle()
|
||||
{
|
||||
// 重置状态
|
||||
eventId = default;
|
||||
}
|
||||
}
|
||||
|
||||
// 具体事件参数
|
||||
public class PlayerLevelUpEventArgs : GameEventArgs
|
||||
{
|
||||
public int playerId;
|
||||
public int oldLevel;
|
||||
public int newLevel;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、UI 窗口事件监听
|
||||
|
||||
### 3.1 在窗口中注册 CTCEvent
|
||||
|
||||
```csharp
|
||||
[HFAssetBundlePath(enHFLanguageRootFolder.ASSETS_GAME_GAMEHFRESOURCE, "UIWindows/CardDeck/CardDeckWindow.prefab")]
|
||||
[HFUIWindowLayer(enHFUICanvasLayer.UIWindow, enHFUIWindowLayer.Dynamic, isIgnoreCloseByESC = false)]
|
||||
public class CardDeckWindow : AbsHFUIWindow
|
||||
{
|
||||
protected override void OnOpen(Action _onOpenComplete)
|
||||
{
|
||||
base.OnOpen(_onOpenComplete);
|
||||
// 注册客户端内部消息监听(CTCEvent)
|
||||
HFFunCatalogue.Event.CTCEvent.AddListener<HFEvent_CTC_CardUpdate>(enHFCTCEvent.CardUpdate, OnCardUpdate);
|
||||
}
|
||||
|
||||
protected override void OnClose(Action _onCloseComplete)
|
||||
{
|
||||
base.OnClose(_onCloseComplete);
|
||||
// 取消客户端内部消息监听
|
||||
HFFunCatalogue.Event.CTCEvent.RemoveListener<HFEvent_CTC_CardUpdate>(enHFCTCEvent.CardUpdate, OnCardUpdate);
|
||||
}
|
||||
|
||||
void OnCardUpdate(HFEventArgBase arg)
|
||||
{
|
||||
HFEvent_CTC_CardUpdate cardArg = arg as HFEvent_CTC_CardUpdate;
|
||||
// 更新卡组 UI 显示
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **注意**:UI 窗口中**不监听服务器事件(STCEvent)**,服务器事件应在 Logic 层的 Manager 中监听。
|
||||
|
||||
---
|
||||
|
||||
## 四、Manager 事件监听
|
||||
|
||||
### 4.1 使用 AbsHFSingleMonoBehaviour(需要 Update)
|
||||
|
||||
```csharp
|
||||
public class PlayerManager : AbsHFSingleMonoBehaviour<PlayerManager>
|
||||
{
|
||||
protected override int[] simulateBehaviourEvents => new int[]
|
||||
{
|
||||
enHFSimulateBehaviourEvent.Mono_Update,
|
||||
enHFSimulateBehaviourEvent.Mono_FixedUpdate
|
||||
};
|
||||
|
||||
protected override void OnAfterCreateInstance()
|
||||
{
|
||||
base.OnAfterCreateInstance();
|
||||
RegisterServerEvents();
|
||||
}
|
||||
|
||||
void RegisterServerEvents()
|
||||
{
|
||||
HFFunCatalogue.Event.STCEvent.AddListener<HFEvent_STC_PlayerInfo>(enHFSTCEvent.PlayerInfo, OnPlayerInfo);
|
||||
}
|
||||
|
||||
void OnPlayerInfo(HFEventArgBase arg)
|
||||
{
|
||||
HFEvent_STC_PlayerInfo info = arg as HFEvent_STC_PlayerInfo;
|
||||
// 处理服务器返回的玩家信息
|
||||
// 触发客户端事件通知 UI 更新
|
||||
HFEvent_CTC_PlayerLevelUp evt = HFFunCatalogue.New<HFEvent_CTC_PlayerLevelUp>();
|
||||
evt.playerId = info.playerId;
|
||||
evt.newLevel = info.level;
|
||||
HFFunCatalogue.Event.CTCEvent.Dispatch(evt);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 使用 AbsHFSingleRunTime(纯逻辑)
|
||||
|
||||
```csharp
|
||||
public class ConfigManager : AbsHFSingleRunTime<ConfigManager>
|
||||
{
|
||||
protected override void OnAfterCreateInstance()
|
||||
{
|
||||
base.OnAfterCreateInstance();
|
||||
// 加载配置表
|
||||
LoadConfigTables();
|
||||
}
|
||||
|
||||
void LoadConfigTables()
|
||||
{
|
||||
// 加载配置数据
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、Manager 类型对比
|
||||
|
||||
| 类型 | 说明 | 适用场景 |
|
||||
|-----|------|---------|
|
||||
| `AbsHFSingleMonoBehaviour` | 生成 GameObject,可注册 MonoBehaviour 事件 | 需要 Update/FixedUpdate 等生命周期 |
|
||||
| `AbsHFSingleRunTime` | 纯内存单例,无 GameObject | 不需要生命周期事件 |
|
||||
|
||||
---
|
||||
|
||||
**版本**: v1.0
|
||||
**生效日期**: 2026年
|
||||
**适用范围**: GiftGameX 项目业务开发人员
|
||||
|
||||
---
|
||||
|
||||
## 文档关联
|
||||
|
||||
> 当本文档发生变更时,应同步检查以下关联文档,避免信息不一致。
|
||||
|
||||
| 文档 | 关联原因 |
|
||||
|---|---|
|
||||
| [事件系统入口](README.md) | 本文件是事件系统模块的子文档 |
|
||||
| [网络系统](../网络系统.md) | STC 事件来源与网络系统相关 |
|
||||
| [UI 窗口系统](../UI窗口系统/README.md) | UI 窗口订阅和释放事件 |
|
||||
| [实体系统](../实体系统/README.md) | Manager 常与实体系统协作 |
|
||||
| [modules/模块索引.md](../../模块索引.md) | 模块索引是项目知识总入口 |
|
||||
| [rules/10-架构说明.md](../../../rules/10-架构说明.md) | 架构说明定义总体分层和依赖方向 |
|
||||
| [rules/20-开发规则.md](../../../rules/20-开发规则.md) | 事件订阅/释放与开发规则互相引用 |
|
||||
| [rules/30-模块登记规则.md](../../../rules/30-模块登记规则.md) | 模块登记规则定义分类和状态口径 |
|
||||
110
modules/frameworks/内存对象管理/README.md
Normal file
110
modules/frameworks/内存对象管理/README.md
Normal file
@@ -0,0 +1,110 @@
|
||||
# 对象池与运行时对象管理
|
||||
|
||||
## 分类
|
||||
|
||||
- 核心框架
|
||||
|
||||
## 目的
|
||||
|
||||
提供类实例池、行为对象池、脚本对象池、实体池和 RT 展示对象的统一复用机制,减少 GC 和实例化开销。
|
||||
|
||||
---
|
||||
|
||||
## 核心路径
|
||||
|
||||
- 内存管理:`Assets/Game/GameHFScripte/CopyToHF/MemoryManager/`
|
||||
- 运行时对象:`Assets/Game/GameHFScripte/GameFunction/RTObjectManager/`
|
||||
- 调用池:`Assets/Game/GameHFScripte/GameFunction/Logic/CallPoolManager/`
|
||||
- 实体池:`Assets/Game/GameHFScripte/GameFunction/GameEntity/EntityPool/`
|
||||
|
||||
---
|
||||
|
||||
## 池类型
|
||||
|
||||
### HFClassEntityManager
|
||||
|
||||
类实例池,按类型 hash 维护 `Stack<object>`。
|
||||
|
||||
```text
|
||||
GetClassEntity<T>()
|
||||
-> 优先从 Stack 弹出复用
|
||||
-> 否则 Activator.CreateInstance<T>()
|
||||
```
|
||||
|
||||
### HFSimulateBehaviourPool<T>
|
||||
|
||||
行为对象池,用于继承 `AbsHFSimulateBehaviour` 的对象。
|
||||
|
||||
- 维护未使用队列和使用中列表。
|
||||
- `GiveBack()` 隐藏 GameObject 并调用 `Recycle()`。
|
||||
|
||||
### HFScriptMemPool<T>
|
||||
|
||||
脚本对象池,用于 `IHFRecycle` 的普通对象或 MonoBehaviour。
|
||||
|
||||
- 若类型继承 MonoBehaviour,自动创建 GameObject 挂载组件。
|
||||
- 若类型为普通类,直接 `Activator.CreateInstance`。
|
||||
|
||||
### 实体池
|
||||
|
||||
`HFEntityPool` + 各类 `AbsHFEntityPool` 实现,见 [实体系统](../实体系统/README.md)。
|
||||
|
||||
---
|
||||
|
||||
## 运行时展示对象(RT)
|
||||
|
||||
RT 对象用于在独立相机上展示模型、特效等:
|
||||
|
||||
- `RTObject`:运行时展示对象基类
|
||||
- `RTGameObjectRes`:从资源 id 创建展示对象
|
||||
- `RTEntity`:实体展示对象
|
||||
- `RTCameraItem`:RT 相机项
|
||||
- `RTUIEffectCameraItem`:UI 特效相机项
|
||||
|
||||
创建流程:
|
||||
|
||||
```text
|
||||
RTGameObjectRes
|
||||
-> 从资源 id 创建 GameObject
|
||||
-> 资源加载走 HFABMgr.Instance.LoadAsset
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 回收接口
|
||||
|
||||
| 接口 | 职责 |
|
||||
|---|---|
|
||||
| `IHFRecycle` | 基础回收接口,声明 `Recycle()` |
|
||||
| `IHFRecycleEventHandler` | 回收事件处理 |
|
||||
| `OnAfterRecycleHandler` | 回收到类实例池 |
|
||||
|
||||
---
|
||||
|
||||
## 使用规则
|
||||
|
||||
- 频繁创建/销毁的对象优先使用对象池。
|
||||
- 归还对象时确保来源正确,避免泄漏。
|
||||
- 对象池内对象在归还后应重置状态。
|
||||
- RT 对象注意及时释放相机 seat 和资源引用。
|
||||
|
||||
---
|
||||
|
||||
## 相关模块
|
||||
|
||||
- [实体系统](../实体系统/README.md)
|
||||
- [资源管理](../资源管理/README.md)
|
||||
|
||||
---
|
||||
|
||||
## 文档关联
|
||||
|
||||
> 当本文档发生变更时,应同步检查以下关联文档,避免信息不一致。
|
||||
|
||||
| 文档 | 关联原因 |
|
||||
|---|---|
|
||||
| [modules/模块索引.md](../../模块索引.md) | 模块索引是项目知识总入口 |
|
||||
| [modules/frameworks/实体系统/README.md](../实体系统/README.md) | 实体池是对象池的一种特化 |
|
||||
| [modules/frameworks/资源管理/README.md](../资源管理/README.md) | RT 对象资源加载依赖资源管理 |
|
||||
| [rules/10-架构说明.md](../../../rules/10-架构说明.md) | 架构说明定义总体分层和依赖方向 |
|
||||
| [rules/30-模块登记规则.md](../../../rules/30-模块登记规则.md) | 模块登记规则定义分类和状态口径 |
|
||||
116
modules/frameworks/内存对象管理/使用指南.md
Normal file
116
modules/frameworks/内存对象管理/使用指南.md
Normal file
@@ -0,0 +1,116 @@
|
||||
# 对象池使用指南
|
||||
|
||||
## 一、GameObject 对象池
|
||||
|
||||
### 1.1 基本用法
|
||||
|
||||
```csharp
|
||||
public class BulletManager : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private GameObject bulletPrefab;
|
||||
private HFObjectMemPool m_BulletPool;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
// 创建对象池
|
||||
m_BulletPool = new HFObjectMemPool(bulletPrefab, transform);
|
||||
}
|
||||
|
||||
public void Fire(Vector3 position, Quaternion rotation)
|
||||
{
|
||||
bool isNew;
|
||||
GameObject bullet = m_BulletPool.GetItem(out isNew);
|
||||
|
||||
bullet.transform.position = position;
|
||||
bullet.transform.rotation = rotation;
|
||||
|
||||
// 如果是新创建的对象,初始化组件
|
||||
if (isNew)
|
||||
{
|
||||
bullet.GetComponent<BulletComponent>().Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
public void RecycleBullet(GameObject bullet)
|
||||
{
|
||||
m_BulletPool.GiveBack(bullet);
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
// 清理对象池
|
||||
m_BulletPool.Recycle();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、脚本对象池
|
||||
|
||||
### 2.1 基本用法
|
||||
|
||||
```csharp
|
||||
public class EventArgPool : MonoBehaviour
|
||||
{
|
||||
private HFScriptMemPool<GameEventArgs> m_ArgPool;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
m_ArgPool = new HFScriptMemPool<GameEventArgs>();
|
||||
}
|
||||
|
||||
public GameEventArgs GetEventArgs(GameEventId eventId)
|
||||
{
|
||||
bool isNew;
|
||||
GameEventArgs arg = m_ArgPool.GetItem(out isNew);
|
||||
arg.eventId = eventId;
|
||||
return arg;
|
||||
}
|
||||
|
||||
public void RecycleEventArgs(GameEventArgs arg)
|
||||
{
|
||||
m_ArgPool.GiveBack(arg);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、使用注意事项
|
||||
|
||||
### 3.1 对象池设计特点
|
||||
|
||||
| 特性 | 说明 |
|
||||
|-----|------|
|
||||
| **双池结构** | 空闲池 + 使用池,高效管理对象状态 |
|
||||
| **内存泄漏检测** | 归还时校验来源,及时发现问题 |
|
||||
| **泛型支持** | `HFScriptMemPool<T>` 提供类型安全 |
|
||||
| **Mono 支持** | 自动创建 GameObject,无缝集成 Unity |
|
||||
|
||||
### 3.2 性能优化建议
|
||||
|
||||
- 频繁创建销毁的对象必须使用对象池
|
||||
- 合理设置对象池初始容量
|
||||
- 定期清理空闲对象避免内存泄漏
|
||||
|
||||
---
|
||||
|
||||
**版本**: v1.0
|
||||
**生效日期**: 2026年
|
||||
**适用范围**: GiftGameX 项目业务开发人员
|
||||
|
||||
---
|
||||
|
||||
## 文档关联
|
||||
|
||||
> 当本文档发生变更时,应同步检查以下关联文档,避免信息不一致。
|
||||
|
||||
| 文档 | 关联原因 |
|
||||
|---|---|
|
||||
| [对象池与运行时对象管理入口](README.md) | 本文件是内存对象管理模块的子文档 |
|
||||
| [实体系统](../实体系统/README.md) | 实体池是对象池的一种特化 |
|
||||
| [资源管理](../资源管理/README.md) | RT 对象资源加载依赖资源管理 |
|
||||
| [modules/模块索引.md](../../模块索引.md) | 模块索引是项目知识总入口 |
|
||||
| [rules/10-架构说明.md](../../../rules/10-架构说明.md) | 架构说明定义总体分层和依赖方向 |
|
||||
| [rules/30-模块登记规则.md](../../../rules/30-模块登记规则.md) | 模块登记规则定义分类和状态口径 |
|
||||
98
modules/frameworks/场景系统.md
Normal file
98
modules/frameworks/场景系统.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# 场景系统
|
||||
|
||||
## 分类
|
||||
|
||||
- 核心框架
|
||||
|
||||
## 目的
|
||||
|
||||
管理 Unity 场景的加载、资源配置读取和场景生命周期回调。
|
||||
|
||||
---
|
||||
|
||||
## 核心路径
|
||||
|
||||
- 场景管理器:`Assets/Game/GameHFScripte/GameFunction/SceneManager/`
|
||||
- Unity 场景:`Assets/Game/GameMono/Scenes/`
|
||||
|
||||
---
|
||||
|
||||
## 核心类
|
||||
|
||||
| 类 | 职责 |
|
||||
|---|---|
|
||||
| `HFSceneMgr` | 场景管理器单例,继承 `AbsHFSingleMonoBehaviour<HFSceneMgr>` |
|
||||
| `enHFSceneName` | 场景枚举,例如 `Game = 80001` |
|
||||
|
||||
---
|
||||
|
||||
## 加载流程
|
||||
|
||||
```text
|
||||
LoadScene(enHFSceneName scene, Action complete)
|
||||
-> 转为资源/关卡 id
|
||||
-> LoadScene(int levelId, Action complete)
|
||||
-> HFFunCatalogue.NDB.ds_Resource840C5F09TableData.ReadTableByPK(levelId)
|
||||
-> 读取资源配置
|
||||
```
|
||||
|
||||
### Editor 模式
|
||||
|
||||
```text
|
||||
从配置 Path 取文件名
|
||||
-> SceneManager.LoadScene(scName)
|
||||
```
|
||||
|
||||
### 非 Editor 模式
|
||||
|
||||
```text
|
||||
HFFunCatalogue.Asset.LoadAsset(levelId, ...)
|
||||
-> 用结果中的 res.sceneName 调用 SceneManager.LoadScene
|
||||
```
|
||||
|
||||
`OnLoadScene()` 同步调用 `SceneManager.LoadScene()` 后立即执行完成回调。
|
||||
|
||||
---
|
||||
|
||||
## 生命周期
|
||||
|
||||
`ZYCore` 中存在 `IZYH_ZYCore_Scene` 和 `AbsZYH_ZYCore_Scene` 接口:
|
||||
|
||||
- 进入加载场景
|
||||
- 加载完成
|
||||
- 进入/退出场景前后
|
||||
- 切场景释放
|
||||
|
||||
`ZYUGUI` 窗口管理器提供:
|
||||
|
||||
- `isAutoCloseWhenToggleScene`:切场景时自动关闭
|
||||
- `isAutoDestoryWhenToggleScene`:切场景时自动销毁
|
||||
|
||||
---
|
||||
|
||||
## 使用规则
|
||||
|
||||
- 场景切换统一走 `HFFunCatalogue.Scene.LoadScene()`。
|
||||
- 不要在场景里堆放运行时动态加载的资源。
|
||||
- 切场景时注意 UI 窗口的自动关闭/销毁配置。
|
||||
|
||||
---
|
||||
|
||||
## 相关模块
|
||||
|
||||
- [资源管理](资源管理/README.md)
|
||||
- [UI 窗口系统](UI窗口系统/README.md)
|
||||
|
||||
---
|
||||
|
||||
## 文档关联
|
||||
|
||||
> 当本文档发生变更时,应同步检查以下关联文档,避免信息不一致。
|
||||
|
||||
| 文档 | 关联原因 |
|
||||
|---|---|
|
||||
| [modules/模块索引.md](../模块索引.md) | 模块索引是项目知识总入口 |
|
||||
| [modules/frameworks/资源管理/README.md](资源管理/README.md) | 场景资源加载依赖资源管理 |
|
||||
| [modules/frameworks/UI窗口系统/README.md](UI窗口系统/README.md) | 切场景影响 UI 窗口生命周期 |
|
||||
| [rules/10-架构说明.md](../../rules/10-架构说明.md) | 架构说明定义总体分层和依赖方向 |
|
||||
| [rules/30-模块登记规则.md](../../rules/30-模块登记规则.md) | 模块登记规则定义分类和状态口径 |
|
||||
138
modules/frameworks/实体系统/README.md
Normal file
138
modules/frameworks/实体系统/README.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# 实体系统
|
||||
|
||||
## 分类
|
||||
|
||||
- 核心框架
|
||||
|
||||
## 目的
|
||||
|
||||
管理游戏实体的创建、生命周期、System-Model 架构、实体池和子系统协作。
|
||||
|
||||
---
|
||||
|
||||
## 核心路径
|
||||
|
||||
- 实体框架:`Assets/Game/GameHFScripte/GameFunction/GameEntity/`
|
||||
- 实体数据:`Assets/Game/GameHFScripte/GameFunction/Logic/EntityData/`
|
||||
- 子目录:`Core/`、`Entity/`、`EntityPool/`、`RVO/`、`StateMachineBehaviour/`
|
||||
|
||||
---
|
||||
|
||||
## 核心类
|
||||
|
||||
| 类 | 职责 |
|
||||
|---|---|
|
||||
| `HFAbsEntity` | 实体基类,继承 `AbsHFSimulateBehaviour` |
|
||||
| `HFEntity_Basic` | 基础实体标签 |
|
||||
| `HFEntity_Enemy` | 敌人类型标签 |
|
||||
| `HFEntity_Human` | 人类/玩家类型标签 |
|
||||
| `HFEntityPool` | 实体池入口 |
|
||||
| `HFBasicPool` / `HFEnemyPool` / `HFHumanityPool` | 按 `enEntityLayer` 分类的实体池实现 |
|
||||
| `AbsHFEntityPool` | 实体池抽象基类 |
|
||||
| `stEntityInfo` | 实体数据结构 |
|
||||
|
||||
---
|
||||
|
||||
## System-Model 架构
|
||||
|
||||
`HFAbsEntity` 内部维护两个集合:
|
||||
|
||||
```text
|
||||
m_Models : IModel[] // 数据层
|
||||
m_Systems : ISystem[] // 表现层/逻辑层
|
||||
```
|
||||
|
||||
按 `enSystemType` 关联 `IModel` 和 `ISystem`。
|
||||
|
||||
添加系统:
|
||||
|
||||
```csharp
|
||||
AddSystem<T>()
|
||||
-> HFClassEntityManager.Instance.GetClassEntity<T>() // 创建或复用系统对象
|
||||
-> 生成对应 IModel
|
||||
-> model.Init(this, system.Init) // 建立实体、模型、系统关系
|
||||
```
|
||||
|
||||
更新顺序:
|
||||
|
||||
```text
|
||||
OnSystemUpdate() // 表现层更新
|
||||
OnModelUpdate() // 数据层更新
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 实体池机制
|
||||
|
||||
`HFEntityPool` 按 `enEntityLayer` 注册不同的 `AbsHFEntityPool` 实现。
|
||||
|
||||
创建实体:
|
||||
|
||||
```text
|
||||
AbsHFEntityPool.OnCreateEnity<T>()
|
||||
-> 创建 GameObject 根节点
|
||||
-> 维护 onlyid -> entity 索引
|
||||
-> 维护 EntityId -> onlyid 索引
|
||||
```
|
||||
|
||||
查询实体:
|
||||
|
||||
```text
|
||||
HFEntityPool.SelectByOnlyid(onlyid)
|
||||
-> 通过 onlyid -> enEntityLayer 找到对应池
|
||||
-> 从具体池取实体
|
||||
```
|
||||
|
||||
回收实体:
|
||||
|
||||
```text
|
||||
DelEntityByOnlyid(onlyid)
|
||||
-> 从池索引移除
|
||||
-> 调用 entity.Recycle()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 子系统
|
||||
|
||||
实体相关的子系统通常位于:
|
||||
|
||||
- `Models/AI`、`Systems/AI` — AI
|
||||
- `Models/Animation`、`Systems/Animation` — 动画
|
||||
- `Models/Feature`、`Systems/Feature` — 特性/属性
|
||||
- `Models/Move`、`Systems/Move` — 移动
|
||||
- `Models/Skill`、`Systems/Skill` — 技能
|
||||
- `HFSkillTrigger_*` — 技能触发器
|
||||
- `HFSkill_*_Releaser` — 技能释放器
|
||||
- `GameEntity/RVO/RVOManager.cs` — RVO 避障
|
||||
|
||||
---
|
||||
|
||||
## 使用规则
|
||||
|
||||
- 新实体类型应继承 `HFAbsEntity`。
|
||||
- 实体对象统一通过 `HFEntityPool` 创建和回收。
|
||||
- 系统通过 `AddSystem<T>()` 添加,避免直接 new。
|
||||
- 子系统数据更新放在 `OnModelUpdate()`,表现更新放在 `OnSystemUpdate()`。
|
||||
|
||||
---
|
||||
|
||||
## 相关模块
|
||||
|
||||
- [技能系统](../技能系统/README.md)
|
||||
- [对象池](../内存对象管理/README.md)
|
||||
- [数据/配置框架](../数据配置框架.md)
|
||||
|
||||
---
|
||||
|
||||
## 文档关联
|
||||
|
||||
> 当本文档发生变更时,应同步检查以下关联文档,避免信息不一致。
|
||||
|
||||
| 文档 | 关联原因 |
|
||||
|---|---|
|
||||
| [modules/模块索引.md](../../模块索引.md) | 模块索引是项目知识总入口 |
|
||||
| [modules/frameworks/技能系统/README.md](../技能系统/README.md) | 技能系统是实体系统的子系统 |
|
||||
| [modules/frameworks/内存对象管理/README.md](../内存对象管理/README.md) | 实体池与对象池机制相关 |
|
||||
| [rules/10-架构说明.md](../../../rules/10-架构说明.md) | 架构说明定义总体分层和依赖方向 |
|
||||
| [rules/30-模块登记规则.md](../../../rules/30-模块登记规则.md) | 模块登记规则定义分类和状态口径 |
|
||||
339
modules/frameworks/实体系统/开发指南.md
Normal file
339
modules/frameworks/实体系统/开发指南.md
Normal file
@@ -0,0 +1,339 @@
|
||||
# Entity 开发指南
|
||||
|
||||
## 一、Entity 概述
|
||||
|
||||
Entity 是游戏世界中所有游戏对象的抽象基类,包括玩家、敌人、塔楼、城堡等。Entity 系统提供统一的对象管理、生命周期控制和 **System-Model 架构**。
|
||||
|
||||
### 1.1 Entity 分类
|
||||
|
||||
Entity 分类根据具体业务需求定义,以下为示例分类(项目实际分类以业务为准):
|
||||
|
||||
| 类别 | 说明 | 示例 |
|
||||
|-----|------|------|
|
||||
| **Human** | 具有生命、移动能力的实体 | Player、Enemy |
|
||||
| **Tower** | 防御塔实体 | 箭塔、炮塔 |
|
||||
| **Castle** | 城堡/基地实体 | 玩家基地 |
|
||||
| **Obstacle** | 障碍物实体 | 墙壁、陷阱 |
|
||||
|
||||
> **说明**:实际项目中需根据业务需求定义 Entity 类型,继承 `HFAbsEntity` 基类并实现对应逻辑。
|
||||
|
||||
### 1.2 核心数据结构
|
||||
|
||||
```csharp
|
||||
// Entity 唯一标识
|
||||
public long onlyid { get; } // 运行时唯一ID
|
||||
public int EntityId { get; } // 资源配置ID
|
||||
public enEntityLayer entityType { get; } // Entity 类型
|
||||
public enEntityCamp entityCamp { get; } // 阵营
|
||||
public bool isDeath { get; } // 是否死亡
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、Entity 创建规范
|
||||
|
||||
### 2.1 继承结构
|
||||
|
||||
```csharp
|
||||
// 核心基类
|
||||
public abstract partial class HFAbsEntity : AbsHFSimulateBehaviour
|
||||
{
|
||||
public long onlyid { get; private set; }
|
||||
public enEntityLayer entityType { get; private set; }
|
||||
public enEntityCamp entityCamp { get; private set; }
|
||||
public int EntityId { get; private set; }
|
||||
public bool isDeath { get; private set; } // 逻辑死亡标记
|
||||
|
||||
// 初始化方法
|
||||
public void Init() { ... }
|
||||
public virtual void OnInit() { ... }
|
||||
|
||||
// 死亡方法(表现层)
|
||||
public void Death() { ... } // 触发死亡事件
|
||||
public virtual void OnDeath() { ... } // 子类重写处理表现死亡逻辑
|
||||
|
||||
// 逻辑死亡(数据层)
|
||||
protected void Logic_Death() { ... } // 设置 isDeath=true,数据层死亡
|
||||
protected virtual void OnLogicDeath() { ... } // 子类重写处理逻辑死亡逻辑
|
||||
}
|
||||
|
||||
// 具体 Entity 示例 - Human 类型
|
||||
public class HFEntity_Human : HFAbsEntity
|
||||
{
|
||||
protected override void OnRunAwake()
|
||||
{
|
||||
base.OnRunAwake();
|
||||
AddSystem<HFFeatureSystem>();
|
||||
AddSystem<HFAnimationSystem_Human>();
|
||||
AddSystem<HFApathMoveSystem>();
|
||||
AddSystem<HFSkillSystem>();
|
||||
}
|
||||
|
||||
public override void OnInit()
|
||||
{
|
||||
// 初始化配置数据
|
||||
}
|
||||
|
||||
public override void OnDeath()
|
||||
{
|
||||
// 死亡处理逻辑
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 命名规范
|
||||
|
||||
| 类型 | 命名规则 | 示例 |
|
||||
|-----|---------|------|
|
||||
| Entity 类 | `HFEntity_{TypeName}` | `HFEntity_Human`、`HFEntity_Tower` |
|
||||
| 系统类 | `HF{SystemName}System[_TypeName]` | `HFAnimationSystem_Human` |
|
||||
| 模型类 | `HF{ModelName}Model[_TypeName]` | `HFAnimationModel_Human` |
|
||||
| 数据结构 | `st{DataName}` | `stAbilityData`、`stDamageInfo` |
|
||||
|
||||
---
|
||||
|
||||
## 三、Entity 生命周期
|
||||
|
||||
### 3.1 生命周期方法
|
||||
|
||||
| 方法 | 调用时机 | 职责 | 层级 |
|
||||
|-----|---------|------|-----|
|
||||
| `OnRunAwake()` | 对象创建时 | 添加 System 组件 | 表现层 |
|
||||
| `Init()` | 初始化时调用 | 重置死亡状态、调用 OnInit | - |
|
||||
| `OnInit()` | Init 内部调用 | 子类重写,初始化配置数据 | 数据层 |
|
||||
| `Logic_Death()` | HP 归零时调用 | 设置 `isDeath=true`,调用 OnLogicDeath | 数据层 |
|
||||
| `OnLogicDeath()` | Logic_Death 内部调用 | 子类重写处理逻辑死亡逻辑 | 数据层 |
|
||||
| `Death()` | 逻辑死亡后调用 | 触发死亡事件、调用 OnDeath | 表现层 |
|
||||
| `OnDeath()` | Death 内部调用 | 子类重写,处理表现死亡逻辑(动画、特效等) | 表现层 |
|
||||
| `OnRecycle()` | 对象回收时 | 清理资源、释放引用 | - |
|
||||
|
||||
> **重要区分**:
|
||||
> - **逻辑死亡**:`Logic_Death()` 由伤害计算触发,设置 `isDeath=true`,仅影响数据层
|
||||
> - **表现死亡**:`Death()` 在逻辑死亡后调用,触发死亡事件和表现效果(动画、音效等)
|
||||
|
||||
### 3.2 创建流程
|
||||
|
||||
```csharp
|
||||
// 1. 对象池创建 Entity
|
||||
HFEntityPool.Instance.CreateEntity<HFEntity_Human>(onlyid, entityId,
|
||||
() => HFClassEntityManager.Instance.GetClassEntity<HFEntity_Human>(),
|
||||
(entity) => {
|
||||
// 设置阵营
|
||||
entity.SetEntityCamp(enEntityCamp.Player);
|
||||
// 初始化
|
||||
entity.Init();
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、System-Model 架构
|
||||
|
||||
### 4.1 架构设计
|
||||
|
||||
Entity 采用 **System-Model 分离架构**:
|
||||
|
||||
- **System 层**:处理表现逻辑(Update、状态转换)
|
||||
- **Model 层**:处理数据逻辑(数据存储、计算)
|
||||
|
||||
### 4.2 添加系统
|
||||
|
||||
```csharp
|
||||
public class HFEntity_Human : HFAbsEntity
|
||||
{
|
||||
protected override void OnRunAwake()
|
||||
{
|
||||
base.OnRunAwake();
|
||||
|
||||
// 添加系统(按执行顺序)
|
||||
AddSystem<HFFeatureSystem>(); // 属性系统
|
||||
AddSystem<HFAnimationSystem_Human>(); // 动画系统
|
||||
AddSystem<HFApathMoveSystem>(); // 寻路移动系统
|
||||
AddSystem<HFSkillSystem>(); // 技能系统
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 常用系统
|
||||
|
||||
| 系统 | 职责 | 适用 Entity |
|
||||
|-----|------|-----------|
|
||||
| `HFFeatureSystem` | 属性管理 | 所有 Entity |
|
||||
| `HFAnimationSystem` | 动画控制 | Human、Tower |
|
||||
| `HFMoveSystem` | 移动控制 | Human |
|
||||
| `HFAISystem` | AI 行为 | Enemy、Tower |
|
||||
| `HFSkillSystem` | 技能系统 | Human、Tower |
|
||||
|
||||
### 4.4 获取 Model
|
||||
|
||||
```csharp
|
||||
// 通过 System 类型获取 Model
|
||||
HFAnimationModel_Human animModel = GetModel<HFAnimationModel_Human>();
|
||||
|
||||
// 通过 System 对象获取 Model
|
||||
HFSkillSystem skillSystem = ...;
|
||||
HFSkillModel skillModel = GetModel<HFSkillModel>(skillSystem);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、属性系统
|
||||
|
||||
### 5.1 属性分类
|
||||
|
||||
| 属性类型 | 说明 | 示例 |
|
||||
|---------|------|------|
|
||||
| **基础属性** | 配置表读取,基础值 | HP、SPD、MoveSpeed |
|
||||
| **战斗属性** | 实际战斗中使用,受 Buff 影响 | Fight_SPD、Fight_ATK |
|
||||
|
||||
### 5.2 属性数据结构
|
||||
|
||||
```csharp
|
||||
public partial class HFAbsEntity
|
||||
{
|
||||
// 基础属性(配置表)
|
||||
private float m_Hp;
|
||||
private float m_MaxHp;
|
||||
private float m_SPD;
|
||||
private float m_MoveSpeed;
|
||||
private float m_NormalATK;
|
||||
private float m_Critical;
|
||||
private float m_CriticalDamage;
|
||||
|
||||
// 战斗属性(实时计算)
|
||||
private float m_Fight_SPD;
|
||||
private float m_Fight_NormalATK;
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 属性变更
|
||||
|
||||
```csharp
|
||||
// 临时属性变更(Buff)
|
||||
public void ChangeData(stAbilityData data)
|
||||
{
|
||||
if (data.IsPermanent)
|
||||
{
|
||||
// 永久属性变更
|
||||
switch (data.ChangeTarget)
|
||||
{
|
||||
case enEntitityAbility.SPD:
|
||||
m_SPD += data.Value;
|
||||
break;
|
||||
case enEntitityAbility.NormalATK:
|
||||
m_NormalATK += data.Value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 临时属性变更(存入 Buff 字典)
|
||||
OnAddEntityAbilityData(data);
|
||||
}
|
||||
|
||||
// 刷新战斗属性
|
||||
OnRefushEntityAbility(data.ChangeTarget);
|
||||
// 通知所有 Model 属性变更
|
||||
ChangeAbility(data.ChangeTarget);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、Entity 对象池
|
||||
|
||||
### 6.1 对象池职责
|
||||
|
||||
```csharp
|
||||
public abstract partial class AbsHFEntityPool : IHFObject
|
||||
{
|
||||
public abstract enEntityLayer entityType { get; }
|
||||
|
||||
// 根据 onlyid 获取 Entity
|
||||
public virtual HFAbsEntity SelectByOnlyid(long onlyid) { ... }
|
||||
|
||||
// 根据资源 ID 获取所有 Entity
|
||||
public List<HFAbsEntity> SelectByResId(int resid) { ... }
|
||||
|
||||
// 删除 Entity
|
||||
public virtual bool DelEntityByOnlyid(long onlyid) { ... }
|
||||
|
||||
// 获取所有 Entity
|
||||
public List<HFAbsEntity> GetAllEntities() { ... }
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 使用示例
|
||||
|
||||
```csharp
|
||||
// 创建 Entity
|
||||
HFEntityPool.Instance.CreateEntity<HFEntity_Human>(
|
||||
onlyid: 1001,
|
||||
entityId: 10001,
|
||||
func: () => HFClassEntityManager.Instance.GetClassEntity<HFEntity_Human>(),
|
||||
action: (entity) => {
|
||||
entity.SetEntityCamp(enEntityCamp.Player);
|
||||
entity.Init();
|
||||
});
|
||||
|
||||
// 获取 Entity
|
||||
HFAbsEntity entity = HFEntityPool.Instance.SelectByOnlyid(1001);
|
||||
HFEntity_Human human = entity as HFEntity_Human;
|
||||
|
||||
// 删除 Entity
|
||||
HFEntityPool.Instance.DelEntityByOnlyid(1001);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 七、开发规范
|
||||
|
||||
### 7.1 文件路径
|
||||
|
||||
| 类型 | 路径 |
|
||||
|-----|------|
|
||||
| Entity 基类 | `Assets/Game/GameHFScripte/GameFunction/GameEntity/Core/BaseEntity/AbsEntity/` |
|
||||
| 具体 Entity | `Assets/Game/GameHFScripte/GameFunction/GameEntity/Entity/EntityTag/` |
|
||||
| 系统类 | `Assets/Game/GameHFScripte/GameFunction/GameEntity/Entity/System/` |
|
||||
| 模型类 | `Assets/Game/GameHFScripte/GameFunction/GameEntity/Entity/Model/` |
|
||||
| 对象池 | `Assets/Game/GameHFScripte/GameFunction/GameEntity/Core/BaseEntity/EntityPool/` |
|
||||
|
||||
### 7.2 编码规范
|
||||
|
||||
1. **必须继承** `HFAbsEntity` 基类
|
||||
2. **必须在** `OnRunAwake()` 中添加所需 System
|
||||
3. **必须重写** `OnInit()` 初始化配置数据
|
||||
4. **必须重写** `OnDeath()` 处理死亡逻辑
|
||||
5. **禁止** 在 Entity 中直接访问 Manager,通过 `HFFunCatalogue` 访问
|
||||
6. **禁止** 在 Entity 中持有场景引用
|
||||
|
||||
### 7.3 性能优化
|
||||
|
||||
- 使用对象池复用 Entity 实例
|
||||
- 使用 `AddSystem<T>()` 延迟初始化系统
|
||||
- 属性变更通过 `ChangeAbility()` 通知所有 Model
|
||||
- 死亡 Entity 通过 `OnDeathFinish()` 回调回收
|
||||
|
||||
---
|
||||
|
||||
**版本**: v1.1
|
||||
**生效日期**: 2026年
|
||||
**适用范围**: StrayFog 框架开发人员
|
||||
|
||||
---
|
||||
|
||||
## 文档关联
|
||||
|
||||
> 当本文档发生变更时,应同步检查以下关联文档,避免信息不一致。
|
||||
|
||||
| 文档 | 关联原因 |
|
||||
|---|---|
|
||||
| [实体系统入口](README.md) | 本文件是实体系统模块的子文档 |
|
||||
| [通用技能系统使用指南](../技能系统/使用指南.md) | 技能系统是 Entity 常用子系统 |
|
||||
| [对象池与运行时对象管理](../内存对象管理/README.md) | Entity 池机制相关 |
|
||||
| [事件系统](../事件系统/README.md) | Entity 间通信常依赖事件系统 |
|
||||
| [modules/模块索引.md](../../模块索引.md) | 模块索引是项目知识总入口 |
|
||||
| [rules/10-架构说明.md](../../../rules/10-架构说明.md) | 架构说明定义总体分层和依赖方向 |
|
||||
| [rules/20-开发规则.md](../../../rules/20-开发规则.md) | 编码规范与开发规则互相引用 |
|
||||
| [rules/30-模块登记规则.md](../../../rules/30-模块登记规则.md) | 模块登记规则定义分类和状态口径 |
|
||||
89
modules/frameworks/实体系统/注意事项.md
Normal file
89
modules/frameworks/实体系统/注意事项.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# Unity Entities 注意事项和最佳实践
|
||||
|
||||
## 1. 枚举定义相关
|
||||
|
||||
### enHFCTCEvent 枚举
|
||||
- **新增项**:`Custom = 281` - 用于自定义事件
|
||||
- **用途**:在 `HFEvent_OnDamage` 等自定义事件中使用
|
||||
- **注意**:添加新事件时,确保数值不与其他事件冲突
|
||||
|
||||
### enEntityLayer 枚举
|
||||
- **现有项**:`None=0`, `Role`, `Monster`, `Max`
|
||||
- **注意**:没有 `Enemy` 项,敌人应归类为 `Monster`
|
||||
|
||||
## 2. 实体-系统-模型 (ESM) 架构相关
|
||||
|
||||
### HFAbsEntity 访问方式
|
||||
- **Transform 访问**:使用 `entity.gameObject.transform.position` 而不是 `entity.transform.position`
|
||||
- **属性访问**:使用公共属性如 `entity.NormalATK` 而不是私有字段 `entity.m_NormalATK`
|
||||
- **系统获取**:`HFAbsEntity` 没有 `GetSystem<T>()` 方法,只有 `GetModel<T>()` 方法
|
||||
- **模型获取**:通过 `entity.GetModel<T>()` 获取模型实例
|
||||
|
||||
### 系统间通信
|
||||
- **推荐方式**:通过事件系统进行通信,而不是直接获取其他系统
|
||||
- **事件分发**:使用 `event.Dispatch()` 而不是 `HFFunCatalogue.Event.TCEvent.SendEvent()`
|
||||
|
||||
## 3. 数据表字段映射
|
||||
|
||||
### Role 表字段
|
||||
- `Lifefold` -> 生命值
|
||||
- `Attackfold` -> 攻击力
|
||||
- `Defensefold` -> 防御力
|
||||
- `AttackRange` -> 攻击范围
|
||||
- `CriticalRate` -> 暴击率
|
||||
- `CriticalDamage` -> 暴击伤害
|
||||
- **注意**:Role 表中没有速度字段 (SPD)
|
||||
|
||||
## 4. 动画系统相关
|
||||
|
||||
### FSM (有限状态机) 系统
|
||||
- **HFFMSModel** 现在包含 `CurrentSkeletonAnimation` 属性
|
||||
- **访问方式**:通过 `FSM.CurrentSkeletonAnimation` 访问 SkeletonAnimation
|
||||
- **动画速度设置**:使用 `skeletonAnimation.timeScale = speed`
|
||||
|
||||
## 5. AI 系统相关
|
||||
|
||||
### HFAIModel_Enemy
|
||||
- **伤害处理**:通过事件系统 (`HFEvent_OnDamage`) 而不是直接获取系统
|
||||
- **攻击逻辑**:创建并分发伤害事件,让其他系统处理实际伤害计算
|
||||
|
||||
## 6. 错误处理和调试
|
||||
|
||||
### 常见错误类型
|
||||
- **CS0117**: 枚举中不包含指定定义 - 检查枚举项是否存在
|
||||
- **CS1061**: 类型不包含指定定义 - 检查属性/方法名是否正确
|
||||
- **CS0122**: 成员因保护级别不可访问 - 使用公共 API 而不是私有字段
|
||||
|
||||
### 修复策略
|
||||
- 优先使用公共属性而不是私有字段
|
||||
- 通过事件系统进行组件间通信
|
||||
- 正确访问 GameObject 的 Transform 组件
|
||||
- 使用正确的枚举值
|
||||
|
||||
## 7. 架构模式
|
||||
|
||||
### 系统-模型 (System-Model) 模式
|
||||
- **AbsHFMoveSystem<T>** 需要指定泛型参数
|
||||
- **AbsHFAIModel** 不能直接获取系统,应通过事件或模型进行通信
|
||||
- **AbsHFAnimationModel** 需要配合 `AbsHFFMSModel` 使用
|
||||
|
||||
### 事件驱动架构
|
||||
- 自定义事件需继承 `AbsHFEventArg<enHFCTCEvent>`
|
||||
- 事件 ID 使用 `enHFCTCEvent.Custom` 或其他预定义事件
|
||||
- 事件分发使用 `Dispatch()` 方法
|
||||
|
||||
---
|
||||
|
||||
## 文档关联
|
||||
|
||||
> 当本文档发生变更时,应同步检查以下关联文档,避免信息不一致。
|
||||
|
||||
| 文档 | 关联原因 |
|
||||
|---|---|
|
||||
| [实体系统入口](README.md) | 本文件是实体系统模块的子文档 |
|
||||
| [Entity 开发指南](开发指南.md) | 与开发指南中的 System-Model、属性系统互补 |
|
||||
| [事件系统](../事件系统/README.md) | 自定义事件派发相关 |
|
||||
| [modules/模块索引.md](../../模块索引.md) | 模块索引是项目知识总入口 |
|
||||
| [rules/10-架构说明.md](../../../rules/10-架构说明.md) | 架构说明定义总体分层和依赖方向 |
|
||||
| [rules/20-开发规则.md](../../../rules/20-开发规则.md) | 公共 API 访问方式与开发规则互相引用 |
|
||||
| [rules/30-模块登记规则.md](../../../rules/30-模块登记规则.md) | 模块登记规则定义分类和状态口径 |
|
||||
134
modules/frameworks/技能系统/README.md
Normal file
134
modules/frameworks/技能系统/README.md
Normal file
@@ -0,0 +1,134 @@
|
||||
# 技能系统
|
||||
|
||||
## 分类
|
||||
|
||||
- 核心框架
|
||||
|
||||
## 目的
|
||||
|
||||
为角色、怪物等实体提供统一的技能管理、冷却控制、释放接口和事件通知。
|
||||
|
||||
---
|
||||
|
||||
## 核心组成
|
||||
|
||||
| 类/结构 | 职责 |
|
||||
|---------|------|
|
||||
| `HFGenericSkillSystem` | 通用技能系统,继承 `AbsHFSkillSystem<HFGenericSkillModel>` |
|
||||
| `HFGenericSkillModel` | 通用技能模型,继承 `HFAbsModel` |
|
||||
| `HFEvent_OnSkillUsed` | 技能使用事件 |
|
||||
| `stSkillConfig` | 技能配置结构 |
|
||||
|
||||
---
|
||||
|
||||
## 技能管理
|
||||
|
||||
```csharp
|
||||
// 添加技能
|
||||
skillSystem.AddSkill(skillId);
|
||||
|
||||
// 移除技能
|
||||
skillSystem.RemoveSkill(skillId);
|
||||
|
||||
// 检查技能是否就绪
|
||||
bool isReady = skillSystem.IsSkillReady(skillId);
|
||||
|
||||
// 获取技能冷却时间
|
||||
float cooldown = skillSystem.GetSkillCooldown(skillId);
|
||||
|
||||
// 是否有技能在冷却
|
||||
bool hasCooldown = skillSystem.HasSkillInCooldown();
|
||||
|
||||
// 清除所有冷却
|
||||
skillSystem.ClearAllCooldowns();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 技能使用
|
||||
|
||||
```csharp
|
||||
// 对目标使用技能
|
||||
skillSystem.UseSkill(skillId, targetEntity);
|
||||
|
||||
// 对位置使用技能
|
||||
skillSystem.UseSkillAtPosition(skillId, worldPosition);
|
||||
```
|
||||
|
||||
触发事件:
|
||||
|
||||
```csharp
|
||||
public class HFEvent_OnSkillUsed : AbsHFEventArg<enHFCTCEvent>
|
||||
{
|
||||
public HFAbsEntity Caster { get; set; }
|
||||
public int SkillId { get; set; }
|
||||
public HFAbsEntity Target { get; set; }
|
||||
public Vector3 Position { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 自动配置加载
|
||||
|
||||
系统会根据实体配置自动加载技能:
|
||||
|
||||
```csharp
|
||||
var config = HFFunCatalogue.NDB.ds_EntityConfig_Sheet_TableData_1FC4334A.ReadTable(ownerEntity.EntityId);
|
||||
if (config != null && config.SkillID > 0)
|
||||
{
|
||||
AddSkill(config.SkillID);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 实体中的添加方式
|
||||
|
||||
在角色或敌人实体中统一添加:
|
||||
|
||||
```csharp
|
||||
AddSystem<HFGenericSkillSystem>();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 子系统边界
|
||||
|
||||
从实体系统角度看,技能相关子目录通常包括:
|
||||
|
||||
- `Models/Skill` — 技能数据模型
|
||||
- `Systems/Skill` — 技能系统逻辑
|
||||
- `HFSkillTrigger_*` — 技能触发器
|
||||
- `HFSkill_*_Releaser` — 技能释放器
|
||||
|
||||
---
|
||||
|
||||
## 使用规则
|
||||
|
||||
- 角色和怪物使用同一套技能系统接口。
|
||||
- 技能 ID 在项目中应保持唯一。
|
||||
- 技能效果逻辑应在事件处理器中实现,避免直接写在释放器里。
|
||||
- 合理设置冷却时间以平衡游戏体验。
|
||||
|
||||
---
|
||||
|
||||
## 相关模块
|
||||
|
||||
- [实体系统](../实体系统/README.md)
|
||||
- [事件系统](../事件系统/README.md)
|
||||
- [数据/配置框架](../数据配置框架.md)
|
||||
|
||||
---
|
||||
|
||||
## 文档关联
|
||||
|
||||
> 当本文档发生变更时,应同步检查以下关联文档,避免信息不一致。
|
||||
|
||||
| 文档 | 关联原因 |
|
||||
|---|---|
|
||||
| [modules/模块索引.md](../../模块索引.md) | 模块索引是项目知识总入口 |
|
||||
| [modules/frameworks/实体系统/README.md](../实体系统/README.md) | 技能系统是实体系统的子系统 |
|
||||
| [modules/frameworks/事件系统/README.md](../事件系统/README.md) | 技能使用事件依赖事件系统 |
|
||||
| [rules/10-架构说明.md](../../../rules/10-架构说明.md) | 架构说明定义总体分层和依赖方向 |
|
||||
| [rules/30-模块登记规则.md](../../../rules/30-模块登记规则.md) | 模块登记规则定义分类和状态口径 |
|
||||
143
modules/frameworks/技能系统/使用指南.md
Normal file
143
modules/frameworks/技能系统/使用指南.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# 通用技能系统使用指南
|
||||
|
||||
## 概述
|
||||
|
||||
通用技能系统(`HFGenericSkillSystem`)是一个适用于角色和怪物的统一技能管理系统,提供了一套标准化的技能操作接口。
|
||||
|
||||
## 架构设计
|
||||
|
||||
### 系统组成
|
||||
- **HFGenericSkillSystem** - 通用技能系统(继承自 `AbsHFSkillSystem<HFGenericSkillModel>`)
|
||||
- **HFGenericSkillModel** - 通用技能模型(继承自 `HFAbsModel`)
|
||||
- **HFEvent_OnSkillUsed** - 技能使用事件
|
||||
|
||||
### 核心功能
|
||||
|
||||
#### 1. 技能管理
|
||||
```csharp
|
||||
// 添加技能
|
||||
skillSystem.AddSkill(skillId);
|
||||
|
||||
// 移除技能
|
||||
skillSystem.RemoveSkill(skillId);
|
||||
|
||||
// 检查技能是否就绪
|
||||
bool isReady = skillSystem.IsSkillReady(skillId);
|
||||
|
||||
// 获取技能冷却时间
|
||||
float cooldown = skillSystem.GetSkillCooldown(skillId);
|
||||
```
|
||||
|
||||
#### 2. 技能使用
|
||||
```csharp
|
||||
// 使用技能(指定目标)
|
||||
skillSystem.UseSkill(skillId, targetEntity);
|
||||
|
||||
// 使用技能(指定位置)
|
||||
skillSystem.UseSkillAtPosition(skillId, worldPosition);
|
||||
```
|
||||
|
||||
#### 3. 冷却管理
|
||||
```csharp
|
||||
// 检查是否有技能在冷却
|
||||
bool hasCooldown = skillSystem.HasSkillInCooldown();
|
||||
|
||||
// 清除所有技能冷却
|
||||
skillSystem.ClearAllCooldowns();
|
||||
```
|
||||
|
||||
## 使用场景
|
||||
|
||||
### 角色实体
|
||||
在 `HFEntity_Role` 中,技能系统被自动添加:
|
||||
```csharp
|
||||
AddSystem<HFGenericSkillSystem>(); // 替代原来的 HFSkillSystem
|
||||
```
|
||||
|
||||
### 敌人实体
|
||||
在 `HFEntity_Enemy` 和 `HFEntity_Boss` 中,技能系统被自动添加:
|
||||
```csharp
|
||||
AddSystem<HFGenericSkillSystem>(); // 替代原来的 HFSkillSystem
|
||||
```
|
||||
|
||||
## 技能配置
|
||||
|
||||
### 技能配置结构
|
||||
```csharp
|
||||
public struct stSkillConfig
|
||||
{
|
||||
public int Id; // 技能ID
|
||||
public string Name; // 技能名称
|
||||
public float Cooldown; // 冷却时间
|
||||
public float Range; // 作用范围
|
||||
public float Damage; // 伤害值
|
||||
public string Description; // 描述
|
||||
}
|
||||
```
|
||||
|
||||
### 技能加载
|
||||
系统会根据实体配置自动加载对应的技能:
|
||||
```csharp
|
||||
// 从 EntityConfig 表加载技能
|
||||
var config = HFFunCatalogue.NDB.ds_EntityConfig_Sheet_TableData_1FC4334A.ReadTable(ownerEntity.EntityId);
|
||||
if (config != null && config.SkillID > 0)
|
||||
{
|
||||
AddSkill(config.SkillID);
|
||||
}
|
||||
```
|
||||
|
||||
## 事件系统
|
||||
|
||||
### 技能使用事件
|
||||
当技能被使用时,系统会触发 `HFEvent_OnSkillUsed` 事件:
|
||||
```csharp
|
||||
public class HFEvent_OnSkillUsed : AbsHFEventArg<enHFCTCEvent>
|
||||
{
|
||||
public HFAbsEntity Caster { get; set; } // 施法者
|
||||
public int SkillId { get; set; } // 技能ID
|
||||
public HFAbsEntity Target { get; set; } // 目标
|
||||
public Vector3 Position { get; set; } // 位置(可选)
|
||||
}
|
||||
```
|
||||
|
||||
## 扩展接口
|
||||
|
||||
### Buff 管理
|
||||
```csharp
|
||||
// 添加 Buff
|
||||
skillSystem.AddBuff(buffTemplate);
|
||||
|
||||
// 移除 Buff
|
||||
skillSystem.RemoveBuff(buffId);
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
1. **统一接口**:角色和怪物使用相同的技能系统接口
|
||||
2. **自动配置**:技能配置根据实体类型自动加载
|
||||
3. **冷却管理**:内置技能冷却机制
|
||||
4. **事件驱动**:通过事件系统处理技能效果
|
||||
5. **类型安全**:使用强类型的技能ID和配置
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 技能释放器的具体实现需要根据游戏需求进行定制
|
||||
2. 技能效果逻辑应在事件处理器中实现
|
||||
3. 确保技能 ID 在项目中唯一
|
||||
4. 合理设置技能冷却时间以平衡游戏体验
|
||||
|
||||
---
|
||||
|
||||
## 文档关联
|
||||
|
||||
> 当本文档发生变更时,应同步检查以下关联文档,避免信息不一致。
|
||||
|
||||
| 文档 | 关联原因 |
|
||||
|---|---|
|
||||
| [技能系统入口](README.md) | 本文件是技能系统模块的子文档 |
|
||||
| [实体系统](../实体系统/README.md) | 技能系统是实体系统的子系统 |
|
||||
| [事件系统](../事件系统/README.md) | 技能使用事件依赖事件系统 |
|
||||
| [数据/配置框架](../数据配置框架.md) | 技能配置加载依赖配置框架 |
|
||||
| [modules/模块索引.md](../../模块索引.md) | 模块索引是项目知识总入口 |
|
||||
| [rules/10-架构说明.md](../../../rules/10-架构说明.md) | 架构说明定义总体分层和依赖方向 |
|
||||
| [rules/30-模块登记规则.md](../../../rules/30-模块登记规则.md) | 模块登记规则定义分类和状态口径 |
|
||||
111
modules/frameworks/数据配置框架.md
Normal file
111
modules/frameworks/数据配置框架.md
Normal file
@@ -0,0 +1,111 @@
|
||||
# 数据 / 配置框架
|
||||
|
||||
## 分类
|
||||
|
||||
- 核心框架
|
||||
|
||||
## 目的
|
||||
|
||||
管理 XLSX 配置表源、解析、生成运行时数据和读取 API。
|
||||
|
||||
---
|
||||
|
||||
## 核心路径
|
||||
|
||||
- 配置源:`XLSX_SRC/CN/`、`XLSX_SRC/EN/`
|
||||
- 生成设置:`ProjectSettings/ZYF/ZYXlsx/ZYXlsxProjectSettings.asset`
|
||||
- Resolve Asset:`Assets/Editor/XLSX_SRC/Resolve/ZYXlsx/`
|
||||
- 输出数据:`Assets/AutoBuildAsset/{Editor,Hotfix}/AutoFrame/Google.Protobuf/XLSX_SRC/<语言>/`
|
||||
- 输出代码:`Assets/AutoBuildScript/{Editor,General,Hotfix}/AutoFrame/Google.Protobuf/XLSX_Script/`
|
||||
|
||||
---
|
||||
|
||||
## 常见配置表
|
||||
|
||||
| 表名 | 用途 |
|
||||
|---|---|
|
||||
| `Constants.xlsx` | 常量配置 |
|
||||
| `EntityConfig.xlsx` | 实体配置 |
|
||||
| `Gift.xlsx` | 礼包/奖励配置 |
|
||||
| `Resource.xlsx` | 资源/道具配置 |
|
||||
| `SkillGroup.xlsx` | 技能组配置 |
|
||||
| `SkillReleaser.xlsx` | 技能释放器配置 |
|
||||
| `SkillTrigger.xlsx` | 技能触发器配置 |
|
||||
| `SkillUp.xlsx` | 技能升级配置 |
|
||||
| `Sounds.xlsx` | 音效配置 |
|
||||
| `Wave.xlsx` | 波次配置(业务示例) |
|
||||
|
||||
---
|
||||
|
||||
## 运行时加载调用链
|
||||
|
||||
```text
|
||||
HFGameStart.LoadDB()
|
||||
-> Hotfix_ZYXlsx_GoogleProtobuf_Helper.instance.LoadAllXlsx(OnLoadDbFinish)
|
||||
-> 遍历 enZYXlsxSheet
|
||||
-> GetExistsDataLang / GetSheetMemoryPackBufferFilePath
|
||||
-> HFFunCatalogue.Asset.LoadDBHelper(path.GetHashCode(), path, callback)
|
||||
-> 缓存 sheet bytes 到 mDicLangSheetBytes
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 运行时读取 API
|
||||
|
||||
```csharp
|
||||
// 按主键读取单条
|
||||
HFFunCatalogue.NDB.ds_xxx.ReadTableByPK(pk);
|
||||
|
||||
// 读取全表
|
||||
HFFunCatalogue.NDB.ds_xxx.ReadTableList();
|
||||
|
||||
// 读取行列式数据
|
||||
HFFunCatalogue.NDB.ds_xxx.ReadTableDeterminant();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 使用规则
|
||||
|
||||
- 改表后必须重新生成。
|
||||
- 配置读取必须在 `LoadAllXlsx` 完成后。
|
||||
- 主键/全表/常量分别用对应 API。
|
||||
- 禁止高频全表读取。
|
||||
- id 优先来自表/协议/常量,禁止魔法数字。
|
||||
- 高频配置在 manager 初始化或窗口打开时缓存。
|
||||
- 多语言字段需补齐。
|
||||
|
||||
---
|
||||
|
||||
## 变更清单
|
||||
|
||||
1. 修改源表。
|
||||
2. 确认 settings / resolve 配置。
|
||||
3. 重新生成。
|
||||
4. 检查输出。
|
||||
5. 编译验证。
|
||||
6. 运行时验证。
|
||||
7. 更新文档。
|
||||
|
||||
---
|
||||
|
||||
## 相关模块
|
||||
|
||||
- [实体系统](实体系统/README.md)
|
||||
- [技能系统](技能系统/README.md)
|
||||
- [资源管理](资源管理/README.md)
|
||||
|
||||
---
|
||||
|
||||
## 文档关联
|
||||
|
||||
> 当本文档发生变更时,应同步检查以下关联文档,避免信息不一致。
|
||||
|
||||
| 文档 | 关联原因 |
|
||||
|---|---|
|
||||
| [modules/模块索引.md](../模块索引.md) | 模块索引是项目知识总入口 |
|
||||
| [modules/frameworks/实体系统/README.md](实体系统/README.md) | 实体配置来自配置表 |
|
||||
| [modules/frameworks/技能系统/README.md](技能系统/README.md) | 技能配置来自配置表 |
|
||||
| [modules/frameworks/资源管理/README.md](资源管理/README.md) | 配置资源加载依赖资源管理 |
|
||||
| [rules/10-架构说明.md](../../rules/10-架构说明.md) | 架构说明定义总体分层和依赖方向 |
|
||||
| [rules/30-模块登记规则.md](../../rules/30-模块登记规则.md) | 模块登记规则定义分类和状态口径 |
|
||||
122
modules/frameworks/相机系统/README.md
Normal file
122
modules/frameworks/相机系统/README.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# 相机系统
|
||||
|
||||
## 分类
|
||||
|
||||
- 核心框架
|
||||
|
||||
## 目的
|
||||
|
||||
管理多相机注册、相机堆栈排序、渲染顺序控制,为 UI、场景、特效等提供分层渲染能力。
|
||||
|
||||
---
|
||||
|
||||
## 核心类
|
||||
|
||||
| 类 | 职责 |
|
||||
|---|---|
|
||||
| `HFCameraManager` | 相机管理器单例,维护相机列表和主相机引用 |
|
||||
| `HFMainCamera` | 主相机,管理 CameraStack |
|
||||
| `AbsHFCameraItem` | 相机项抽象基类,子类实现具体相机逻辑 |
|
||||
|
||||
---
|
||||
|
||||
## 创建自定义相机
|
||||
|
||||
继承 `AbsHFCameraItem`,实现 `CameraItem` 和 `Weight` 属性:
|
||||
|
||||
```csharp
|
||||
public class UIOverlayCamera : AbsHFCameraItem
|
||||
{
|
||||
[SerializeField] private Camera uiCamera;
|
||||
[SerializeField] private int weight = 10;
|
||||
|
||||
public override Camera CameraItem => uiCamera;
|
||||
public override int Weight => weight;
|
||||
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
base.OnInitialize();
|
||||
uiCamera.clearFlags = CameraClearFlags.Depth;
|
||||
uiCamera.depth = 100;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 相机堆栈架构
|
||||
|
||||
```text
|
||||
HFMainCamera (主相机)
|
||||
│
|
||||
├── CameraStack (相机堆栈)
|
||||
│ ├── CameraItem1 (Weight=10)
|
||||
│ ├── CameraItem2 (Weight=20)
|
||||
│ └── CameraItem3 (Weight=5)
|
||||
│
|
||||
└── 按 Weight 排序 → 渲染顺序控制
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 管理相机
|
||||
|
||||
```csharp
|
||||
public class CameraController : MonoBehaviour
|
||||
{
|
||||
void Start()
|
||||
{
|
||||
HFCameraManager cameraMgr = HFCameraManager.Instance;
|
||||
|
||||
UIOverlayCamera uiCamera = GetComponent<UIOverlayCamera>();
|
||||
cameraMgr.AddCamera(uiCamera);
|
||||
|
||||
Camera mainCam = cameraMgr.mainCamera.MainCamera;
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
UIOverlayCamera uiCamera = GetComponent<UIOverlayCamera>();
|
||||
HFCameraManager.Instance.RemoveCamera(uiCamera);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 常用 API
|
||||
|
||||
| 方法 | 说明 |
|
||||
|-----|------|
|
||||
| `AddCamera(AbsHFCameraItem)` | 添加相机到堆栈 |
|
||||
| `RemoveCamera(AbsHFCameraItem)` | 从堆栈移除相机 |
|
||||
| `GetCamera(string cameraName)` | 获取指定名称相机 |
|
||||
|
||||
---
|
||||
|
||||
## 使用规则
|
||||
|
||||
- 相机项必须继承 `AbsHFCameraItem`。
|
||||
- `Weight` 决定堆栈内渲染顺序,值越大越靠后渲染。
|
||||
- 添加和移除相机需成对出现,避免残留。
|
||||
- UI 相机通常使用 `CameraClearFlags.Depth` 和较高 depth。
|
||||
|
||||
---
|
||||
|
||||
## 相关模块
|
||||
|
||||
- [UI 窗口系统](../UI窗口系统/README.md)
|
||||
- [资源管理](../资源管理/README.md)
|
||||
|
||||
---
|
||||
|
||||
## 文档关联
|
||||
|
||||
> 当本文档发生变更时,应同步检查以下关联文档,避免信息不一致。
|
||||
|
||||
| 文档 | 关联原因 |
|
||||
|---|---|
|
||||
| [modules/模块索引.md](../../模块索引.md) | 模块索引是项目知识总入口 |
|
||||
| [modules/frameworks/UI窗口系统/README.md](../UI窗口系统/README.md) | UI 窗口系统常与相机系统协作 |
|
||||
| [rules/10-架构说明.md](../../../rules/10-架构说明.md) | 架构说明定义总体分层和依赖方向 |
|
||||
| [rules/30-模块登记规则.md](../../../rules/30-模块登记规则.md) | 模块登记规则定义分类和状态口径 |
|
||||
101
modules/frameworks/相机系统/使用指南.md
Normal file
101
modules/frameworks/相机系统/使用指南.md
Normal file
@@ -0,0 +1,101 @@
|
||||
# 相机系统使用指南
|
||||
|
||||
## 一、创建自定义相机
|
||||
|
||||
### 1.1 继承 AbsHFCameraItem
|
||||
|
||||
```csharp
|
||||
public class UIOverlayCamera : AbsHFCameraItem
|
||||
{
|
||||
[SerializeField] private Camera uiCamera;
|
||||
[SerializeField] private int weight = 10;
|
||||
|
||||
public override Camera CameraItem => uiCamera;
|
||||
public override int Weight => weight;
|
||||
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
base.OnInitialize();
|
||||
// 自定义初始化逻辑
|
||||
uiCamera.clearFlags = CameraClearFlags.Depth;
|
||||
uiCamera.depth = 100;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、管理相机堆栈
|
||||
|
||||
### 2.1 使用相机管理器
|
||||
|
||||
```csharp
|
||||
public class CameraController : MonoBehaviour
|
||||
{
|
||||
void Start()
|
||||
{
|
||||
// 获取相机管理器
|
||||
HFCameraManager cameraMgr = HFCameraManager.Instance;
|
||||
|
||||
// 添加自定义相机
|
||||
UIOverlayCamera uiCamera = GetComponent<UIOverlayCamera>();
|
||||
cameraMgr.AddCamera(uiCamera);
|
||||
|
||||
// 获取主相机
|
||||
Camera mainCam = cameraMgr.mainCamera.MainCamera;
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
UIOverlayCamera uiCamera = GetComponent<UIOverlayCamera>();
|
||||
HFCameraManager.Instance.RemoveCamera(uiCamera);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 相机堆栈架构
|
||||
|
||||
```
|
||||
相机层级架构:
|
||||
HFMainCamera (主相机)
|
||||
│
|
||||
├── CameraStack (相机堆栈)
|
||||
│ ├── CameraItem1 (Weight=10)
|
||||
│ ├── CameraItem2 (Weight=20)
|
||||
│ └── CameraItem3 (Weight=5)
|
||||
│
|
||||
└── 按 Weight 排序 → 渲染顺序控制
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、相机管理 API
|
||||
|
||||
### 3.1 常用方法
|
||||
|
||||
| 方法 | 说明 |
|
||||
|-----|------|
|
||||
| `AddCamera()` | 添加相机到堆栈 |
|
||||
| `RemoveCamera()` | 从堆栈移除相机 |
|
||||
| `GetCamera()` | 获取指定相机 |
|
||||
|
||||
---
|
||||
|
||||
**版本**: v1.0
|
||||
**生效日期**: 2026年
|
||||
**适用范围**: GiftGameX 项目业务开发人员
|
||||
|
||||
---
|
||||
|
||||
## 文档关联
|
||||
|
||||
> 当本文档发生变更时,应同步检查以下关联文档,避免信息不一致。
|
||||
|
||||
| 文档 | 关联原因 |
|
||||
|---|---|
|
||||
| [相机系统入口](README.md) | 本文件是相机系统模块的子文档 |
|
||||
| [UI 窗口系统](../UI窗口系统/README.md) | UI 窗口系统常与相机系统协作 |
|
||||
| [资源管理](../资源管理/README.md) | 相机资源加载依赖资源管理 |
|
||||
| [modules/模块索引.md](../../模块索引.md) | 模块索引是项目知识总入口 |
|
||||
| [rules/10-架构说明.md](../../../rules/10-架构说明.md) | 架构说明定义总体分层和依赖方向 |
|
||||
| [rules/30-模块登记规则.md](../../../rules/30-模块登记规则.md) | 模块登记规则定义分类和状态口径 |
|
||||
49
modules/frameworks/第三方插件.md
Normal file
49
modules/frameworks/第三方插件.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# 第三方插件
|
||||
|
||||
## 分类
|
||||
|
||||
- 项目框架
|
||||
|
||||
## 目的
|
||||
|
||||
记录第三方插件和 embedded packages 的用途、边界和修改规则。
|
||||
|
||||
## 已知线索
|
||||
|
||||
- 插件目录:`Assets/ThirdPlugins/`
|
||||
- Package 目录:`Packages/`
|
||||
- 已识别插件包括 HybridCLR、Spine、DOTween、SuperScrollView、Astar、UnityWebSocket、SoundManagerPro、SRDebugger 等。
|
||||
|
||||
## 梳理任务
|
||||
|
||||
- [ ] 梳理主要插件列表和用途。
|
||||
- [ ] 标记哪些插件是运行时依赖,哪些是编辑器依赖。
|
||||
- [ ] 梳理插件是否有项目本地修改。
|
||||
- [ ] 梳理升级插件的风险和验证步骤。
|
||||
- [ ] 记录禁止直接修改 vendor 的规则例外。
|
||||
|
||||
## 待确认
|
||||
|
||||
- 哪些插件来自 Asset Store,哪些来自 package。
|
||||
- 是否有插件被项目深度改造。
|
||||
- 哪些插件参与 hotfix 或 WebGL 构建。
|
||||
|
||||
## 相关模块
|
||||
|
||||
- [通用框架](通用框架/README.md)
|
||||
- [编辑器工具索引](../模块索引.md#编辑器工具)
|
||||
|
||||
---
|
||||
|
||||
## 文档关联
|
||||
|
||||
> 当本文档发生变更时,应同步检查以下关联文档,避免信息不一致。
|
||||
|
||||
| 文档 | 关联原因 |
|
||||
|---|---|
|
||||
| [modules/模块索引.md](../模块索引.md) | 编辑器工具索引 |
|
||||
| [modules/frameworks/通用框架/README.md](通用框架/README.md) | 通用框架 |
|
||||
| [rules/10-架构说明.md](../../rules/10-架构说明.md) | 架构说明定义总体分层和依赖方向 |
|
||||
| [rules/20-开发规则.md](../../rules/20-开发规则.md) | 开发规则与框架/生成规则互相引用 |
|
||||
| [rules/30-模块登记规则.md](../../rules/30-模块登记规则.md) | 模块登记规则定义分类和状态口径 |
|
||||
| TODO:补充依赖或使用本框架的模块文档 | 如依赖本框架的功能模块 |
|
||||
54
modules/frameworks/编辑器工具/README.md
Normal file
54
modules/frameworks/编辑器工具/README.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# 编辑器工具
|
||||
|
||||
## 分类
|
||||
|
||||
- 编辑器工具
|
||||
|
||||
## 目的
|
||||
|
||||
汇总 Unity Editor 内使用的 StrayFog 相关编辑器扩展和项目编辑器资源。
|
||||
|
||||
---
|
||||
|
||||
## SF Editor
|
||||
|
||||
- **路径**:`Assets/StrayFog/Editor/`
|
||||
- **程序集**:`SFEditor`
|
||||
- **职责**:
|
||||
- StrayFog 编辑器扩展
|
||||
- 打包、UGUI、SpriteAtlas、表格路径选择等编辑器工具
|
||||
|
||||
---
|
||||
|
||||
## Project Editor Assets
|
||||
|
||||
- **路径**:`Assets/Editor/`
|
||||
- **职责**:
|
||||
- 项目编辑器资源与脚本
|
||||
- 引导菜单、编辑器插件
|
||||
|
||||
---
|
||||
|
||||
## 使用规则
|
||||
|
||||
- 编辑器工具可以读取/生成核心运行时代码,但运行时代码不要反向依赖 Editor 程序集。
|
||||
- 新增 Editor 工具时,优先在 `Assets/StrayFog/Editor/` 或 `Assets/Editor/` 下按功能分类存放。
|
||||
|
||||
---
|
||||
|
||||
## 相关模块
|
||||
|
||||
- [通用框架](../通用框架/README.md)
|
||||
- [资源管理](../资源管理/README.md)
|
||||
|
||||
---
|
||||
|
||||
## 文档关联
|
||||
|
||||
> 当本文档发生变更时,应同步检查以下关联文档,避免信息不一致。
|
||||
|
||||
| 文档 | 关联原因 |
|
||||
|---|---|
|
||||
| [modules/模块索引.md](../../模块索引.md) | 模块索引是项目知识总入口 |
|
||||
| [rules/10-架构说明.md](../../../rules/10-架构说明.md) | 编辑器工具层的架构说明 |
|
||||
| [rules/30-模块登记规则.md](../../../rules/30-模块登记规则.md) | 模块登记规则定义分类和状态口径 |
|
||||
176
modules/frameworks/编辑器工具/UI工具/ExternalCommandListener.cs
Normal file
176
modules/frameworks/编辑器工具/UI工具/ExternalCommandListener.cs
Normal file
@@ -0,0 +1,176 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using System.IO;
|
||||
|
||||
[InitializeOnLoad]
|
||||
public static class ExternalCommandListener
|
||||
{
|
||||
private static FileSystemWatcher watcher;
|
||||
private static string triggerFilePath;
|
||||
private static bool isInitialized = false;
|
||||
|
||||
static ExternalCommandListener()
|
||||
{
|
||||
EditorApplication.update += InitializeOnFirstUpdate;
|
||||
}
|
||||
|
||||
private static void InitializeOnFirstUpdate()
|
||||
{
|
||||
if (!isInitialized)
|
||||
{
|
||||
isInitialized = true;
|
||||
EditorApplication.update -= InitializeOnFirstUpdate;
|
||||
SetupFileWatcher();
|
||||
Debug.Log("ExternalCommandListener started");
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetupFileWatcher()
|
||||
{
|
||||
triggerFilePath = Path.Combine(Application.dataPath, "..", "Trigger_Command.txt");
|
||||
string watchFolder = Path.GetDirectoryName(triggerFilePath);
|
||||
|
||||
if (watcher != null) watcher.Dispose();
|
||||
|
||||
watcher = new FileSystemWatcher(watchFolder, "Trigger_Command.txt");
|
||||
watcher.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.LastWrite;
|
||||
watcher.Created += OnFileChanged;
|
||||
watcher.Changed += OnFileChanged;
|
||||
watcher.EnableRaisingEvents = true;
|
||||
}
|
||||
|
||||
private static void OnFileChanged(object sender, FileSystemEventArgs e)
|
||||
{
|
||||
if (e.ChangeType != WatcherChangeTypes.Created && e.ChangeType != WatcherChangeTypes.Changed)
|
||||
return;
|
||||
|
||||
EditorApplication.delayCall += ExecuteCommandFromFile;
|
||||
}
|
||||
|
||||
private static void ExecuteCommandFromFile()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!File.Exists(triggerFilePath)) return;
|
||||
|
||||
string content = File.ReadAllText(triggerFilePath).Trim();
|
||||
File.Delete(triggerFilePath);
|
||||
|
||||
ParseAndExecute(content);
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.LogError("Execute failed: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ParseAndExecute(string content)
|
||||
{
|
||||
content = content.Trim().Trim('"', '\'');
|
||||
|
||||
string[] parts = content.Split(':');
|
||||
if (parts.Length == 0) return;
|
||||
|
||||
string command = parts[0].Trim();
|
||||
string[] args = parts.Length > 1 ? parts[1..] : new string[0];
|
||||
|
||||
ExecuteCommand(command, args);
|
||||
}
|
||||
|
||||
private static void ExecuteCommand(string command, string[] args)
|
||||
{
|
||||
switch (command)
|
||||
{
|
||||
case "ReadJson":
|
||||
ExecuteReadJson(args);
|
||||
break;
|
||||
case "GenerateUIPrefab":
|
||||
ExecuteGenerateUIPrefab(args);
|
||||
break;
|
||||
case "ExportPrefabToJson":
|
||||
ExecuteExportPrefabToJson(args);
|
||||
break;
|
||||
default:
|
||||
Debug.LogWarning("Unknown command: " + command);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ExecuteReadJson(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
var method = typeof(RemoteCommand).GetMethod("ReadJson",
|
||||
System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static,
|
||||
null,
|
||||
new[] { typeof(string[]) },
|
||||
null);
|
||||
|
||||
if (method != null)
|
||||
{
|
||||
method.Invoke(null, new object[] { args });
|
||||
Debug.Log("Executed: ReadJson(" + string.Join(", ", args) + ")");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("RemoteCommand.ReadJson not found");
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.LogError("ReadJson failed: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ExecuteGenerateUIPrefab(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
var method = typeof(RemoteCommand).GetMethod("GenerateUIPrefab",
|
||||
System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static,
|
||||
null,
|
||||
new[] { typeof(string[]) },
|
||||
null);
|
||||
|
||||
if (method != null)
|
||||
{
|
||||
method.Invoke(null, new object[] { args });
|
||||
Debug.Log("Executed: GenerateUIPrefab(" + string.Join(", ", args) + ")");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("RemoteCommand.GenerateUIPrefab not found");
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.LogError("GenerateUIPrefab failed: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ExecuteExportPrefabToJson(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
var method = typeof(RemoteCommand).GetMethod("ExportPrefabToJson",
|
||||
System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static,
|
||||
null,
|
||||
new[] { typeof(string[]) },
|
||||
null);
|
||||
|
||||
if (method != null)
|
||||
{
|
||||
method.Invoke(null, new object[] { args });
|
||||
Debug.Log("Executed: ExportPrefabToJson(" + string.Join(", ", args) + ")");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("RemoteCommand.ExportPrefabToJson not found");
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.LogError("ExportPrefabToJson failed: " + ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 93f1f8f942aa53347a7e58bb4c561893
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
39
modules/frameworks/编辑器工具/UI工具/RemoteCommand.cs
Normal file
39
modules/frameworks/编辑器工具/UI工具/RemoteCommand.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
public static class RemoteCommand
|
||||
{
|
||||
public static void ReadJson(string[] args)
|
||||
{
|
||||
SFP.Error("收到参数: " + string.Join(", ", args));
|
||||
}
|
||||
|
||||
public static void GenerateUIPrefab(string[] args)
|
||||
{
|
||||
if (args.Length == 0)
|
||||
{
|
||||
SFP.Error("GenerateUIPrefab: 需要提供JSON文件路径参数");
|
||||
return;
|
||||
}
|
||||
|
||||
string jsonPath = args[0];
|
||||
string prefabPath = args.Length > 1 ? args[1] : null;
|
||||
|
||||
SFP.Error("GenerateUIPrefab: 开始生成UI, JSON路径: " + jsonPath);
|
||||
|
||||
UIPrefabGenerator.GeneratePrefabFromJson(jsonPath, prefabPath);
|
||||
}
|
||||
|
||||
public static void ExportPrefabToJson(string[] args)
|
||||
{
|
||||
if (args.Length == 0)
|
||||
{
|
||||
SFP.Error("ExportPrefabToJson: 需要提供预制体文件路径参数");
|
||||
return;
|
||||
}
|
||||
|
||||
string prefabPath = args[0];
|
||||
string jsonPath = args.Length > 1 ? args[1] : null;
|
||||
|
||||
SFP.Error("ExportPrefabToJson: 开始导出JSON, 预制体路径: " + prefabPath);
|
||||
|
||||
UIPrefabToJson.ExportPrefabToJson(prefabPath, jsonPath);
|
||||
}
|
||||
}
|
||||
11
modules/frameworks/编辑器工具/UI工具/RemoteCommand.cs.meta
Normal file
11
modules/frameworks/编辑器工具/UI工具/RemoteCommand.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 359ded64689f3f64c87c561d88cead9d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
584
modules/frameworks/编辑器工具/UI工具/UIPrefabGenerator.cs
Normal file
584
modules/frameworks/编辑器工具/UI工具/UIPrefabGenerator.cs
Normal file
@@ -0,0 +1,584 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEditor;
|
||||
using TMPro;
|
||||
|
||||
public static class UIPrefabGenerator
|
||||
{
|
||||
public static void GeneratePrefabFromJson(string jsonPath, string prefabPath = null)
|
||||
{
|
||||
if (!File.Exists(jsonPath))
|
||||
{
|
||||
Debug.LogError("JSON file not found: " + jsonPath);
|
||||
return;
|
||||
}
|
||||
|
||||
string jsonContent = File.ReadAllText(jsonPath);
|
||||
UILayoutData layoutData = JsonUtility.FromJson<UILayoutData>(jsonContent);
|
||||
|
||||
if (layoutData == null)
|
||||
{
|
||||
Debug.LogError("Failed to parse JSON file: " + jsonPath);
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(prefabPath))
|
||||
{
|
||||
prefabPath = "Assets/Game/GameHFResource/CN/UIWindows/" + layoutData.name + "/" + layoutData.name + ".prefab";
|
||||
}
|
||||
|
||||
GameObject root = new GameObject(layoutData.name);
|
||||
root.layer = layoutData.layer;
|
||||
|
||||
Canvas canvas = root.AddComponent<Canvas>();
|
||||
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
|
||||
if (layoutData.canvas != null)
|
||||
{
|
||||
canvas.pixelPerfect = layoutData.canvas.pixelPerfect;
|
||||
}
|
||||
|
||||
root.AddComponent<CanvasScaler>();
|
||||
root.AddComponent<GraphicRaycaster>();
|
||||
|
||||
RectTransform rootRect = root.GetComponent<RectTransform>();
|
||||
SetRectTransform(rootRect, layoutData.rectTransform);
|
||||
|
||||
foreach (var childData in layoutData.children)
|
||||
{
|
||||
CreateUIElement(childData, root.transform);
|
||||
}
|
||||
|
||||
CreatePrefab(root, prefabPath);
|
||||
GameObject.DestroyImmediate(root);
|
||||
|
||||
AssetDatabase.Refresh();
|
||||
Debug.Log("UI Prefab generated successfully: " + prefabPath);
|
||||
}
|
||||
|
||||
private static void CreateUIElement(UIElementData data, Transform parent)
|
||||
{
|
||||
GameObject obj = new GameObject(data.name);
|
||||
obj.transform.SetParent(parent, false);
|
||||
|
||||
RectTransform rect = obj.AddComponent<RectTransform>();
|
||||
SetRectTransform(rect, data.rectTransform);
|
||||
|
||||
switch (data.type)
|
||||
{
|
||||
case "Image":
|
||||
case "SFUI_Image":
|
||||
CreateImage(obj, data.image);
|
||||
break;
|
||||
case "Button":
|
||||
case "SFUI_Button":
|
||||
CreateButton(obj, data.button, data.image);
|
||||
break;
|
||||
case "TextMeshProUGUI":
|
||||
case "SFUI_TextMeshProUGUI":
|
||||
CreateTextMeshPro(obj, data.text);
|
||||
break;
|
||||
case "RectTransform":
|
||||
case "SFUI_Viewport":
|
||||
case "SFUI_Content":
|
||||
CreateRectTransform(obj, data.layoutGroup);
|
||||
break;
|
||||
case "ScrollRect":
|
||||
case "SFUI_ScrollRect":
|
||||
CreateScrollRect(obj, data.scrollRect);
|
||||
break;
|
||||
case "GridLayoutGroup":
|
||||
CreateGridLayoutGroup(obj, data.layoutGroup);
|
||||
break;
|
||||
case "VerticalLayoutGroup":
|
||||
CreateVerticalLayoutGroup(obj, data.layoutGroup);
|
||||
break;
|
||||
case "HorizontalLayoutGroup":
|
||||
CreateHorizontalLayoutGroup(obj, data.layoutGroup);
|
||||
break;
|
||||
case "Toggle":
|
||||
case "SFUI_Toggle":
|
||||
CreateToggle(obj, data.toggle);
|
||||
break;
|
||||
case "Slider":
|
||||
case "SFUI_Slider":
|
||||
CreateSlider(obj, data.slider);
|
||||
break;
|
||||
case "InputField":
|
||||
case "SFUI_InputField":
|
||||
CreateInputField(obj, data.inputField);
|
||||
break;
|
||||
case "CanvasScaler":
|
||||
case "GraphicRaycaster":
|
||||
break;
|
||||
case "SFUI_ListViewItem":
|
||||
CreateListViewItemPlaceholder(obj);
|
||||
break;
|
||||
case "Canvas":
|
||||
case "SFUI_Window":
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (data.children != null)
|
||||
{
|
||||
foreach (var childData in data.children)
|
||||
{
|
||||
CreateUIElement(childData, obj.transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetRectTransform(RectTransform rect, RectTransformData data)
|
||||
{
|
||||
if (data == null) return;
|
||||
|
||||
rect.anchorMin = new Vector2(data.anchorMin[0], data.anchorMin[1]);
|
||||
rect.anchorMax = new Vector2(data.anchorMax[0], data.anchorMax[1]);
|
||||
rect.anchoredPosition3D = new Vector3(data.position[0], data.position[1], data.position[2]);
|
||||
rect.sizeDelta = new Vector2(data.sizeDelta[0], data.sizeDelta[1]);
|
||||
rect.pivot = new Vector2(data.pivot[0], data.pivot[1]);
|
||||
|
||||
if (data.localScale != null && data.localScale.Length >= 3)
|
||||
{
|
||||
rect.localScale = new Vector3(data.localScale[0], data.localScale[1], data.localScale[2]);
|
||||
}
|
||||
|
||||
if (data.localRotation != null && data.localRotation.Length >= 3)
|
||||
{
|
||||
rect.localEulerAngles = new Vector3(data.localRotation[0], data.localRotation[1], data.localRotation[2]);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateImage(GameObject obj, ImageData data)
|
||||
{
|
||||
if (data == null) return;
|
||||
|
||||
Image image = obj.AddComponent<Image>();
|
||||
if (!string.IsNullOrEmpty(data.sprite))
|
||||
{
|
||||
Sprite sprite = AssetDatabase.LoadAssetAtPath<Sprite>(data.sprite);
|
||||
if (sprite != null)
|
||||
{
|
||||
image.sprite = sprite;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
image.type = (Image.Type)Enum.Parse(typeof(Image.Type), data.type);
|
||||
}
|
||||
catch
|
||||
{
|
||||
image.type = Image.Type.Simple;
|
||||
}
|
||||
|
||||
image.color = new Color(data.color[0], data.color[1], data.color[2], data.color[3]);
|
||||
image.raycastTarget = data.raycastTarget;
|
||||
image.preserveAspect = data.preserveAspect;
|
||||
}
|
||||
|
||||
private static void CreateButton(GameObject obj, ButtonData buttonData, ImageData imageData)
|
||||
{
|
||||
Button button = obj.AddComponent<Button>();
|
||||
if (buttonData != null)
|
||||
{
|
||||
button.interactable = buttonData.interactable;
|
||||
try
|
||||
{
|
||||
button.transition = (Selectable.Transition)Enum.Parse(typeof(Selectable.Transition), buttonData.transition);
|
||||
}
|
||||
catch
|
||||
{
|
||||
button.transition = Selectable.Transition.ColorTint;
|
||||
}
|
||||
}
|
||||
|
||||
if (imageData != null)
|
||||
{
|
||||
CreateImage(obj, imageData);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateTextMeshPro(GameObject obj, TextData data)
|
||||
{
|
||||
if (data == null) return;
|
||||
|
||||
TextMeshProUGUI text = obj.AddComponent<TextMeshProUGUI>();
|
||||
text.text = data.text;
|
||||
text.fontSize = data.fontSize;
|
||||
text.color = new Color(data.color[0], data.color[1], data.color[2], data.color[3]);
|
||||
|
||||
if (!string.IsNullOrEmpty(data.font))
|
||||
{
|
||||
TMP_FontAsset fontAsset = AssetDatabase.LoadAssetAtPath<TMP_FontAsset>(data.font);
|
||||
if (fontAsset != null)
|
||||
{
|
||||
text.font = fontAsset;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(data.materialPreset))
|
||||
{
|
||||
Material material = AssetDatabase.LoadAssetAtPath<Material>(data.materialPreset);
|
||||
if (material != null)
|
||||
{
|
||||
text.material = material;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
text.alignment = (TextAlignmentOptions)Enum.Parse(typeof(TextAlignmentOptions), data.alignment);
|
||||
}
|
||||
catch
|
||||
{
|
||||
text.alignment = TextAlignmentOptions.Center;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
text.verticalAlignment = (VerticalAlignmentOptions)Enum.Parse(typeof(VerticalAlignmentOptions), data.verticalAlignment);
|
||||
}
|
||||
catch
|
||||
{
|
||||
text.verticalAlignment = VerticalAlignmentOptions.Middle;
|
||||
}
|
||||
|
||||
text.richText = data.richText;
|
||||
|
||||
try
|
||||
{
|
||||
text.overflowMode = (TextOverflowModes)Enum.Parse(typeof(TextOverflowModes), data.overflowMode);
|
||||
}
|
||||
catch
|
||||
{
|
||||
text.overflowMode = TextOverflowModes.Truncate;
|
||||
}
|
||||
|
||||
text.enableWordWrapping = data.enableWordWrapping;
|
||||
}
|
||||
|
||||
private static void CreateRectTransform(GameObject obj, LayoutGroupData layoutData)
|
||||
{
|
||||
if (layoutData != null)
|
||||
{
|
||||
switch (layoutData.type)
|
||||
{
|
||||
case "GridLayoutGroup":
|
||||
CreateGridLayoutGroup(obj, layoutData);
|
||||
break;
|
||||
case "VerticalLayoutGroup":
|
||||
CreateVerticalLayoutGroup(obj, layoutData);
|
||||
break;
|
||||
case "HorizontalLayoutGroup":
|
||||
CreateHorizontalLayoutGroup(obj, layoutData);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateGridLayoutGroup(GameObject obj, LayoutGroupData layoutData)
|
||||
{
|
||||
if (layoutData == null) return;
|
||||
|
||||
GridLayoutGroup grid = obj.AddComponent<GridLayoutGroup>();
|
||||
grid.cellSize = new Vector2(layoutData.cellSize[0], layoutData.cellSize[1]);
|
||||
grid.spacing = new Vector2(layoutData.spacing[0], layoutData.spacing[1]);
|
||||
grid.constraint = (GridLayoutGroup.Constraint)layoutData.constraint;
|
||||
|
||||
try
|
||||
{
|
||||
grid.childAlignment = (TextAnchor)Enum.Parse(typeof(TextAnchor), layoutData.childAlignment);
|
||||
}
|
||||
catch
|
||||
{
|
||||
grid.childAlignment = TextAnchor.UpperLeft;
|
||||
}
|
||||
|
||||
grid.padding = new RectOffset(layoutData.padding[0], layoutData.padding[1], layoutData.padding[2], layoutData.padding[3]);
|
||||
}
|
||||
|
||||
private static void CreateVerticalLayoutGroup(GameObject obj, LayoutGroupData layoutData)
|
||||
{
|
||||
if (layoutData == null) return;
|
||||
|
||||
VerticalLayoutGroup vertical = obj.AddComponent<VerticalLayoutGroup>();
|
||||
vertical.spacing = layoutData.spacing[1];
|
||||
|
||||
try
|
||||
{
|
||||
vertical.childAlignment = (TextAnchor)Enum.Parse(typeof(TextAnchor), layoutData.childAlignment);
|
||||
}
|
||||
catch
|
||||
{
|
||||
vertical.childAlignment = TextAnchor.UpperLeft;
|
||||
}
|
||||
|
||||
vertical.padding = new RectOffset(layoutData.padding[0], layoutData.padding[1], layoutData.padding[2], layoutData.padding[3]);
|
||||
}
|
||||
|
||||
private static void CreateHorizontalLayoutGroup(GameObject obj, LayoutGroupData layoutData)
|
||||
{
|
||||
if (layoutData == null) return;
|
||||
|
||||
HorizontalLayoutGroup horizontal = obj.AddComponent<HorizontalLayoutGroup>();
|
||||
horizontal.spacing = layoutData.spacing[0];
|
||||
|
||||
try
|
||||
{
|
||||
horizontal.childAlignment = (TextAnchor)Enum.Parse(typeof(TextAnchor), layoutData.childAlignment);
|
||||
}
|
||||
catch
|
||||
{
|
||||
horizontal.childAlignment = TextAnchor.UpperLeft;
|
||||
}
|
||||
|
||||
horizontal.padding = new RectOffset(layoutData.padding[0], layoutData.padding[1], layoutData.padding[2], layoutData.padding[3]);
|
||||
}
|
||||
|
||||
private static void CreateScrollRect(GameObject obj, ScrollRectData data)
|
||||
{
|
||||
if (data == null) return;
|
||||
|
||||
ScrollRect scrollRect = obj.AddComponent<ScrollRect>();
|
||||
scrollRect.horizontal = data.horizontal;
|
||||
scrollRect.vertical = data.vertical;
|
||||
|
||||
try
|
||||
{
|
||||
scrollRect.movementType = (ScrollRect.MovementType)Enum.Parse(typeof(ScrollRect.MovementType), data.movementType);
|
||||
}
|
||||
catch
|
||||
{
|
||||
scrollRect.movementType = ScrollRect.MovementType.Clamped;
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateToggle(GameObject obj, ToggleData data)
|
||||
{
|
||||
if (data == null) return;
|
||||
|
||||
Toggle toggle = obj.AddComponent<Toggle>();
|
||||
toggle.isOn = data.isOn;
|
||||
|
||||
try
|
||||
{
|
||||
toggle.toggleTransition = (Toggle.ToggleTransition)Enum.Parse(typeof(Toggle.ToggleTransition), data.toggleTransition);
|
||||
}
|
||||
catch
|
||||
{
|
||||
toggle.toggleTransition = Toggle.ToggleTransition.Fade;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
toggle.transition = (Selectable.Transition)Enum.Parse(typeof(Selectable.Transition), data.transition);
|
||||
}
|
||||
catch
|
||||
{
|
||||
toggle.transition = Selectable.Transition.ColorTint;
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateSlider(GameObject obj, SliderData data)
|
||||
{
|
||||
if (data == null) return;
|
||||
|
||||
Slider slider = obj.AddComponent<Slider>();
|
||||
slider.minValue = data.minValue;
|
||||
slider.maxValue = data.maxValue;
|
||||
slider.value = data.value;
|
||||
slider.wholeNumbers = data.wholeNumbers;
|
||||
|
||||
try
|
||||
{
|
||||
slider.direction = (Slider.Direction)Enum.Parse(typeof(Slider.Direction), data.direction);
|
||||
}
|
||||
catch
|
||||
{
|
||||
slider.direction = Slider.Direction.LeftToRight;
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateInputField(GameObject obj, InputFieldData data)
|
||||
{
|
||||
if (data == null) return;
|
||||
|
||||
InputField inputField = obj.AddComponent<InputField>();
|
||||
inputField.text = data.text;
|
||||
inputField.characterLimit = data.characterLimit;
|
||||
|
||||
try
|
||||
{
|
||||
inputField.contentType = (InputField.ContentType)Enum.Parse(typeof(InputField.ContentType), data.contentType);
|
||||
}
|
||||
catch
|
||||
{
|
||||
inputField.contentType = InputField.ContentType.Standard;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
inputField.inputType = (InputField.InputType)Enum.Parse(typeof(InputField.InputType), data.inputType);
|
||||
}
|
||||
catch
|
||||
{
|
||||
inputField.inputType = InputField.InputType.Standard;
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateListViewItemPlaceholder(GameObject obj)
|
||||
{
|
||||
}
|
||||
|
||||
private static void CreatePrefab(GameObject root, string path)
|
||||
{
|
||||
string directory = Path.GetDirectoryName(path);
|
||||
if (!Directory.Exists(directory))
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
}
|
||||
|
||||
PrefabUtility.SaveAsPrefabAsset(root, path);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class UILayoutData
|
||||
{
|
||||
public string name;
|
||||
public string type;
|
||||
public int layer;
|
||||
public RectTransformData rectTransform;
|
||||
public CanvasData canvas;
|
||||
public List<UIElementData> children;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class UIElementData
|
||||
{
|
||||
public string name;
|
||||
public string type;
|
||||
public RectTransformData rectTransform;
|
||||
public ImageData image;
|
||||
public ButtonData button;
|
||||
public TextData text;
|
||||
public LayoutGroupData layoutGroup;
|
||||
public ScrollRectData scrollRect;
|
||||
public ToggleData toggle;
|
||||
public SliderData slider;
|
||||
public InputFieldData inputField;
|
||||
public List<CustomComponentData> customComponents;
|
||||
public List<UIElementData> children;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class RectTransformData
|
||||
{
|
||||
public float[] anchorMin;
|
||||
public float[] anchorMax;
|
||||
public float[] position;
|
||||
public float[] sizeDelta;
|
||||
public float[] pivot;
|
||||
public float[] localScale;
|
||||
public float[] localRotation;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class CanvasData
|
||||
{
|
||||
public string renderMode;
|
||||
public bool pixelPerfect;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ImageData
|
||||
{
|
||||
public string sprite;
|
||||
public string type;
|
||||
public float[] color;
|
||||
public bool raycastTarget;
|
||||
public bool preserveAspect;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ButtonData
|
||||
{
|
||||
public bool interactable;
|
||||
public string transition;
|
||||
public string navigationMode;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class TextData
|
||||
{
|
||||
public string text;
|
||||
public float fontSize;
|
||||
public string font;
|
||||
public string materialPreset;
|
||||
public string alignment;
|
||||
public string verticalAlignment;
|
||||
public float[] color;
|
||||
public bool richText;
|
||||
public string overflowMode;
|
||||
public bool enableWordWrapping;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class LayoutGroupData
|
||||
{
|
||||
public string type;
|
||||
public float[] cellSize;
|
||||
public float[] spacing;
|
||||
public int constraint;
|
||||
public string childAlignment;
|
||||
public int[] padding;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ScrollRectData
|
||||
{
|
||||
public bool horizontal;
|
||||
public bool vertical;
|
||||
public string movementType;
|
||||
public string horizontalScrollbarVisibility;
|
||||
public string verticalScrollbarVisibility;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ToggleData
|
||||
{
|
||||
public bool isOn;
|
||||
public string toggleTransition;
|
||||
public string transition;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SliderData
|
||||
{
|
||||
public float minValue;
|
||||
public float maxValue;
|
||||
public float value;
|
||||
public bool wholeNumbers;
|
||||
public string direction;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class InputFieldData
|
||||
{
|
||||
public string text;
|
||||
public string placeholder;
|
||||
public int characterLimit;
|
||||
public string contentType;
|
||||
public string inputType;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class CustomComponentData
|
||||
{
|
||||
public string typeName;
|
||||
}
|
||||
11
modules/frameworks/编辑器工具/UI工具/UIPrefabGenerator.cs.meta
Normal file
11
modules/frameworks/编辑器工具/UI工具/UIPrefabGenerator.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 31deea0e57bec2f4893548b0a41853cb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
366
modules/frameworks/编辑器工具/UI工具/UIPrefabToJson.cs
Normal file
366
modules/frameworks/编辑器工具/UI工具/UIPrefabToJson.cs
Normal file
@@ -0,0 +1,366 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEditor;
|
||||
using TMPro;
|
||||
|
||||
public static class UIPrefabToJson
|
||||
{
|
||||
public static void ExportPrefabToJson(string prefabPath, string jsonPath = null)
|
||||
{
|
||||
if (!AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath))
|
||||
{
|
||||
Debug.LogError("Prefab not found: " + prefabPath);
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(jsonPath))
|
||||
{
|
||||
jsonPath = prefabPath.Replace(".prefab", ".json");
|
||||
}
|
||||
|
||||
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
|
||||
UILayoutData layoutData = ConvertPrefabToLayoutData(prefab);
|
||||
|
||||
string jsonContent = JsonUtility.ToJson(layoutData, true);
|
||||
string fullPath = Path.Combine(Application.dataPath, "..", jsonPath);
|
||||
string directory = Path.GetDirectoryName(fullPath);
|
||||
|
||||
if (!Directory.Exists(directory))
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
}
|
||||
|
||||
File.WriteAllText(fullPath, jsonContent);
|
||||
AssetDatabase.Refresh();
|
||||
|
||||
Debug.Log("JSON exported successfully: " + jsonPath);
|
||||
}
|
||||
|
||||
private static UILayoutData ConvertPrefabToLayoutData(GameObject prefab)
|
||||
{
|
||||
UILayoutData data = new UILayoutData();
|
||||
data.name = prefab.name;
|
||||
data.type = "Canvas";
|
||||
data.layer = prefab.layer;
|
||||
|
||||
Canvas canvas = prefab.GetComponent<Canvas>();
|
||||
if (canvas != null)
|
||||
{
|
||||
data.canvas = new CanvasData();
|
||||
data.canvas.renderMode = canvas.renderMode.ToString();
|
||||
data.canvas.pixelPerfect = canvas.pixelPerfect;
|
||||
}
|
||||
|
||||
RectTransform rectTransform = prefab.GetComponent<RectTransform>();
|
||||
if (rectTransform != null)
|
||||
{
|
||||
data.rectTransform = ConvertRectTransform(rectTransform);
|
||||
}
|
||||
|
||||
data.children = new List<UIElementData>();
|
||||
foreach (Transform child in prefab.transform)
|
||||
{
|
||||
UIElementData childData = ConvertTransformToElement(child);
|
||||
if (childData != null)
|
||||
{
|
||||
data.children.Add(childData);
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private static UIElementData ConvertTransformToElement(Transform transform)
|
||||
{
|
||||
UIElementData data = new UIElementData();
|
||||
data.name = transform.name;
|
||||
|
||||
RectTransform rectTransform = transform.GetComponent<RectTransform>();
|
||||
if (rectTransform != null)
|
||||
{
|
||||
data.rectTransform = ConvertRectTransform(rectTransform);
|
||||
}
|
||||
|
||||
Component[] components = transform.GetComponents<Component>();
|
||||
List<string> componentTypes = new List<string>();
|
||||
|
||||
foreach (Component comp in components)
|
||||
{
|
||||
if (comp == null) continue;
|
||||
|
||||
string compTypeName = comp.GetType().Name;
|
||||
componentTypes.Add(compTypeName);
|
||||
|
||||
switch (compTypeName)
|
||||
{
|
||||
case "Image":
|
||||
case "SFUI_Image":
|
||||
data.type = compTypeName;
|
||||
data.image = ConvertImage((Image)comp);
|
||||
break;
|
||||
case "Button":
|
||||
case "SFUI_Button":
|
||||
data.type = compTypeName;
|
||||
data.button = ConvertButton((Button)comp);
|
||||
break;
|
||||
case "TextMeshProUGUI":
|
||||
case "SFUI_TextMeshProUGUI":
|
||||
data.type = compTypeName;
|
||||
data.text = ConvertText((TextMeshProUGUI)comp);
|
||||
break;
|
||||
case "ScrollRect":
|
||||
case "SFUI_ScrollRect":
|
||||
data.type = compTypeName;
|
||||
data.scrollRect = ConvertScrollRect((ScrollRect)comp);
|
||||
break;
|
||||
case "GridLayoutGroup":
|
||||
data.type = compTypeName;
|
||||
data.layoutGroup = ConvertGridLayoutGroup((GridLayoutGroup)comp);
|
||||
break;
|
||||
case "VerticalLayoutGroup":
|
||||
data.type = compTypeName;
|
||||
data.layoutGroup = ConvertVerticalLayoutGroup((VerticalLayoutGroup)comp);
|
||||
break;
|
||||
case "HorizontalLayoutGroup":
|
||||
data.type = compTypeName;
|
||||
data.layoutGroup = ConvertHorizontalLayoutGroup((HorizontalLayoutGroup)comp);
|
||||
break;
|
||||
case "Toggle":
|
||||
case "SFUI_Toggle":
|
||||
data.type = compTypeName;
|
||||
data.toggle = ConvertToggle((Toggle)comp);
|
||||
break;
|
||||
case "Slider":
|
||||
case "SFUI_Slider":
|
||||
data.type = compTypeName;
|
||||
data.slider = ConvertSlider((Slider)comp);
|
||||
break;
|
||||
case "InputField":
|
||||
case "SFUI_InputField":
|
||||
data.type = compTypeName;
|
||||
data.inputField = ConvertInputField((InputField)comp);
|
||||
break;
|
||||
case "RectTransform":
|
||||
if (string.IsNullOrEmpty(data.type))
|
||||
data.type = compTypeName;
|
||||
break;
|
||||
case "Canvas":
|
||||
if (string.IsNullOrEmpty(data.type))
|
||||
data.type = compTypeName;
|
||||
break;
|
||||
case "CanvasScaler":
|
||||
data.type = compTypeName;
|
||||
break;
|
||||
case "GraphicRaycaster":
|
||||
data.type = compTypeName;
|
||||
break;
|
||||
default:
|
||||
if (compTypeName != "Transform" && compTypeName != "RectTransform")
|
||||
{
|
||||
if (data.customComponents == null)
|
||||
data.customComponents = new List<CustomComponentData>();
|
||||
data.customComponents.Add(ConvertCustomComponent(comp));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(data.type))
|
||||
{
|
||||
data.type = "Transform";
|
||||
}
|
||||
|
||||
if (transform.childCount > 0)
|
||||
{
|
||||
data.children = new List<UIElementData>();
|
||||
foreach (Transform child in transform)
|
||||
{
|
||||
UIElementData childData = ConvertTransformToElement(child);
|
||||
if (childData != null)
|
||||
{
|
||||
data.children.Add(childData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private static RectTransformData ConvertRectTransform(RectTransform rect)
|
||||
{
|
||||
RectTransformData data = new RectTransformData();
|
||||
data.anchorMin = new float[] { rect.anchorMin.x, rect.anchorMin.y };
|
||||
data.anchorMax = new float[] { rect.anchorMax.x, rect.anchorMax.y };
|
||||
data.position = new float[] { rect.anchoredPosition3D.x, rect.anchoredPosition3D.y, rect.anchoredPosition3D.z };
|
||||
data.sizeDelta = new float[] { rect.sizeDelta.x, rect.sizeDelta.y };
|
||||
data.pivot = new float[] { rect.pivot.x, rect.pivot.y };
|
||||
data.localScale = new float[] { rect.localScale.x, rect.localScale.y, rect.localScale.z };
|
||||
data.localRotation = new float[] { rect.localEulerAngles.x, rect.localEulerAngles.y, rect.localEulerAngles.z };
|
||||
return data;
|
||||
}
|
||||
|
||||
private static ImageData ConvertImage(Image image)
|
||||
{
|
||||
ImageData data = new ImageData();
|
||||
data.type = image.type.ToString();
|
||||
data.color = new float[] { image.color.r, image.color.g, image.color.b, image.color.a };
|
||||
data.raycastTarget = image.raycastTarget;
|
||||
data.preserveAspect = image.preserveAspect;
|
||||
|
||||
if (image.sprite != null)
|
||||
{
|
||||
string spritePath = AssetDatabase.GetAssetPath(image.sprite);
|
||||
if (!string.IsNullOrEmpty(spritePath))
|
||||
{
|
||||
data.sprite = spritePath;
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private static ButtonData ConvertButton(Button button)
|
||||
{
|
||||
ButtonData data = new ButtonData();
|
||||
data.interactable = button.interactable;
|
||||
data.transition = button.transition.ToString();
|
||||
data.navigationMode = button.navigation.mode.ToString();
|
||||
return data;
|
||||
}
|
||||
|
||||
private static TextData ConvertText(TextMeshProUGUI text)
|
||||
{
|
||||
TextData data = new TextData();
|
||||
data.text = text.text;
|
||||
data.fontSize = text.fontSize;
|
||||
data.color = new float[] { text.color.r, text.color.g, text.color.b, text.color.a };
|
||||
data.alignment = text.alignment.ToString();
|
||||
data.verticalAlignment = text.verticalAlignment.ToString();
|
||||
data.richText = text.richText;
|
||||
data.overflowMode = text.overflowMode.ToString();
|
||||
data.enableWordWrapping = text.enableWordWrapping;
|
||||
|
||||
if (text.font != null)
|
||||
{
|
||||
string fontPath = AssetDatabase.GetAssetPath(text.font);
|
||||
if (!string.IsNullOrEmpty(fontPath))
|
||||
{
|
||||
data.font = fontPath;
|
||||
}
|
||||
}
|
||||
|
||||
if (text.material != null)
|
||||
{
|
||||
string materialPath = AssetDatabase.GetAssetPath(text.material);
|
||||
if (!string.IsNullOrEmpty(materialPath))
|
||||
{
|
||||
data.materialPreset = materialPath;
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private static ScrollRectData ConvertScrollRect(ScrollRect scrollRect)
|
||||
{
|
||||
ScrollRectData data = new ScrollRectData();
|
||||
data.horizontal = scrollRect.horizontal;
|
||||
data.vertical = scrollRect.vertical;
|
||||
data.movementType = scrollRect.movementType.ToString();
|
||||
data.horizontalScrollbarVisibility = scrollRect.horizontalScrollbarVisibility.ToString();
|
||||
data.verticalScrollbarVisibility = scrollRect.verticalScrollbarVisibility.ToString();
|
||||
return data;
|
||||
}
|
||||
|
||||
private static LayoutGroupData ConvertGridLayoutGroup(GridLayoutGroup layout)
|
||||
{
|
||||
LayoutGroupData data = new LayoutGroupData();
|
||||
data.type = "GridLayoutGroup";
|
||||
data.cellSize = new float[] { layout.cellSize.x, layout.cellSize.y };
|
||||
data.spacing = new float[] { layout.spacing.x, layout.spacing.y };
|
||||
data.constraint = (int)layout.constraint;
|
||||
data.childAlignment = layout.childAlignment.ToString();
|
||||
data.padding = new int[] { layout.padding.left, layout.padding.right, layout.padding.top, layout.padding.bottom };
|
||||
return data;
|
||||
}
|
||||
|
||||
private static LayoutGroupData ConvertVerticalLayoutGroup(VerticalLayoutGroup layout)
|
||||
{
|
||||
LayoutGroupData data = new LayoutGroupData();
|
||||
data.type = "VerticalLayoutGroup";
|
||||
data.spacing = new float[] { layout.spacing, layout.spacing };
|
||||
data.childAlignment = layout.childAlignment.ToString();
|
||||
data.padding = new int[] { layout.padding.left, layout.padding.right, layout.padding.top, layout.padding.bottom };
|
||||
return data;
|
||||
}
|
||||
|
||||
private static LayoutGroupData ConvertHorizontalLayoutGroup(HorizontalLayoutGroup layout)
|
||||
{
|
||||
LayoutGroupData data = new LayoutGroupData();
|
||||
data.type = "HorizontalLayoutGroup";
|
||||
data.spacing = new float[] { layout.spacing, layout.spacing };
|
||||
data.childAlignment = layout.childAlignment.ToString();
|
||||
data.padding = new int[] { layout.padding.left, layout.padding.right, layout.padding.top, layout.padding.bottom };
|
||||
return data;
|
||||
}
|
||||
|
||||
private static ToggleData ConvertToggle(Toggle toggle)
|
||||
{
|
||||
ToggleData data = new ToggleData();
|
||||
data.isOn = toggle.isOn;
|
||||
data.toggleTransition = toggle.toggleTransition.ToString();
|
||||
data.transition = toggle.transition.ToString();
|
||||
return data;
|
||||
}
|
||||
|
||||
private static SliderData ConvertSlider(Slider slider)
|
||||
{
|
||||
SliderData data = new SliderData();
|
||||
data.minValue = slider.minValue;
|
||||
data.maxValue = slider.maxValue;
|
||||
data.value = slider.value;
|
||||
data.wholeNumbers = slider.wholeNumbers;
|
||||
data.direction = slider.direction.ToString();
|
||||
return data;
|
||||
}
|
||||
|
||||
private static InputFieldData ConvertInputField(InputField inputField)
|
||||
{
|
||||
InputFieldData data = new InputFieldData();
|
||||
data.text = inputField.text;
|
||||
|
||||
if (inputField.placeholder != null)
|
||||
{
|
||||
Text textPlaceholder = inputField.placeholder as Text;
|
||||
if (textPlaceholder != null)
|
||||
{
|
||||
data.placeholder = textPlaceholder.text;
|
||||
}
|
||||
else
|
||||
{
|
||||
TextMeshProUGUI tmpPlaceholder = inputField.placeholder as TextMeshProUGUI;
|
||||
if (tmpPlaceholder != null)
|
||||
{
|
||||
data.placeholder = tmpPlaceholder.text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data.characterLimit = inputField.characterLimit;
|
||||
data.contentType = inputField.contentType.ToString();
|
||||
data.inputType = inputField.inputType.ToString();
|
||||
return data;
|
||||
}
|
||||
|
||||
private static CustomComponentData ConvertCustomComponent(Component comp)
|
||||
{
|
||||
CustomComponentData data = new CustomComponentData();
|
||||
data.typeName = comp.GetType().Name;
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
11
modules/frameworks/编辑器工具/UI工具/UIPrefabToJson.cs.meta
Normal file
11
modules/frameworks/编辑器工具/UI工具/UIPrefabToJson.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 33906f611de8963438be24b30bc8592e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
248
modules/frameworks/编辑器工具/UI拼接工具.md
Normal file
248
modules/frameworks/编辑器工具/UI拼接工具.md
Normal file
@@ -0,0 +1,248 @@
|
||||
# UI 拼接工具
|
||||
|
||||
## 概述
|
||||
|
||||
UI 拼接工具提供 JSON 配置与 Unity 预制体的双向转换,帮助 UI 相关人员通过 JSON 配置文件快速生成 UI 预制体,同时支持从现有预制体反向导出 JSON 用于版本控制或重新生成。
|
||||
|
||||
## 核心组件
|
||||
|
||||
| 组件 | 职责 | 文件路径 |
|
||||
|-----|------|---------|
|
||||
| **UIPrefabGenerator** | 解析 JSON 并生成 UI 预制体 | `Assets/Editor/Command/UIPrefabGenerator.cs` |
|
||||
| **UIPrefabToJson** | 从预制体反向导出 JSON | `Assets/Editor/Command/UIPrefabToJson.cs` |
|
||||
| **RemoteCommand** | 提供外部调用接口 | `Assets/Editor/Command/RemoteCommand.cs` |
|
||||
| **ExternalCommandListener** | 监听外部命令触发文件 | `Assets/Editor/Command/ExternalCommandListener.cs` |
|
||||
|
||||
工具脚本在文档库中的位置(已并入编辑器工具模块):
|
||||
|
||||
```
|
||||
modules/frameworks/编辑器工具/UI工具/
|
||||
├── ExternalCommandListener.cs # 外部命令监听器
|
||||
├── RemoteCommand.cs # 远程命令接口
|
||||
├── UIPrefabGenerator.cs # JSON 转预制体生成器
|
||||
└── UIPrefabToJson.cs # 预制体转 JSON 导出器
|
||||
```
|
||||
|
||||
## JSON 配置文件结构
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "BagWindow",
|
||||
"width": 1024,
|
||||
"height": 768,
|
||||
"elements": [
|
||||
{
|
||||
"type": "Image",
|
||||
"name": "BgPanel",
|
||||
"anchorMin": [0, 0],
|
||||
"anchorMax": [1, 1],
|
||||
"position": [0, 0, 0],
|
||||
"sizeDelta": [0, 0],
|
||||
"sprite": "Assets/Game/GameHFResource/CN/UIWindows/BagWindow/Sprites/Bg.png"
|
||||
},
|
||||
{
|
||||
"type": "Text",
|
||||
"name": "TitleText",
|
||||
"anchorMin": [0.5, 0.9],
|
||||
"anchorMax": [0.5, 0.9],
|
||||
"position": [0, 0, 0],
|
||||
"sizeDelta": [200, 40],
|
||||
"text": "背包",
|
||||
"font": "Assets/Game/GameHFResource/CN/Fonts/FZY4K-M05S SDF.asset",
|
||||
"fontSize": 28,
|
||||
"color": [1, 1, 1, 1]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 支持的 UI 元素类型
|
||||
|
||||
| 类型 | 对应 Unity 组件 | 关键属性 |
|
||||
|-----|-------------|---------|
|
||||
| **Image** | Image | sprite, color, type, preserveAspect |
|
||||
| **TextMeshProUGUI** | TextMeshProUGUI | text, font, fontSize, color, alignment |
|
||||
| **Button** | Button | interactable, transition, navigationMode |
|
||||
| **Slider** | Slider | minValue, maxValue, value, wholeNumbers, direction |
|
||||
| **Toggle** | Toggle | isOn, toggleTransition, transition |
|
||||
| **InputField** | InputField | text, placeholder, characterLimit, contentType |
|
||||
| **ScrollRect** | ScrollRect | horizontal, vertical, movementType |
|
||||
| **GridLayoutGroup** | GridLayoutGroup | cellSize, spacing, constraint, padding |
|
||||
| **VerticalLayoutGroup** | VerticalLayoutGroup | spacing, childAlignment, padding |
|
||||
| **HorizontalLayoutGroup** | HorizontalLayoutGroup | spacing, childAlignment, padding |
|
||||
|
||||
## 外部命令触发机制
|
||||
|
||||
### 触发方式
|
||||
|
||||
通过创建/修改 `Trigger_Command.txt` 文件触发 UI 生成:
|
||||
|
||||
```
|
||||
GenerateUIPrefab:Assets/Editor/UI/BagWindow.json
|
||||
```
|
||||
|
||||
### 命令格式
|
||||
|
||||
```
|
||||
<命令名>:<参数1>:<参数2>:...
|
||||
```
|
||||
|
||||
**支持的命令**:
|
||||
|
||||
| 命令 | 参数 | 说明 |
|
||||
|-----|------|------|
|
||||
| **GenerateUIPrefab** | `jsonPath` [, `prefabPath`] | 根据 JSON 生成 UI 预制体 |
|
||||
| **ExportPrefabToJson** | `prefabPath` [, `jsonPath`] | 从预制体反向导出 JSON |
|
||||
| **ReadJson** | [参数列表] | 读取 JSON 配置(测试用) |
|
||||
|
||||
### 完整执行流程
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ 外部系统写入命令到 Trigger_Command.txt │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ ExternalCommandListener (FileSystemWatcher) 检测到文件变化 │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ 解析命令格式:GenerateUIPrefab:Assets/Editor/UI/BagWindow.json │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ 通过反射调用 RemoteCommand.GenerateUIPrefab(string[] args) │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ UIPrefabGenerator.GeneratePrefabFromJson(jsonPath, prefabPath) │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ 生成预制体到指定路径:Assets/Game/GameHFResource/CN/UIWindows/... │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 方式一:外部命令触发
|
||||
|
||||
```bash
|
||||
# 正向:从 JSON 生成预制体
|
||||
echo "GenerateUIPrefab:Assets/Editor/UI/BagWindow.json" > "F:\JMNet\CrystalBattle_Client\Trigger_Command.txt"
|
||||
|
||||
# 反向:从预制体导出 JSON
|
||||
echo "ExportPrefabToJson:Assets/Game/GameHFResource/CN/UIWindows/BagWindow/BagWindow.prefab" > "F:\JMNet\CrystalBattle_Client\Trigger_Command.txt"
|
||||
```
|
||||
|
||||
### 方式二:Unity 编辑器菜单
|
||||
|
||||
**正向生成**:
|
||||
1. 打开 Unity 编辑器
|
||||
2. 点击菜单 `Tools > External > Test GenerateUIPrefab`
|
||||
3. 查看 Console 窗口日志确认执行结果
|
||||
|
||||
**反向导出**:
|
||||
1. 打开 Unity 编辑器
|
||||
2. 点击菜单 `Tools > UI > Export Prefab to JSON`
|
||||
3. 选择要导出的预制体文件
|
||||
|
||||
### 方式三:代码调用
|
||||
|
||||
```csharp
|
||||
// 正向:从 JSON 生成预制体
|
||||
UIPrefabGenerator.GeneratePrefabFromJson(
|
||||
"Assets/Editor/UI/BagWindow.json",
|
||||
"Assets/Game/GameHFResource/CN/UIWindows/BagWindow/BagWindow.prefab"
|
||||
);
|
||||
|
||||
// 反向:从预制体导出 JSON
|
||||
UIPrefabToJson.ExportPrefabToJson(
|
||||
"Assets/Game/GameHFResource/CN/UIWindows/BagWindow/BagWindow.prefab",
|
||||
"Assets/Editor/UI/BagWindow.json"
|
||||
);
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **路径格式**:JSON 中的资源路径使用 `/` 分隔符,且为 Unity 项目相对路径
|
||||
2. **资源引用**:确保 JSON 中引用的 Sprite、Font 等资源已存在于项目中
|
||||
3. **编码格式**:JSON 文件使用 UTF-8 编码
|
||||
4. **坐标系统**:使用 Unity RectTransform 的 anchorMin/anchorMax/position/sizeDelta 四元组
|
||||
5. **文件锁定**:生成前确保目标预制体文件未被其他进程占用
|
||||
|
||||
## 部署步骤
|
||||
|
||||
### 方法一:手动复制
|
||||
|
||||
1. 复制 `modules/frameworks/编辑器工具/UI工具/` 目录下的所有文件
|
||||
2. 粘贴到目标项目的 `Assets/Editor/Command/` 目录中
|
||||
3. 确保目录结构如下:
|
||||
```
|
||||
Assets/Editor/Command/
|
||||
├── ExternalCommandListener.cs
|
||||
├── RemoteCommand.cs
|
||||
├── UIPrefabGenerator.cs
|
||||
└── UIPrefabToJson.cs
|
||||
```
|
||||
|
||||
### 方法二:自动部署脚本
|
||||
|
||||
创建部署脚本 `DeployUITools.bat`:
|
||||
|
||||
```batch
|
||||
@echo off
|
||||
set "SOURCE_DIR=E:\AI\UnityAI\modules\frameworks\编辑器工具\UI工具"
|
||||
set "TARGET_DIR=%cd%\Assets\Editor\Command"
|
||||
|
||||
mkdir "%TARGET_DIR%" 2>nul
|
||||
xcopy "%SOURCE_DIR%\*.cs" "%TARGET_DIR%\" /y
|
||||
xcopy "%SOURCE_DIR%\*.cs.meta" "%TARGET_DIR%\" /y
|
||||
|
||||
echo UI 工具部署完成
|
||||
pause
|
||||
```
|
||||
|
||||
### 部署检查项
|
||||
|
||||
| 检查项 | 要求 |
|
||||
|-------|------|
|
||||
| **目标路径** | 必须放置在 `Assets/Editor/Command/` 目录下 |
|
||||
| **文件完整性** | 必须包含所有 4 个 cs 文件及其 meta 文件 |
|
||||
| **Unity 版本** | 支持 Unity 2020 及以上版本 |
|
||||
| **依赖组件** | 需要导入 TextMeshPro 包 |
|
||||
|
||||
### 验证部署
|
||||
|
||||
部署完成后,在 Unity 编辑器中验证:
|
||||
|
||||
1. 打开 Unity 编辑器
|
||||
2. 检查菜单 `Tools > UI` 是否存在
|
||||
3. 检查菜单 `Tools > External` 是否存在
|
||||
4. 在 Console 窗口确认无编译错误
|
||||
|
||||
---
|
||||
|
||||
**版本**: v1.2
|
||||
**适用**: StrayFog 框架 UI 拼接开发人员
|
||||
|
||||
---
|
||||
|
||||
## 文档关联
|
||||
|
||||
> 当本文档发生变更时,应同步检查以下关联文档,避免信息不一致。
|
||||
|
||||
| 文档 | 关联原因 |
|
||||
|---|---|
|
||||
| [编辑器工具入口](README.md) | 本文件是编辑器工具模块的子文档 |
|
||||
| [UI 窗口系统](../UI窗口系统/README.md) | UI 拼接工具服务于 UI 窗口系统 |
|
||||
| [UI 界面拼接设计指南](../UI窗口系统/界面拼接.md) | 设计指南说明 JSON 驱动的界面拼接原则 |
|
||||
| [modules/模块索引.md](../../模块索引.md) | 模块索引是项目知识总入口 |
|
||||
| [rules/10-架构说明.md](../../../rules/10-架构说明.md) | 架构说明定义编辑器工具层与运行时层边界 |
|
||||
| [rules/20-开发规则.md](../../../rules/20-开发规则.md) | 编辑器工具开发规则互相引用 |
|
||||
| [rules/30-模块登记规则.md](../../../rules/30-模块登记规则.md) | 模块登记规则定义分类和状态口径 |
|
||||
121
modules/frameworks/网络系统.md
Normal file
121
modules/frameworks/网络系统.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# 网络系统
|
||||
|
||||
## 分类
|
||||
|
||||
- 核心框架
|
||||
|
||||
## 目的
|
||||
|
||||
提供 WebSocket 连接、protobuf 消息收发、消息 ID / route 映射和错误响应处理。
|
||||
|
||||
---
|
||||
|
||||
## 核心路径
|
||||
|
||||
- protobuf 源:`GoogleProtobufNetwork/`
|
||||
- 生成输出:`Assets/Game/GameHFScripte/CopyToHF/Network/`
|
||||
- WebSocket:`Assets/Game/GameHFScripte/GameFunction/WebSocket/`
|
||||
|
||||
---
|
||||
|
||||
## 核心类
|
||||
|
||||
| 类 | 职责 |
|
||||
|---|---|
|
||||
| `HFSeverManager` | 网络管理器单例 |
|
||||
| `WSSession` | WebSocket 会话 |
|
||||
| `HFM_Socket_WS` | WebSocket 底层连接 |
|
||||
| `HFWebSocket_Manager_AutoMsgMap` | 自动生成消息映射 |
|
||||
| `HFNetWorkMsgIdMap` | 网络消息 ID 映射 |
|
||||
| `MessagePage` | 数据包编解码 |
|
||||
|
||||
---
|
||||
|
||||
## C2S 发送链路
|
||||
|
||||
```text
|
||||
IMessage.Dispatch()
|
||||
-> HFSeverManager.Instance.SendMsg(message)
|
||||
-> HFSeverManager.OnAuto_OnGetProtocNo(message)
|
||||
-> HFSeverManager.OnAuto_OnGetRoute(Network_MsgId)
|
||||
-> m_Session.SendMessage(protoNo, route, PacketType.Data, Request, message)
|
||||
```
|
||||
|
||||
`IMessage.Dispatch()` 定义在 `Assets/Game/GameHFScripte/GameFunction/WebSocket/Extends/HFServer_Extend.cs`。
|
||||
|
||||
---
|
||||
|
||||
## WebSocket 连接流程
|
||||
|
||||
```text
|
||||
登录管理器.ConnectServer
|
||||
-> HFSeverManager.Connect(MainConnect, WebSocket, ip, port, token)
|
||||
-> WSSession.Connect
|
||||
-> HFM_Socket_WS.Connect
|
||||
-> Handshake -> HandshakeAck -> 连接成功回调
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## S2C 收包和派发
|
||||
|
||||
```text
|
||||
WSSession.ReceiveMessages(PacketType.Data)
|
||||
-> MessagePage.Decode(packet.Body)
|
||||
-> Response.MessageId / Push.Route -> Network_MsgId
|
||||
-> HFSeverManager.OnAuto_OnMergeFrom(msgid, body)
|
||||
-> protobuf Parser.ParseFrom
|
||||
-> 反射创建 HFEvent_STC_{MessageTypeName}
|
||||
-> 写入 Request / ErrorCode
|
||||
-> AbsHFEventArg<enHFSTCEvent>.Dispatch()
|
||||
-> HFEventManager.Instance.STCEvent.Dispatch(arg)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 自动生成映射
|
||||
|
||||
- `HFWebSocket_Manager_AutoMsgMap.cs`:提供 `OnAuto_OnGetProtocNo`、`OnAuto_OnGetRoute`、`OnAuto_OnMergeFrom`、`msrDicRouteToMsg`、S2C/C2S 类型字典。
|
||||
- `HFNetWorkMsgIdMap.cs`:为 protobuf partial class 标记 `[HFNetworkMsg(...)]`。
|
||||
|
||||
---
|
||||
|
||||
## 使用规则
|
||||
|
||||
- 新增协议先改 `.proto`、`msg.proto`、`route.txt`。
|
||||
- 不手动修改生成输出。
|
||||
- 不硬编码 route 或数字 msgId。
|
||||
- UI 监听 S2C 必须在 `OnClose` 移除。
|
||||
- 错误响应会触发 `HFEvent_ErrorCode`。
|
||||
|
||||
---
|
||||
|
||||
## 协议变更清单
|
||||
|
||||
1. 修改 `.proto` 和 `msg.proto`。
|
||||
2. 登记 msgId / route。
|
||||
3. 重新生成 C# 代码。
|
||||
4. 检查 `HFNetWorkMsgIdMap.cs` / `HFWebSocket_Manager_AutoMsgMap.cs`。
|
||||
5. 更新业务入口和监听。
|
||||
6. 验证。
|
||||
|
||||
---
|
||||
|
||||
## 相关模块
|
||||
|
||||
- [事件系统](事件系统/README.md)
|
||||
- [数据/配置框架](数据配置框架.md)
|
||||
|
||||
---
|
||||
|
||||
## 文档关联
|
||||
|
||||
> 当本文档发生变更时,应同步检查以下关联文档,避免信息不一致。
|
||||
|
||||
| 文档 | 关联原因 |
|
||||
|---|---|
|
||||
| [modules/模块索引.md](../模块索引.md) | 模块索引是项目知识总入口 |
|
||||
| [modules/frameworks/事件系统/README.md](事件系统/README.md) | S2C 消息派发为 STC 事件 |
|
||||
| [modules/frameworks/数据配置框架.md](数据配置框架.md) | 配置表与网络消息可能联动 |
|
||||
| [rules/10-架构说明.md](../../rules/10-架构说明.md) | 架构说明定义总体分层和依赖方向 |
|
||||
| [rules/30-模块登记规则.md](../../rules/30-模块登记规则.md) | 模块登记规则定义分类和状态口径 |
|
||||
136
modules/frameworks/资源管理/README.md
Normal file
136
modules/frameworks/资源管理/README.md
Normal file
@@ -0,0 +1,136 @@
|
||||
# 资源管理
|
||||
|
||||
## 分类
|
||||
|
||||
- 核心框架
|
||||
|
||||
## 目的
|
||||
|
||||
管理 AssetBundle、语言资源、图集、prefab、DllBytes 等资源的加载、缓存和释放。
|
||||
|
||||
---
|
||||
|
||||
## 核心路径
|
||||
|
||||
- AssetBundle:`Assets/Game/GameHFScripte/CopyToHF/AssetBundle/`
|
||||
- 图集:`Assets/Game/GameHFScripte/CopyToHF/SpriteAtlas/`
|
||||
- 资源目录:`Assets/Game/GameHFResource/CN/`、`EN/`、`DllBytes/`
|
||||
|
||||
---
|
||||
|
||||
## 核心类
|
||||
|
||||
| 类 | 职责 |
|
||||
|---|---|
|
||||
| `HFABMgr` | AssetBundle 资源管理器单例 |
|
||||
| `HFAssetLoader` | 具体资源加载器 |
|
||||
| `HFAssetRequest` | 资源加载请求 |
|
||||
| `HFAssetBundlePathAttribute` | 资源路径属性,标记资源根目录、相对路径、语言包规则等 |
|
||||
| `HFGlobal_SpriteAtlas` | 图集全局管理 |
|
||||
|
||||
---
|
||||
|
||||
## 资源路径规则
|
||||
|
||||
`HFAssetBundlePathAttribute` 记录:
|
||||
|
||||
- 资源根目录
|
||||
- 相对路径
|
||||
- 是否忽略语言包
|
||||
- 是否场景
|
||||
- 线程优先级
|
||||
|
||||
`ChangeLanguage` 生成:
|
||||
|
||||
- `editorPath`
|
||||
- `assetBundlePath`
|
||||
- `assetBundleKey`
|
||||
- `assetName`
|
||||
|
||||
`HFABMgr.OnCheckAssetBundlePathAttributeLanguage` 在当前语言无资源时回退默认语言。
|
||||
|
||||
---
|
||||
|
||||
## 加载流程
|
||||
|
||||
```text
|
||||
HFABMgr.LoadInMemory(pathAttr)
|
||||
-> OnReadAssetBundleManifest()
|
||||
-> OnCheckAssetBundlePathAttributeLanguage()
|
||||
-> new HFAssetRequest(...)
|
||||
-> OnStartLoad()
|
||||
-> 获取或创建 HFAssetLoader
|
||||
-> HFAssetLoader.LoadAsset()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 加载模式
|
||||
|
||||
### Editor 模式
|
||||
|
||||
```text
|
||||
enHFAbLoadModel.Editor
|
||||
-> SFAssetUtility_Unity.LoadFromFile(editorPath)
|
||||
-> 直接读取编辑器资源
|
||||
```
|
||||
|
||||
### 非 Editor 模式
|
||||
|
||||
```text
|
||||
读取 AssetBundleManifest
|
||||
-> 解析依赖
|
||||
-> AssetBundle.LoadFromMemoryAsync 读取主资源
|
||||
```
|
||||
|
||||
### CDN / 边玩边下
|
||||
|
||||
```text
|
||||
缺失资源进入 WaitDownload
|
||||
-> HFAssetsDownLoadManager 下载
|
||||
-> HFABMgr.SaveFile 保存到本地
|
||||
-> 加载完成
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 图集加载
|
||||
|
||||
```text
|
||||
SpriteAtlasManager.atlasRequested
|
||||
-> HFGlobal_SpriteAtlas.Instance.GetSpriteAtlasPath(spriteAtlasName)
|
||||
-> HFABMgr.OnLoadInMemory(spriteAtlasAttribute)
|
||||
-> callback(SpriteAtlas)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 使用规则
|
||||
|
||||
- 资源路径统一通过 `HFAssetBundlePathAttribute` 配置。
|
||||
- 加载资源统一走 `HFFunCatalogue.Asset.LoadAsset()` 或 `HFABMgr.LoadInMemory()`。
|
||||
- 语言资源按 `GameHFResource/<lang>/` 分目录存放。
|
||||
- 图集按业务或 UI 模块划分。
|
||||
|
||||
---
|
||||
|
||||
## 相关模块
|
||||
|
||||
- [UI 窗口系统](../UI窗口系统/README.md)
|
||||
- [实体系统](../实体系统/README.md)
|
||||
- [场景系统](../场景系统.md)
|
||||
|
||||
---
|
||||
|
||||
## 文档关联
|
||||
|
||||
> 当本文档发生变更时,应同步检查以下关联文档,避免信息不一致。
|
||||
|
||||
| 文档 | 关联原因 |
|
||||
|---|---|
|
||||
| [modules/模块索引.md](../../模块索引.md) | 模块索引是项目知识总入口 |
|
||||
| [modules/frameworks/UI窗口系统/README.md](../UI窗口系统/README.md) | UI 资源加载依赖资源管理 |
|
||||
| [modules/frameworks/实体系统/README.md](../实体系统/README.md) | 实体资源加载依赖资源管理 |
|
||||
| [modules/frameworks/场景系统.md](../场景系统.md) | 场景资源加载依赖资源管理 |
|
||||
| [rules/10-架构说明.md](../../../rules/10-架构说明.md) | 架构说明定义总体分层和依赖方向 |
|
||||
| [rules/30-模块登记规则.md](../../../rules/30-模块登记规则.md) | 模块登记规则定义分类和状态口径 |
|
||||
102
modules/frameworks/资源管理/使用指南.md
Normal file
102
modules/frameworks/资源管理/使用指南.md
Normal file
@@ -0,0 +1,102 @@
|
||||
# 资源管理使用指南
|
||||
|
||||
## 一、AssetBundle 资源加载
|
||||
|
||||
### 1.1 加载资源示例
|
||||
|
||||
```csharp
|
||||
public class ResourceLoader : MonoBehaviour
|
||||
{
|
||||
public void LoadPlayerModel(int resId, Action<GameObject> onLoaded)
|
||||
{
|
||||
// 通过 HFFunCatalogue 统一入口加载资源
|
||||
// resId: 从 Res 配置表中读取的资源 ID
|
||||
HFFunCatalogue.Asset.LoadAsset(resId, (res, obj) =>
|
||||
{
|
||||
if (obj != null)
|
||||
{
|
||||
GameObject model = res.GetAsset<GameObject>();
|
||||
onLoaded?.Invoke(model);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"加载失败: resId={resId}");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 1.2 实体资源加载示例
|
||||
|
||||
```csharp
|
||||
// 在 Entity 中加载资源
|
||||
HFFunCatalogue.Asset.LoadAsset(data.EntityRes.Id, (res, obj) => {
|
||||
if (!entity.isDeath)
|
||||
{
|
||||
var Boddy = res.GetAsset<GameObject>();
|
||||
data.SetBoddy(Boddy);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、资源管理 API
|
||||
|
||||
### 2.1 常用方法
|
||||
|
||||
| 类别 | 管理器 | 常用方法 |
|
||||
|-----|-------|---------|
|
||||
| **资源** | `HFABMgr` | `LoadAsset()`, `OnStartLoad()` |
|
||||
|
||||
### 2.2 资源加载流程
|
||||
|
||||
```
|
||||
资源加载流程:
|
||||
OnStartLoad(request)
|
||||
│
|
||||
▼
|
||||
检查缓存 → 获取/创建 HFAssetLoader
|
||||
│
|
||||
▼
|
||||
loader.AddRequest(request)
|
||||
│
|
||||
▼
|
||||
loader.LoadAsset() → 异步加载
|
||||
│
|
||||
▼
|
||||
加载完成 → 回调处理
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、资源管理注意事项
|
||||
|
||||
### 3.1 性能优化建议
|
||||
|
||||
- 及时释放不再使用的资源
|
||||
- 使用异步加载避免卡顿
|
||||
- 合理使用资源缓存
|
||||
|
||||
---
|
||||
|
||||
**版本**: v1.0
|
||||
**生效日期**: 2026年
|
||||
**适用范围**: GiftGameX 项目业务开发人员
|
||||
|
||||
---
|
||||
|
||||
## 文档关联
|
||||
|
||||
> 当本文档发生变更时,应同步检查以下关联文档,避免信息不一致。
|
||||
|
||||
| 文档 | 关联原因 |
|
||||
|---|---|
|
||||
| [资源管理入口](README.md) | 本文件是资源管理模块的子文档 |
|
||||
| [UI 窗口系统](../UI窗口系统/README.md) | UI 资源加载依赖资源管理 |
|
||||
| [实体系统](../实体系统/README.md) | 实体资源加载依赖资源管理 |
|
||||
| [场景系统](../场景系统.md) | 场景资源加载依赖资源管理 |
|
||||
| [modules/模块索引.md](../../模块索引.md) | 模块索引是项目知识总入口 |
|
||||
| [rules/10-架构说明.md](../../../rules/10-架构说明.md) | 架构说明定义总体分层和依赖方向 |
|
||||
| [rules/30-模块登记规则.md](../../../rules/30-模块登记规则.md) | 模块登记规则定义分类和状态口径 |
|
||||
70
modules/frameworks/通用框架/README.md
Normal file
70
modules/frameworks/通用框架/README.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# 通用框架
|
||||
|
||||
## 分类
|
||||
|
||||
- 项目框架
|
||||
|
||||
## 目的
|
||||
|
||||
记录 general/shared 代码、生成框架、StrayFog 核心和跨模块通用能力。
|
||||
|
||||
## 已知线索
|
||||
|
||||
- General:`Assets/Game/GameGeneralScripte/`
|
||||
- 生成 General:`Assets/AutoBuildScript/General/`
|
||||
- StrayFog Core:`Assets/StrayFog/Core/`
|
||||
- 程序集:`GameGeneral`、`ZY.General.ZYCore`、`StrayFogCore`
|
||||
|
||||
## 当前结构
|
||||
|
||||
- `GameGeneralScripte/`:项目通用运行时代码,包含 `CopyToGeneral`、状态机扩展、`GameGeneral.asmdef`、`SEHFCommon.dll`。
|
||||
- `AutoBuildScript/General/`:生成框架的 General 层,包含 `ZYCore`、`ZYXlsx`、`ZYUGUI` 的通用模板和属性。
|
||||
- `StrayFog/Core/`:底层核心程序集和模拟行为桥接,包含 `SFCore.dll`、`SFGlobalData`、`SFAssetUtility_Unity`、`SimulateBehaviour`。
|
||||
- `GameGeneralScripte/CopyToGeneral/AssetBundle/HFAssetBundleHelper.cs`:资源路径、StreamingAssets、PersistentData、CDNAssets、微信小游戏文件读写适配。
|
||||
- `GameGeneralScripte/CopyToGeneral/DllHelper/HFDllHelper.cs`:Hotfix DLL、PDB、HybridCLR AOT DLL bytes 路径和读取。
|
||||
- `GameGeneralScripte/CopyToGeneral/Enum/HFEnumForAssetBundle.cs`:AssetBundle 加载/更新模式枚举。
|
||||
- `GameGeneralScripte/StateMachineBehaviour/`:Animator 状态机扩展和只读属性 Drawer。
|
||||
|
||||
## 边界判断
|
||||
|
||||
- `GameGeneralScripte` 更像项目运行时可依赖的通用层。
|
||||
- `AutoBuildScript/General` 更像代码生成框架的通用模板层。
|
||||
- `StrayFog/Core` 提供更底层的工具、全局路径和模拟行为桥接能力。
|
||||
- 资源框架、配置框架、Hotfix 启动都会依赖这里的通用能力;反向依赖 hotfix 业务逻辑应避免。
|
||||
|
||||
## 梳理任务
|
||||
|
||||
- [x] 梳理 `GameGeneralScripte` 和 `AutoBuildScript/General` 的边界。
|
||||
- [x] 梳理 StrayFog Core 提供的基础能力。
|
||||
- [x] 梳理通用 attributes、enum、dll helper、asset bundle helper。
|
||||
- [x] 梳理哪些功能/框架允许依赖 general/shared。
|
||||
- [x] 梳理生成代码和手写代码的边界。
|
||||
|
||||
## 待确认
|
||||
|
||||
- `StrayFog` 是项目自研框架还是外部框架。
|
||||
- `CopyToGeneral` 的复制/生成来源需要继续追 `AutoBuildScript` 配置。
|
||||
- General 层建议保持底层通用,不依赖 hotfix 业务层。
|
||||
|
||||
## 相关模块
|
||||
|
||||
- [Hotfix 启动/目录](../Hotfix启动.md)
|
||||
- [资源框架](../资源管理/README.md)
|
||||
- [数据/配置框架](../数据配置框架.md)
|
||||
|
||||
---
|
||||
|
||||
## 文档关联
|
||||
|
||||
> 当本文档发生变更时,应同步检查以下关联文档,避免信息不一致。
|
||||
|
||||
| 文档 | 关联原因 |
|
||||
|---|---|
|
||||
| [modules/模块索引.md](../../模块索引.md) | 模块索引是项目知识总入口 |
|
||||
| [modules/frameworks/数据配置框架.md](../数据配置框架.md) | 数据/配置框架 |
|
||||
| [modules/frameworks/Hotfix启动.md](../Hotfix启动.md) | Hotfix 启动/目录 |
|
||||
| [modules/frameworks/资源管理/README.md](../资源管理/README.md) | 资源框架 |
|
||||
| [rules/10-架构说明.md](../../../rules/10-架构说明.md) | 架构说明定义总体分层和依赖方向 |
|
||||
| [rules/20-开发规则.md](../../../rules/20-开发规则.md) | 开发规则与框架/生成规则互相引用 |
|
||||
| [rules/30-模块登记规则.md](../../../rules/30-模块登记规则.md) | 模块登记规则定义分类和状态口径 |
|
||||
| TODO:补充依赖或使用本框架的模块文档 | 如依赖本框架的功能模块 |
|
||||
398
modules/frameworks/通用框架/框架架构.md
Normal file
398
modules/frameworks/通用框架/框架架构.md
Normal file
@@ -0,0 +1,398 @@
|
||||
# StrayFog 框架架构分析
|
||||
|
||||
> 本文档从实现视角分析 StrayFog 框架的整体架构。高层架构规则与程序集边界见 [rules/10-架构说明.md](../../../rules/10-架构说明.md)。
|
||||
|
||||
## 一、框架整体架构
|
||||
|
||||
### 1.1 模块划分
|
||||
|
||||
| 模块层级 | 路径 | 职责描述 |
|
||||
|---------|------|---------|
|
||||
| **Core 层** | `Assets/StrayFog/Core` | 核心基础框架,模拟行为系统、全局数据管理 |
|
||||
| **HF 层** | `Assets/Game/GameHFScripte/CopyToHF` | 游戏核心业务框架,资源管理、事件系统、相机管理等 |
|
||||
| **General 层** | `Assets/Game/GameGeneralScripte` | 通用工具层,状态机、辅助工具等 |
|
||||
|
||||
### 1.2 架构图
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ StrayFog Framework │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ ┌──────────────────┐ ┌──────────────────┐ ┌───────────────┐ │
|
||||
│ │ GameGeneral │ │ GameHF │ │ Core │ │
|
||||
│ │ 状态机/工具类 │ │ 业务核心模块 │ │ 基础框架层 │ │
|
||||
│ └────────┬─────────┘ └────────┬─────────┘ └───────┬───────┘ │
|
||||
├───────────┼─────────────────────┼───────────────────┼───────────┤
|
||||
│ │ │ │ │
|
||||
│ ▼ ▼ ▼ │
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ Unity Engine │ │
|
||||
│ └─────────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、Core 层架构
|
||||
|
||||
### 2.1 SimulateBehaviour 模拟行为系统
|
||||
|
||||
#### 2.1.1 核心接口
|
||||
|
||||
```csharp
|
||||
public interface ISimulateBehaviour
|
||||
{
|
||||
long simulateGlobalId { get; } // 模拟行为全局 ID
|
||||
int bindGameObjectInstanceId { get; } // 绑定对象实例 ID
|
||||
void BindGameObject(GameObject _go); // 绑定 GameObject
|
||||
void UnBindGameObject(); // 解绑 GameObject
|
||||
// ... 完整的 MonoBehaviour 生命周期方法
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.1.2 设计目的
|
||||
|
||||
| 设计目标 | 实现方式 |
|
||||
|---------|---------|
|
||||
| **解耦 MonoBehaviour** | 将逻辑从 MonoBehaviour 中剥离,便于测试和复用 |
|
||||
| **生命周期控制** | 完整模拟 Unity 生命周期,支持自定义调度 |
|
||||
| **对象池优化** | 配合对象池实现高效对象复用 |
|
||||
|
||||
#### 2.1.3 目录结构
|
||||
|
||||
```
|
||||
SimulateBehaviour/
|
||||
├── ISimulateBehaviour.cs # 核心接口定义
|
||||
├── Bind/ # 绑定实现
|
||||
│ ├── IBindSimulateBehaviour.cs
|
||||
│ ├── AbsBindSimulateBehaviour_Mono.cs # Mono 绑定基类
|
||||
│ ├── AbsBindSimulateBehaviour_UI.cs # UI 绑定基类
|
||||
│ └── BindSimulateBehaviour_Collect.cs # 收集器
|
||||
├── MonoBehaviour/ # 各生命周期模拟实现
|
||||
│ ├── SimulateMonoBehaviour_Awake__xxx.cs
|
||||
│ ├── SimulateMonoBehaviour_Update__xxx.cs
|
||||
│ └── ... (40+ 生命周期方法)
|
||||
└── UIBehaviour/ # UI 生命周期模拟
|
||||
├── SimulateUIBehaviour_Awake__xxx.cs
|
||||
└── ...
|
||||
```
|
||||
|
||||
### 2.2 全局数据管理
|
||||
|
||||
**SFGlobalData.cs** - 全局路径和平台管理
|
||||
|
||||
```csharp
|
||||
public sealed class SFGlobalData
|
||||
{
|
||||
public static string assetBundleRoot; // AB 包根路径
|
||||
public static string streamingAssetsRoot; // 流式资源根路径
|
||||
public static string cdnassetBundleRoot; // CDN 资源路径
|
||||
public static RuntimePlatform Platform(); // 当前平台判断
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、HF 层架构
|
||||
|
||||
### 3.1 接口体系
|
||||
|
||||
#### 3.1.1 核心接口继承链
|
||||
|
||||
```
|
||||
IHFObject (基础对象接口)
|
||||
├── IHFRecycle (回收接口)
|
||||
│ └── void Recycle();
|
||||
├── IHFGlobal (全局对象接口)
|
||||
│ └── long globalId { get; }
|
||||
├── IHFClone (克隆接口)
|
||||
├── IHFLanguage (语言接口)
|
||||
├── IHFScene (场景接口)
|
||||
└── IHFSerializable (序列化接口)
|
||||
```
|
||||
|
||||
#### 3.1.2 接口职责表
|
||||
|
||||
| 接口 | 核心职责 | 关键字段/方法 |
|
||||
|-----|---------|--------------|
|
||||
| `IHFObject` | 基础对象标识 | 无 |
|
||||
| `IHFRecycle` | 对象回收机制 | `Recycle()` |
|
||||
| `IHFGlobal` | 全局唯一标识 | `globalId` |
|
||||
| `IHFClone` | 对象克隆能力 | 系列 Clone 方法 |
|
||||
| `IHFLanguage` | 多语言支持 | 语言切换 |
|
||||
| `IHFScene` | 场景管理 | 场景加载/卸载 |
|
||||
|
||||
### 3.2 对象池系统
|
||||
|
||||
#### 3.2.1 HFObjectMemPool - GameObject 对象池
|
||||
|
||||
```csharp
|
||||
public class HFObjectMemPool
|
||||
{
|
||||
private Queue<GameObject> m_UnoccupiedPool; // 空闲队列
|
||||
private Dictionary<int, GameObject> m_UsingPool; // 使用中字典
|
||||
public GameObject GetItem(out bool isnew); // 获取对象
|
||||
public void GiveBack(GameObject item); // 归还对象
|
||||
public void Recycle(); // 完全回收
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.2.2 HFScriptMemPool<T> - 泛型脚本对象池
|
||||
|
||||
```csharp
|
||||
public class HFScriptMemPool<T> : IHFRecycle
|
||||
where T : IHFRecycle
|
||||
{
|
||||
private Queue<T> m_UnoccupiedPool;
|
||||
private List<T> m_UsingPool;
|
||||
// 支持 MonoBehaviour 和普通类的自动创建
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.2.3 对象池设计特点
|
||||
|
||||
| 特性 | 实现方式 | 优势 |
|
||||
|-----|---------|-----|
|
||||
| **双池结构** | 空闲池 + 使用池 | 高效管理对象状态 |
|
||||
| **内存泄漏检测** | 归还时校验来源 | 及时发现问题 |
|
||||
| **泛型支持** | `HFScriptMemPool<T>` | 类型安全 |
|
||||
| **Mono 支持** | 自动创建 GameObject | 无缝集成 Unity |
|
||||
|
||||
### 3.3 事件系统
|
||||
|
||||
#### 3.3.1 核心接口
|
||||
|
||||
```csharp
|
||||
public interface IHFEventHandle<K,T> : IHFRecycle
|
||||
{
|
||||
void AddListener<T1>(K key, Action<T> callback); // 添加监听
|
||||
void RemoveListener<T1>(K key, Action<T> action); // 移除监听
|
||||
void Dispatch(T arg); // 分发事件
|
||||
void RemoveAll(K key); // 清空监听
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.3.2 AbsHFEventHandle 实现机制
|
||||
|
||||
```csharp
|
||||
public abstract class AbsHFEventHandle<K,T> : IHFEventHandle<K,T>
|
||||
where K : struct, Enum
|
||||
where T : IHFEventArg<K>
|
||||
{
|
||||
// 多级索引: EventId -> TypeHashCode -> Action 列表
|
||||
HFMultiKeyDictionary<K, int, IHFCacheEventAction<K,T>> mICacheEventActionMaping;
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.3.3 事件系统架构
|
||||
|
||||
```
|
||||
事件分发流程:
|
||||
Dispatch(arg)
|
||||
│
|
||||
▼
|
||||
获取 EventId 对应的 Action 字典
|
||||
│
|
||||
▼
|
||||
获取 参数类型 HashCode 对应的 CacheEventAction
|
||||
│
|
||||
▼
|
||||
遍历执行所有 Action<T>
|
||||
│
|
||||
▼
|
||||
调用 arg.Recycle() 回收事件参数
|
||||
```
|
||||
|
||||
### 3.4 AssetBundle 资源管理器
|
||||
|
||||
#### 3.4.1 HFABMgr 核心结构
|
||||
|
||||
```csharp
|
||||
public partial class HFABMgr : AbsHFSingleMonoBehaviour<HFABMgr>
|
||||
{
|
||||
public float progress { get; } // 加载进度
|
||||
Dictionary<int, HFAssetLoader> mHFAssetLoaderMaping; // 加载器映射
|
||||
Dictionary<int, HFAssetBundlePathAttribute> mHFAssetBundlePathAttributeMaping; // 属性映射
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.4.2 资源加载流程
|
||||
|
||||
```
|
||||
资源加载流程:
|
||||
OnStartLoad(request)
|
||||
│
|
||||
▼
|
||||
检查缓存 → 获取/创建 HFAssetLoader
|
||||
│
|
||||
▼
|
||||
loader.AddRequest(request)
|
||||
│
|
||||
▼
|
||||
loader.LoadAsset() → 异步加载
|
||||
│
|
||||
▼
|
||||
加载完成 → 回调处理
|
||||
```
|
||||
|
||||
#### 3.4.3 依赖管理
|
||||
|
||||
通过 Manifest 文件解析资源依赖关系:
|
||||
|
||||
```csharp
|
||||
HFAssetBundlePathAttribute OnGetDependencyAbpAttr(string _depManifestName)
|
||||
{
|
||||
// 解析依赖项名称 → 获取属性 → 缓存
|
||||
}
|
||||
```
|
||||
|
||||
### 3.5 相机管理系统
|
||||
|
||||
#### 3.5.1 HFCameraManager 架构
|
||||
|
||||
```csharp
|
||||
public partial class HFCameraManager : AbsHFSingleRunTime<HFCameraManager>
|
||||
{
|
||||
private Dictionary<int, AbsHFCameraItem> m_List_CameraItem; // 相机列表
|
||||
public HFMainCamera mainCamera; // 主相机
|
||||
|
||||
public void AddCamera(AbsHFCameraItem cameraItem); // 添加相机
|
||||
public void RemoveCamera(AbsHFCameraItem cameraItem); // 移除相机
|
||||
public AbsHFEventHandle GetCamera(string _cameraName); // 获取相机
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.5.2 相机堆栈管理
|
||||
|
||||
```
|
||||
相机层级架构:
|
||||
HFMainCamera (主相机)
|
||||
│
|
||||
├── CameraStack (相机堆栈)
|
||||
│ ├── CameraItem1 (Weight=10)
|
||||
│ ├── CameraItem2 (Weight=20)
|
||||
│ └── CameraItem3 (Weight=5)
|
||||
│
|
||||
└── 按 Weight 排序 → 渲染顺序控制
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、General 层架构
|
||||
|
||||
### 4.1 状态机系统
|
||||
|
||||
#### 4.1.1 核心类层次
|
||||
|
||||
```
|
||||
IGFStateMachineBehaviour (接口)
|
||||
│
|
||||
├── AbsGFStateMachineBehaviour (抽象基类)
|
||||
│ ├── AbsGFAttachStateBehaviour (状态行为)
|
||||
│ ├── AbsGFAttachLayerBehaviour (层行为)
|
||||
│ └── AbsGFAttachMachineBehaviour (机行为)
|
||||
│
|
||||
└── IGFStateMachineBehaviourClassify (分类接口)
|
||||
```
|
||||
|
||||
#### 4.1.2 状态机特性
|
||||
|
||||
| 特性 | 说明 |
|
||||
|-----|------|
|
||||
| **层级化设计** | 支持状态、层、机三个层级的行为绑定 |
|
||||
| **参数化驱动** | 通过参数控制状态切换 |
|
||||
| **Inspector 扩展** | 自定义编辑器支持 |
|
||||
|
||||
### 4.2 工具类
|
||||
|
||||
#### 4.2.1 扩展方法集合
|
||||
|
||||
**HFUGUIExtendEngine.cs** - UGUI 扩展方法(40+ 方法)
|
||||
|
||||
| 功能分类 | 方法示例 |
|
||||
|---------|---------|
|
||||
| **坐标转换** | `HFWorldToLocalPointInRectangle` |
|
||||
| **布局操作** | `HFFixedPositionInCanvas`, `HFForceRebuildLayoutImmediate` |
|
||||
| **RectTransform** | `HFIdentityStreech`, `HFCopyRectTransformFrom` |
|
||||
| **事件监听** | `HFAddListener` |
|
||||
|
||||
#### 4.2.2 其他工具
|
||||
|
||||
| 工具类 | 职责 |
|
||||
|-------|-----|
|
||||
| `HFAssetBundleHelper` | AB 包辅助工具 |
|
||||
| `HFDllHelper` | DLL 加载辅助 |
|
||||
| `HFEnumForAssetBundle` | AB 包相关枚举 |
|
||||
| `MinGameModel` | 微信小游戏适配 |
|
||||
|
||||
---
|
||||
|
||||
## 五、架构设计特点
|
||||
|
||||
### 5.1 设计模式应用
|
||||
|
||||
| 模式 | 应用场景 | 实现类 |
|
||||
|-----|---------|-------|
|
||||
| **单例模式** | 全局管理器 | `AbsHFSingleMonoBehaviour`, `AbsHFSingleRunTime` |
|
||||
| **工厂模式** | 对象创建 | 对象池的 GetItem |
|
||||
| **观察者模式** | 事件系统 | `IHFEventHandle` |
|
||||
| **策略模式** | 多平台适配 | `SFGlobalData.Platform()` |
|
||||
| **模板方法** | 生命周期 | `SimulateBehaviour` 系列 |
|
||||
|
||||
### 5.2 架构优势
|
||||
|
||||
| 优势 | 体现 |
|
||||
|-----|------|
|
||||
| **解耦性** | 逻辑与 MonoBehaviour 分离 |
|
||||
| **可测试性** | 纯逻辑类易于单元测试 |
|
||||
| **性能优化** | 对象池减少 GC |
|
||||
| **扩展性** | 接口驱动,易于扩展 |
|
||||
| **可维护性** | 清晰的模块划分 |
|
||||
|
||||
### 5.3 潜在改进点
|
||||
|
||||
| 改进方向 | 说明 |
|
||||
|---------|------|
|
||||
| **异步加载优化** | 可引入协程池或 Job System |
|
||||
| **资源依赖可视化** | 可增加依赖关系图工具 |
|
||||
| **性能监控** | 可增加加载时间、内存占用统计 |
|
||||
| **热更新支持** | 可考虑集成 Lua 或 ILRuntime |
|
||||
|
||||
---
|
||||
|
||||
## 六、总结
|
||||
|
||||
StrayFog 框架是一个结构清晰、设计完善的 Unity 游戏框架,核心特点包括:
|
||||
|
||||
1. **模拟行为系统** - 实现了完整的 MonoBehaviour 生命周期解耦
|
||||
2. **对象池机制** - 高效的内存管理策略
|
||||
3. **事件驱动架构** - 灵活的事件分发系统
|
||||
4. **资源管理体系** - 完善的 AssetBundle 加载和依赖管理
|
||||
5. **模块化设计** - 清晰的分层和职责划分
|
||||
|
||||
该框架适合中大型 Unity 项目使用,具备良好的扩展性和维护性。
|
||||
|
||||
---
|
||||
|
||||
**版本**: v1.0
|
||||
**分析日期**: 2024年
|
||||
**适用项目**: OnlineGame
|
||||
|
||||
---
|
||||
|
||||
## 文档关联
|
||||
|
||||
> 当本文档发生变更时,应同步检查以下关联文档,避免信息不一致。
|
||||
|
||||
| 文档 | 关联原因 |
|
||||
|---|---|
|
||||
| [通用框架入口](README.md) | 本文件是通用框架模块的子文档 |
|
||||
| [架构说明](../../../rules/10-架构说明.md) | 高层架构规则与程序集边界 |
|
||||
| [实体系统](../实体系统/README.md) | 实体系统架构在本文件中分析 |
|
||||
| [对象池与运行时对象管理](../内存对象管理/README.md) | 对象池架构在本文件中分析 |
|
||||
| [事件系统](../事件系统/README.md) | 事件系统架构在本文件中分析 |
|
||||
| [资源管理](../资源管理/README.md) | 资源管理架构在本文件中分析 |
|
||||
| [相机系统](../相机系统/README.md) | 相机系统架构在本文件中分析 |
|
||||
| [modules/模块索引.md](../../模块索引.md) | 模块索引是项目知识总入口 |
|
||||
| [rules/20-开发规则.md](../../../rules/20-开发规则.md) | 开发规则与框架架构互相引用 |
|
||||
| [rules/30-模块登记规则.md](../../../rules/30-模块登记规则.md) | 模块登记规则定义分类和状态口径 |
|
||||
84
modules/模块文档模板.md
Normal file
84
modules/模块文档模板.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# 模块名称
|
||||
|
||||
## 分类
|
||||
|
||||
- 项目功能 / 项目框架 / 编辑器工具
|
||||
|
||||
## 目的
|
||||
|
||||
TODO:一句话说明这个模块是做什么的,从玩家/产品/技术视角写。
|
||||
|
||||
## 主要路径
|
||||
|
||||
| 类型 | 路径 | 说明 |
|
||||
|---|---|---|
|
||||
| 逻辑代码 | `Assets/...` | TODO |
|
||||
| UI 代码 | `Assets/...` | TODO |
|
||||
| 资源 | `Assets/...` | TODO |
|
||||
| 配置 | `XLSX_SRC/...` 或 `GoogleProtobufNetwork/...` | TODO |
|
||||
|
||||
## 入口
|
||||
|
||||
- 运行入口:TODO
|
||||
- 编辑器入口:TODO
|
||||
- 资源入口:TODO
|
||||
|
||||
## 数据流
|
||||
|
||||
```text
|
||||
TODO:用箭头图描述关键流程
|
||||
```
|
||||
|
||||
## 关键类 / API
|
||||
|
||||
| 类/方法 | 用途 |
|
||||
|---|---|
|
||||
| `ClassName` | TODO |
|
||||
|
||||
## 依赖
|
||||
|
||||
- 依赖的框架模块:TODO
|
||||
- 依赖的其他功能模块:TODO
|
||||
- 依赖的第三方插件:TODO
|
||||
|
||||
## 当前状态
|
||||
|
||||
- TODO
|
||||
|
||||
## 变更记录
|
||||
|
||||
| 日期 | 变更内容 | 关联 spec/change | 影响范围 |
|
||||
|---|---|---|---|
|
||||
| YYYY-MM-DD | 简要描述 | `[<change-name>]`(对应 `specs/openspec/<change-name>/业务参考总入口.md`) | 相关模块/文件 |
|
||||
|
||||
## 文档关联
|
||||
|
||||
本模块发生变更时,以下文档必须同步更新:
|
||||
|
||||
- [模块索引](模块索引.md) — 如果新增/删除模块,或模块分类/状态改变
|
||||
- [模块登记规则](../rules/30-模块登记规则.md) — 如果新增模块、分类口径或依赖关系变化
|
||||
- [架构说明](../rules/10-架构说明.md) — 如果涉及架构变化
|
||||
- TODO:链接相关功能/框架模块文档(如依赖方或被依赖方)
|
||||
- TODO:链接相关 spec 目录(如 `../../specs/openspec/<change-name>/业务参考总入口.md`)
|
||||
|
||||
> 注意:本模板位于 `modules/` 根目录。如果复制到 `modules/features/` 或 `modules/frameworks/` 下,请把 `../rules/` 和 `模块索引.md` 调整为 `../../rules/` 和 `../模块索引.md`。
|
||||
|
||||
## 下一步
|
||||
|
||||
- [ ] TODO
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [模块索引](模块索引.md)
|
||||
- [模块登记规则](../rules/30-模块登记规则.md)
|
||||
- [架构说明](../rules/10-架构说明.md)
|
||||
- TODO:链接相关功能/框架模块文档
|
||||
|
||||
## 是否需要拆分文档
|
||||
|
||||
轻量模块可以只保留这个 README。
|
||||
|
||||
当模块出现独立计划、持续进度或多项待办时,再增加:
|
||||
|
||||
- `progress.md`
|
||||
- `tasks.md`
|
||||
85
modules/模块索引.md
Normal file
85
modules/模块索引.md
Normal file
@@ -0,0 +1,85 @@
|
||||
# 模块索引
|
||||
|
||||
这里是 UnityAI / StrayFog 框架知识库的项目知识总入口。
|
||||
|
||||
模块清单、模块状态和模块入口以本文件为准;`rules/` 只维护规则和约束,不重复维护模块事实表。
|
||||
|
||||
---
|
||||
|
||||
## 重要口径
|
||||
|
||||
- **核心框架**:支撑游戏运行的内部系统,例如实体系统、UI 窗口系统、事件系统、资源管理、对象池、场景管理。
|
||||
- **编辑器工具**:Unity Editor 内使用的生成器、Inspector、菜单、配置工具。
|
||||
- **业务参考**:外部实际项目的业务实现示例,仅作参考,不作为框架规范。见 [business-reference](../business-reference/playballoons/业务参考总入口.md)。
|
||||
|
||||
轻量模块先在本文件登记;复杂模块再拆独立目录。
|
||||
|
||||
---
|
||||
|
||||
## 状态口径
|
||||
|
||||
见 [模块登记规则](../rules/30-模块登记规则.md#状态定义)。
|
||||
|
||||
不要使用“已开始”,优先使用“已识别 / 初步梳理 / 待验证 / 已完成 / 未发现实现”。
|
||||
|
||||
---
|
||||
|
||||
## 核心框架
|
||||
|
||||
| 框架模块 | 主要路径 | 当前判断 | 状态 |
|
||||
|---|---|---|---|
|
||||
| [模拟行为系统](frameworks/通用框架/README.md) | `Assets/StrayFog/Core/SimulateBehaviour/` | 解耦 MonoBehaviour,模拟完整生命周期 | 已完成 |
|
||||
| [对象池](frameworks/内存对象管理/README.md) | `Assets/Game/GameHFScripte/CopyToHF/MemoryManager/` | GameObject 对象池、脚本对象池、类实例池 | 已完成 |
|
||||
| [事件系统](frameworks/事件系统/README.md) | `Assets/Game/GameHFScripte/CopyToHF/Event/` | CTC/STC 事件参数和事件派发 | 已完成 |
|
||||
| [资源管理](frameworks/资源管理/README.md) | `Assets/Game/GameHFScripte/CopyToHF/AssetBundle/` | AssetBundle、语言资源、图集、加载规则 | 已完成 |
|
||||
| [相机系统](frameworks/相机系统/README.md) | `Assets/Game/GameHFScripte/CopyToHF/` | 多相机管理、相机堆栈、渲染顺序 | 初步梳理 |
|
||||
| [实体系统](frameworks/实体系统/README.md) | `Assets/Game/GameHFScripte/GameFunction/GameEntity/` | 实体基类、模型/系统、实体池 | 已完成 |
|
||||
| [UI 窗口系统](frameworks/UI窗口系统/README.md) | `Assets/Game/GameHFScripte/CopyToHF/UIWindowMgr/` | UI 窗口管理、窗口基类、生命周期 | 已完成 |
|
||||
| [技能系统](frameworks/技能系统/README.md) | `Assets/Game/GameHFScripte/GameFunction/GameEntity/` | 技能触发器、释放器、配置联动 | 初步梳理 |
|
||||
| [场景系统](frameworks/场景系统.md) | `Assets/Game/GameHFScripte/GameFunction/SceneManager/` | 场景加载、资源配置、生命周期 | 初步梳理 |
|
||||
| [网络系统](frameworks/网络系统.md) | `Assets/Game/GameHFScripte/CopyToHF/Network/` | WebSocket、protobuf、消息映射 | 初步梳理 |
|
||||
| [配置生成](frameworks/数据配置框架.md) | `XLSX_SRC/`、`Assets/AutoBuildScript/` | 表格配置源、解析、生成代码 | 初步梳理 |
|
||||
| [Hotfix 启动](frameworks/Hotfix启动.md) | `Assets/Game/GameHFScripte/GameFunction/GameRoot/` | hotfix 侧根入口和功能目录聚合 | 初步梳理 |
|
||||
|
||||
---
|
||||
|
||||
## 编辑器工具
|
||||
|
||||
| 工具模块 | 主要路径 | 当前判断 | 状态 |
|
||||
|---|---|---|---|
|
||||
| [SF Editor](frameworks/编辑器工具/README.md#sf-editor) | `Assets/StrayFog/Editor/` | StrayFog 编辑器扩展 | 待验证 |
|
||||
| [项目 Editor Assets](frameworks/编辑器工具/README.md#project-editor-assets) | `Assets/Editor/` | 项目编辑器资源与脚本 | 待验证 |
|
||||
|
||||
---
|
||||
|
||||
## 文档拆分规则
|
||||
|
||||
- 核心框架按技术职责命名,可以对应 `GameEntity`、`UIWindowMgr`、`Network`、`AssetBundle`、`Event` 这类内部系统。
|
||||
- 轻量模块只在本文件登记。
|
||||
- 复杂模块再拆 `modules/<module>/`。
|
||||
- `模块索引.md` 写稳定说明。
|
||||
- `progress.md` 写阶段状态。
|
||||
- `tasks.md` 写下一步待办。
|
||||
- 跨模块计划放到 [plans](../plans/)。
|
||||
|
||||
---
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [项目规则入口](../rules/项目规则入口.md):AI 工作方式、扫描范围、归档规则和规则索引。
|
||||
- [模块登记规则](../rules/30-模块登记规则.md):分类口径、状态定义和新增模块登记规则。
|
||||
- [架构说明](../rules/10-架构说明.md):总体分层、程序集边界和依赖方向。
|
||||
|
||||
---
|
||||
|
||||
## 文档关联
|
||||
|
||||
> 当本文档发生变更时,应同步检查以下关联文档,避免信息不一致。
|
||||
|
||||
| 文档 | 关联原因 |
|
||||
|---|---|
|
||||
| [rules/30-模块登记规则.md](../rules/30-模块登记规则.md) | 模块索引是模块登记规则的唯一事实来源 |
|
||||
| [rules/10-架构说明.md](../rules/10-架构说明.md) | 模块分类、分层与架构说明互相影响 |
|
||||
| [rules/项目规则入口.md](../rules/项目规则入口.md) | 模块索引是项目规则入口引用的项目知识总入口 |
|
||||
| [ai/AI接入入口.md](../ai/AI接入入口.md) | AI 接入入口引用模块索引作为必读文档 |
|
||||
| [modules/frameworks/*](frameworks/) | 框架模块文档新增/拆分时需同步索引 |
|
||||
Reference in New Issue
Block a user