@kkarum/framework 2.3.18 → 2.3.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +191 -93
- package/docs/CODEX_GUIDE.md +219 -0
- package/docs/CODEX_GUIDE.md.meta +6 -0
- package/docs/ERROR_HANDLING.md +233 -0
- package/docs/ERROR_HANDLING.md.meta +6 -0
- package/docs/FRAMEWORK_API.md +414 -0
- package/docs/FRAMEWORK_API.md.meta +6 -0
- package/docs/FRAMEWORK_EXAMPLES.md +309 -0
- package/docs/FRAMEWORK_EXAMPLES.md.meta +6 -0
- package/docs/FRAMEWORK_OVERVIEW.md +127 -0
- package/docs/FRAMEWORK_OVERVIEW.md.meta +6 -0
- package/docs/FRAMEWORK_PATTERNS.md +261 -0
- package/docs/FRAMEWORK_PATTERNS.md.meta +6 -0
- package/docs/FRAMEWORK_USAGE.md +344 -0
- package/docs/FRAMEWORK_USAGE.md.meta +6 -0
- package/docs/MCP_INTEGRATION.md +241 -0
- package/docs/MCP_INTEGRATION.md.meta +6 -0
- package/docs/UI_DEVELOPMENT_GUIDE.md +295 -0
- package/docs/UI_DEVELOPMENT_GUIDE.md.meta +6 -0
- package/docs.meta +13 -0
- package/package.json +1 -1
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
# UI Development Guide
|
|
2
|
+
|
|
3
|
+
## AI 生成 UI 代码规则
|
|
4
|
+
|
|
5
|
+
以下规则用于约束 Codex / AI 工具生成 UI 相关代码,并覆盖本文档中的旧示例:
|
|
6
|
+
|
|
7
|
+
- `LayerController` 中不要查找节点或组件,不要使用 `find()` / `findComponent()` 缓存 UI 引用。
|
|
8
|
+
- 节点引用写在对应 `FW.Layer` 组件中,使用 `@FWPropertyNode()`,变量名与节点名一致。
|
|
9
|
+
- 组件引用写在对应 `FW.Layer` 组件中,使用 `@FWPropertyComponent(ComponentClass)`,变量名与节点名一致。
|
|
10
|
+
- `layerAssetProperty` 返回资源时使用点访问:`preLoad.prefab.ShopLayer`,不要写 `preLoad.prefab["ShopLayer"]`。
|
|
11
|
+
- 默认不要在 Controller 或业务层调用 `invoke()`,除非用户明确要求。
|
|
12
|
+
|
|
13
|
+
推荐结构:
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
@ccclass
|
|
17
|
+
export class ShopLayer extends FW.Layer {
|
|
18
|
+
@FWPropertyNode()
|
|
19
|
+
BtnClose: cc.Node = null;
|
|
20
|
+
|
|
21
|
+
@FWPropertyComponent(cc.Label)
|
|
22
|
+
TitleLabel: cc.Label = null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class ShopLayerController extends FW.LayerController {
|
|
26
|
+
get layerAssetProperty(): FW.AssetProperty {
|
|
27
|
+
return FW.Entry.getComponent(ShopAssetConfig).preLoad.prefab.ShopLayer;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
onInit() {
|
|
31
|
+
const layer = this.layer as ShopLayer;
|
|
32
|
+
this.cc(layer.BtnClose, this.onCloseClick);
|
|
33
|
+
layer.TitleLabel.string = "Shop";
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
本文档定义基于本框架开发 UI 的标准做法。
|
|
39
|
+
|
|
40
|
+
## UI 类型
|
|
41
|
+
|
|
42
|
+
### 常驻层
|
|
43
|
+
|
|
44
|
+
适合主界面、HUD、底部导航。
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
layerType = FW.SystemDefine.FWLayerType.PERMANENT;
|
|
48
|
+
renderOrder = FW.SystemDefine.FWLayerRenderOrder.PERMANENT;
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
可使用:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
this.hide();
|
|
55
|
+
this.display();
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
框架会暂停/恢复节点系统事件、框架事件和定时器。
|
|
59
|
+
|
|
60
|
+
### 普通 UI
|
|
61
|
+
|
|
62
|
+
适合可重复打开的页面。
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
layerType = FW.SystemDefine.FWLayerType.REPEAT;
|
|
66
|
+
renderOrder = FW.SystemDefine.FWLayerRenderOrder.UI;
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 队列弹窗
|
|
70
|
+
|
|
71
|
+
适合奖励弹窗、确认弹窗。
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
layerType = FW.SystemDefine.FWLayerType.POPUP_QUEUE;
|
|
75
|
+
renderOrder = FW.SystemDefine.FWLayerRenderOrder.POPUP;
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
已有队列弹窗时,新弹窗会入队,当前弹窗关闭后再打开下一个。
|
|
79
|
+
|
|
80
|
+
### 一次性弹窗
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
layerType = FW.SystemDefine.FWLayerType.POPUP_DISPOSABLE;
|
|
84
|
+
renderOrder = FW.SystemDefine.FWLayerRenderOrder.POPUP;
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## LayerController 标准结构
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
export class InventoryLayerController extends FW.LayerController {
|
|
91
|
+
renderOrder = FW.SystemDefine.FWLayerRenderOrder.UI;
|
|
92
|
+
layerType = FW.SystemDefine.FWLayerType.REPEAT;
|
|
93
|
+
autoRelease = true;
|
|
94
|
+
isRepeatOpen = false;
|
|
95
|
+
|
|
96
|
+
private closeButton: cc.Node;
|
|
97
|
+
private titleLabel: cc.Label;
|
|
98
|
+
|
|
99
|
+
get layerAssetProperty(): FW.AssetProperty {
|
|
100
|
+
return this.getConfig<InventoryAssetConfig>().preLoad.prefab["InventoryLayer"] as FW.AssetProperty;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
onInit(args?: any) {
|
|
104
|
+
this.cacheNodes();
|
|
105
|
+
this.bindEvents();
|
|
106
|
+
this.render();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private cacheNodes() {
|
|
110
|
+
this.closeButton = this.find("CloseButton", this.layer.node);
|
|
111
|
+
this.titleLabel = this.findComponent("TitleLabel", this.layer.node, cc.Label);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
private bindEvents() {
|
|
115
|
+
this.cc(this.closeButton, this.onCloseClick);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
private render() {
|
|
119
|
+
this.titleLabel.string = "Inventory";
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private onCloseClick = () => {
|
|
123
|
+
this.close();
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## 节点查找
|
|
129
|
+
|
|
130
|
+
推荐:
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
const node = this.find("CloseButton", this.layer.node);
|
|
134
|
+
const label = this.findComponent("TitleLabel", this.layer.node, cc.Label);
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
可使用装饰器:
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
@FWPropertyNode({ path: "CloseButton" })
|
|
141
|
+
private closeButton: cc.Node;
|
|
142
|
+
|
|
143
|
+
@FWPropertyComponent(cc.Label, "TitleLabel")
|
|
144
|
+
private titleLabel: cc.Label;
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
注意:
|
|
148
|
+
|
|
149
|
+
- 装饰器改写 `onLoad`,更适合 Cocos Component。
|
|
150
|
+
- LayerController 不是挂在节点上的 Cocos Component,优先使用 `find()`。
|
|
151
|
+
|
|
152
|
+
## 事件绑定
|
|
153
|
+
|
|
154
|
+
按钮:
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
this.cc(buttonNode, this.onButtonClick);
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
自定义事件:
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
this.fw("INVENTORY_CHANGED", this.render);
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
复杂:
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
this.registerEvent({
|
|
170
|
+
CCEvent: [{
|
|
171
|
+
target: buttonNode,
|
|
172
|
+
eventName: cc.Node.EventType.TOUCH_END,
|
|
173
|
+
responseInterval: 500,
|
|
174
|
+
cb: this.onButtonClick,
|
|
175
|
+
}],
|
|
176
|
+
});
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
不要在 LayerController 中散落裸 `node.on()`。如必须使用,必须在关闭时手动 `off`。
|
|
180
|
+
|
|
181
|
+
## 列表开发
|
|
182
|
+
|
|
183
|
+
### 小列表
|
|
184
|
+
|
|
185
|
+
可以直接实例化 item prefab,但仍应使用 `FW.Entry.resMgr` 加载资源。
|
|
186
|
+
|
|
187
|
+
### 大列表
|
|
188
|
+
|
|
189
|
+
优先使用:
|
|
190
|
+
|
|
191
|
+
- `FWVirtualViewComponent`
|
|
192
|
+
- `FWVirtuaScrollViewComponent`
|
|
193
|
+
- `FW.Entry.objectMgr.createObjectPool()`
|
|
194
|
+
|
|
195
|
+
`FWVirtualViewComponent` 常用字段:
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
list.onRender = (item, index) => {};
|
|
199
|
+
list.onSelected = (item, index, lastIndex, selected?) => {};
|
|
200
|
+
list.onPageChange = (item, page) => {};
|
|
201
|
+
list.count = data.length;
|
|
202
|
+
list.updateItem(index);
|
|
203
|
+
list.updateAll();
|
|
204
|
+
list.scrollTo(index, 0.3);
|
|
205
|
+
list.prePage();
|
|
206
|
+
list.nextPage();
|
|
207
|
+
list.skipPage(page, 0.3);
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## UI 与 Data 同步
|
|
211
|
+
|
|
212
|
+
推荐方向:
|
|
213
|
+
|
|
214
|
+
```text
|
|
215
|
+
Logic -> Data -> evtMgr.dispatch -> LayerController.render
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
示例:
|
|
219
|
+
|
|
220
|
+
```ts
|
|
221
|
+
// Data
|
|
222
|
+
setItems(items: Item[]) {
|
|
223
|
+
this.items = items;
|
|
224
|
+
FW.Entry.evtMgr.dispatch("INVENTORY_ITEMS_CHANGED", items);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// LayerController
|
|
228
|
+
this.fw("INVENTORY_ITEMS_CHANGED", this.renderItems);
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## 打开和关闭
|
|
232
|
+
|
|
233
|
+
打开:
|
|
234
|
+
|
|
235
|
+
```ts
|
|
236
|
+
await FW.Entry.layerMgr.openAsync<InventoryLayerController>({
|
|
237
|
+
type: InventoryLayerController,
|
|
238
|
+
args: { selectedTab: 1 },
|
|
239
|
+
});
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
关闭:
|
|
243
|
+
|
|
244
|
+
```ts
|
|
245
|
+
this.close();
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
关闭前异步处理:
|
|
249
|
+
|
|
250
|
+
```ts
|
|
251
|
+
async onClose() {
|
|
252
|
+
await this.playCloseAnimation();
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
`FWLayerManager.close()` 会等待 `onClose()`,再销毁节点、释放资源、清理状态。
|
|
257
|
+
|
|
258
|
+
## 资源释放
|
|
259
|
+
|
|
260
|
+
LayerController 属性:
|
|
261
|
+
|
|
262
|
+
```ts
|
|
263
|
+
autoRelease = true;
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
关闭时,如果 `autoRelease` 为 true,框架会调用:
|
|
267
|
+
|
|
268
|
+
```ts
|
|
269
|
+
FW.Entry.resMgr.releaseAsset(ctr.layerData.layerAssetProperty);
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
注意:资源释放依赖 `AssetProperty.autoRelease` 和引用计数。不要手动释放还在使用的公共资源。
|
|
273
|
+
|
|
274
|
+
## UI 自动化约定
|
|
275
|
+
|
|
276
|
+
使用 cocos-mcp 创建 UI 时:
|
|
277
|
+
|
|
278
|
+
- 根节点名与 prefab 名一致。
|
|
279
|
+
- 交互节点名与代码一致。
|
|
280
|
+
- 按钮节点含 `cc.Button` 和可视组件。
|
|
281
|
+
- 文本节点含 `cc.Label`。
|
|
282
|
+
- 滚动区域含 `cc.ScrollView`、`content`、`cc.Layout`。
|
|
283
|
+
- 全屏根节点使用 `cc.Widget` stretch/full。
|
|
284
|
+
|
|
285
|
+
## UI 质量检查
|
|
286
|
+
|
|
287
|
+
完成 UI 功能前检查:
|
|
288
|
+
|
|
289
|
+
- `layerAssetProperty` 能解析到有效 prefab。
|
|
290
|
+
- `onInit()` 中所有 `find()` 节点存在。
|
|
291
|
+
- 按钮不会重复注册。
|
|
292
|
+
- 列表刷新不会无限实例化节点。
|
|
293
|
+
- 关闭时没有未清理定时器。
|
|
294
|
+
- 弹窗类型和渲染顺序符合用途。
|
|
295
|
+
- `autoRelease` 与资源复用场景一致。
|
package/docs.meta
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"ver": "1.1.3",
|
|
3
|
+
"uuid": "1e0c55a2-93ba-4368-9b54-26c8d470d0ed",
|
|
4
|
+
"importer": "folder",
|
|
5
|
+
"isBundle": false,
|
|
6
|
+
"bundleName": "",
|
|
7
|
+
"priority": 1,
|
|
8
|
+
"compressionType": {},
|
|
9
|
+
"optimizeHotUpdate": {},
|
|
10
|
+
"inlineSpriteFrames": {},
|
|
11
|
+
"isRemoteBundle": {},
|
|
12
|
+
"subMetas": {}
|
|
13
|
+
}
|