文档修复:移除指向不存在文件的失效链接引用

client/02、client/03 中指向 docs/important/client/友乐游戏引擎精灵与资源管理接口规范.md
(该文件不在仓库)的 markdown 链接改为纯文字规范名,保留文意、去除失效链接。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-05 22:51:28 +08:00
co-authored by Claude Opus 4.8
parent f776e4c817
commit 363f97cdbe
2 changed files with 40 additions and 5 deletions
@@ -93,7 +93,7 @@ ID 必须与编辑器中实际存在的精灵**完全一致**。超范围 → `S
- **精灵结构常量**:每个精灵注明【精灵类型(图片精灵 / 文字精灵)+ 功能 + 关联的图片资源 ID + 帧数说明】。 - **精灵结构常量**:每个精灵注明【精灵类型(图片精灵 / 文字精灵)+ 功能 + 关联的图片资源 ID + 帧数说明】。
- **图片资源常量**:每个资源注明【帧数(单帧 / N 帧,且每帧含义)+ 用途 + 尺寸】。 - **图片资源常量**:每个资源注明【帧数(单帧 / N 帧,且每帧含义)+ 用途 + 尺寸】。
(声音、Spine 资源的注释要求见 03。资源 ID 范围与注释格式细则以《[友乐游戏引擎精灵与资源管理接口规范](../../../docs/important/client/友乐游戏引擎精灵与资源管理接口规范.md)》为准。) (声音、Spine 资源的注释要求见 03。资源 ID 范围与注释格式细则以《友乐游戏引擎精灵与资源管理接口规范》为准。)
### 三者如何配合(新增一块 UI 的流程) ### 三者如何配合(新增一块 UI 的流程)
@@ -169,6 +169,40 @@ this.addEventListener(EventBus.Events.GAME_STARTED, fn);
`BaseComponent.addEventListener` 会包装处理函数(绑定 `this`、捕获异常、销毁后忽略事件),并记录下来在 `destroy()` 时统一 `EventBus.off`。**销毁组件用 `destroy()` 而非只 `hide()`** —— 只有 `destroy()` 才清理监听。 `BaseComponent.addEventListener` 会包装处理函数(绑定 `this`、捕获异常、销毁后忽略事件),并记录下来在 `destroy()` 时统一 `EventBus.off`。**销毁组件用 `destroy()` 而非只 `hide()`** —— 只有 `destroy()` 才清理监听。
### 组件数据自持 + set-refresh 范式(核心)
**前端只负责界面展示与玩家交互;所有核心运算与胜负裁定以服务端为准、数据以服务端为权威**(见 01/05)。组件本地保存的只是「用于画界面的数据副本」,据此渲染,不做权威计算。为此每个组件遵循统一范式:
1. **数据集中在 `this.data`**:组件(及其下每个「零件 UI」)的自有数据全部收拢到 `this.data.*`,**不散落**在实例其他字段或全局;界面能否重建只取决于 `this.data`。
2. **每个 UI 都有 `setXxx` / `refreshXxx` 成对方法**:组件本身、以及组件内每个「零件 UI」(如玩家信息条、分数、手牌区、按钮组)都提供一对——
- `setXxx(...)`:**只写数据**到 `this.data`,不碰精灵;
- `refreshXxx()`:**只据 `this.data` 画界面**,不改数据。
两者职责单一、互不越界。
3. **组件有一个总 `refresh()`**:顺序调用其下所有零件的 `refreshXxx()`,把整块界面据 `this.data` 完整重画。**任何时候调用 `refresh()` 都能无歧义重建正确界面**(断线重连、切 app 回来、网页刷新都复用它,不另写一套渲染)。
```js
var GameView = Object.create(BaseComponent);
GameView.init = function (config) {
this.data = { players: [], score: 0 /* ...自有数据只放这里 */ };
// ...
};
// 零件 UI:分数。set 只写数据,refresh 只据数据画
GameView.setScore = function (v) { this.data.score = v; };
GameView.refreshScore = function () { SpriteManager.setText(SCORE_ID, this.data.score); };
// 总 refresh:据 this.data 重画所有零件
GameView.refresh = function () {
this.refreshPlayers();
this.refreshScore();
// ...其余零件
};
```
**收包的正确流程**:先 `setXxx` 把数据写进 `this.data`(必要时 `refresh` 刷新静态界面),**再**做动画/表现。动画只是体验层,其「开始/结束/出错」等生命周期回调里**只刷新界面、绝不设置核心数据**。这样即使动画卡住/播错、或开发前期还没做动画,数据、逻辑与界面依然正确、互不影响。
--- ---
## 4. UIManager:注册与场景切换 ## 4. UIManager:注册与场景切换
@@ -295,6 +329,7 @@ list.onClick = function (type, rowIndex, rowData) { /* ... */ };
| 检查 `SpriteManager` 返回值 | 忽略 `false` 返回 | | 检查 `SpriteManager` 返回值 | 忽略 `false` 返回 |
| 精灵/资源/坐标全进常量文件 | 在业务代码内联裸数字 | | 精灵/资源/坐标全进常量文件 | 在业务代码内联裸数字 |
| 组件继承 `BaseComponent`,事件用 `addEventListener` | 自建组件类 / 直接 `EventBus.on` | | 组件继承 `BaseComponent`,事件用 `addEventListener` | 自建组件类 / 直接 `EventBus.on` |
| 自有数据集中 `this.data`;每个 UI/零件有 set/refresh 对;总 `refresh` 可据数据重建界面 | 数据散落各处 / `set` 里画界面 / `refresh` 里改数据 / 动画回调写核心数据 |
| 销毁用 `destroy()`,`onDestroy` 清动态精灵与定时器 | 只 `hide()` 就当销毁 | | 销毁用 `destroy()`,`onDestroy` 清动态精灵与定时器 | 只 `hide()` 就当销毁 |
| 动态列表用 `DynamicSpriteList` 并 `destroy` | 手搓 SpriteCopy 又忘清理 | | 动态列表用 `DynamicSpriteList` 并 `destroy` | 手搓 SpriteCopy 又忘清理 |
@@ -55,12 +55,12 @@ EventBus.once(EventBus.Events.GAME_FINISHED, fn); // 一次性
收到服务端推送后的固定节奏: 收到服务端推送后的固定节奏:
``` ```
1) 先更新数据模型(不动 UI) 1) 先更新数据模型(setXxx 写入组件 this.data,不动 UI)
2) 播放动画(纯视觉过渡,不改数据) 2) 播放动画(纯视觉过渡,不改数据)
3) 动画完成回调里刷新静态界面 3) 动画完成回调里只刷新静态界面(refresh)
``` ```
好处:动画是“锦上添花”,**即使不播动画,静态界面也始终正确**(重画函数据本地数据即可还原,见 04 重连)。**动画期间绝不修改游戏数据。** 好处:动画是“锦上添花”,**即使不播动画,静态界面也始终正确**(据组件 `this.data` 调 `refresh` 即可还原,见 02 组件范式 / 04 重连)。**动画的开始/结束/出错等生命周期回调里只刷新界面、绝不设置核心数据**;动画期间**绝不修改游戏数据**。这样动画卡住/播错、或开发前期没有动画时,数据、逻辑与界面依然正确、互不影响。
### AnimationManager API(框架,通用) ### AnimationManager API(框架,通用)
@@ -124,7 +124,7 @@ AnimationManager.frame(spriteId, opt.startFrame, opt.endFrame, opt.duration, opt
// 通用音效直接走框架:AudioManager.playSound(音效资源常量.某音效) // 通用音效直接走框架:AudioManager.playSound(音效资源常量.某音效)
``` ```
**声音文件手动准备**:音频文件由开发者**手动**按平台命名规范放入资源目录(文件名/存放目录/ID→文件名转换规则以《[友乐…规范](../../../docs/important/client/友乐游戏引擎精灵与资源管理接口规范.md)》为准),代码只按 ID 引用、不生成文件。因此**音效资源常量每个 ID 必须注明【类型(背景音乐 / 音效 / 语音)+ 用途 + 时长 + 播放方式(循环 / 单次)】**,让人据注释就能准确准备对应音频。 **声音文件手动准备**:音频文件由开发者**手动**按平台命名规范放入资源目录(文件名/存放目录/ID→文件名转换规则以《友乐游戏引擎精灵与资源管理接口规范》为准),代码只按 ID 引用、不生成文件。因此**音效资源常量每个 ID 必须注明【类型(背景音乐 / 音效 / 语音)+ 用途 + 时长 + 播放方式(循环 / 单次)】**,让人据注释就能准确准备对应音频。
**DO**:新音效只改音效资源常量;业务只调游戏音频管理。**DON'T**:在游戏音频管理里写裸文件名/ID;硬编码性别映射。 **DO**:新音效只改音效资源常量;业务只调游戏音频管理。**DON'T**:在游戏音频管理里写裸文件名/ID;硬编码性别映射。