@mclawnet/agent 0.6.20 → 0.6.22
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/cli.js +63 -0
- package/dist/__tests__/checkpoint.test.d.ts +2 -0
- package/dist/__tests__/checkpoint.test.d.ts.map +1 -0
- package/dist/__tests__/fs-handler-decode.test.d.ts +2 -0
- package/dist/__tests__/fs-handler-decode.test.d.ts.map +1 -0
- package/dist/__tests__/idle-sweeper.test.d.ts +2 -0
- package/dist/__tests__/idle-sweeper.test.d.ts.map +1 -0
- package/dist/__tests__/mcp-config.test.d.ts +2 -0
- package/dist/__tests__/mcp-config.test.d.ts.map +1 -0
- package/dist/__tests__/schedule-runtime-spawn.test.d.ts +2 -0
- package/dist/__tests__/schedule-runtime-spawn.test.d.ts.map +1 -0
- package/dist/__tests__/schedule-runtime.test.d.ts +2 -0
- package/dist/__tests__/schedule-runtime.test.d.ts.map +1 -0
- package/dist/__tests__/session-limit.test.d.ts +2 -0
- package/dist/__tests__/session-limit.test.d.ts.map +1 -0
- package/dist/__tests__/swarm-cli-client.test.d.ts +2 -0
- package/dist/__tests__/swarm-cli-client.test.d.ts.map +1 -0
- package/dist/__tests__/swarm-control-dispatch.test.d.ts +2 -0
- package/dist/__tests__/swarm-control-dispatch.test.d.ts.map +1 -0
- package/dist/__tests__/swarm-session-bridge.test.d.ts +2 -0
- package/dist/__tests__/swarm-session-bridge.test.d.ts.map +1 -0
- package/dist/backend-adapter.d.ts +43 -0
- package/dist/backend-adapter.d.ts.map +1 -1
- package/dist/checkpoint.d.ts +67 -0
- package/dist/checkpoint.d.ts.map +1 -0
- package/dist/{chunk-RIK7IXSW.js → chunk-WJWCYGLQ.js} +1130 -147
- package/dist/chunk-WJWCYGLQ.js.map +1 -0
- package/dist/errors.d.ts +40 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/fs-handler.d.ts.map +1 -1
- package/dist/hub-connection.d.ts +13 -0
- package/dist/hub-connection.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/schedule-runtime.d.ts +125 -0
- package/dist/schedule-runtime.d.ts.map +1 -0
- package/dist/session-manager.d.ts +102 -0
- package/dist/session-manager.d.ts.map +1 -1
- package/dist/skill-loader.d.ts +20 -0
- package/dist/skill-loader.d.ts.map +1 -1
- package/dist/start.d.ts +2 -0
- package/dist/start.d.ts.map +1 -1
- package/dist/start.js +1 -1
- package/dist/swarm-cli-client.d.ts +24 -0
- package/dist/swarm-cli-client.d.ts.map +1 -0
- package/dist/swarm-cli-client.js +83 -0
- package/dist/swarm-cli-client.js.map +1 -0
- package/dist/swarm-control-dispatch.d.ts +47 -0
- package/dist/swarm-control-dispatch.d.ts.map +1 -0
- package/dist/swarm-session-bridge.d.ts +22 -0
- package/dist/swarm-session-bridge.d.ts.map +1 -0
- package/package.json +6 -4
- package/skills/cocos-creator-3x-cn/SKILL.md +475 -0
- package/skills/cocos-creator-3x-cn/references/framework/asset-management.md +322 -0
- package/skills/cocos-creator-3x-cn/references/framework/component-system.md +348 -0
- package/skills/cocos-creator-3x-cn/references/framework/event-patterns.md +410 -0
- package/skills/cocos-creator-3x-cn/references/framework/playable-optimization.md +257 -0
- package/skills/cocos-creator-3x-cn/references/language/performance.md +363 -0
- package/skills/cocos-creator-3x-cn/references/language/quality-hygiene.md +307 -0
- package/skills/cocos-creator-3x-cn/references/review/architecture-review.md +183 -0
- package/skills/cocos-creator-3x-cn/references/review/quality-review.md +251 -0
- package/skills/cocos-performance-optimizer/SKILL.md +214 -0
- package/skills/game-development/2d-games/SKILL.md +129 -0
- package/skills/game-development/3d-games/SKILL.md +145 -0
- package/skills/game-development/SKILL.md +175 -0
- package/skills/game-development/game-art/SKILL.md +195 -0
- package/skills/game-development/game-audio/SKILL.md +200 -0
- package/skills/game-development/game-design/SKILL.md +139 -0
- package/skills/game-development/mobile-games/SKILL.md +118 -0
- package/skills/game-development/multiplayer/SKILL.md +142 -0
- package/skills/game-development/pc-games/SKILL.md +154 -0
- package/skills/game-development/vr-ar/SKILL.md +133 -0
- package/skills/game-development/web-games/SKILL.md +160 -0
- package/skills/game-engine/SKILL.md +140 -0
- package/skills/game-engine/assets/2d-maze-game.md +528 -0
- package/skills/game-engine/assets/2d-platform-game.md +1855 -0
- package/skills/game-engine/assets/gameBase-template-repo.md +310 -0
- package/skills/game-engine/assets/paddle-game-template.md +1528 -0
- package/skills/game-engine/assets/simple-2d-engine.md +507 -0
- package/skills/game-engine/references/3d-web-games.md +754 -0
- package/skills/game-engine/references/algorithms.md +843 -0
- package/skills/game-engine/references/basics.md +343 -0
- package/skills/game-engine/references/game-control-mechanisms.md +617 -0
- package/skills/game-engine/references/game-engine-core-principles.md +695 -0
- package/skills/game-engine/references/game-publishing.md +352 -0
- package/skills/game-engine/references/techniques.md +894 -0
- package/skills/game-engine/references/terminology.md +354 -0
- package/skills/game-engine/references/web-apis.md +1394 -0
- package/skills/theone-cocos-standards/SKILL.md +557 -0
- package/skills/theone-cocos-standards/references/framework/component-system.md +645 -0
- package/skills/theone-cocos-standards/references/framework/event-patterns.md +433 -0
- package/skills/theone-cocos-standards/references/framework/playable-optimization.md +429 -0
- package/skills/theone-cocos-standards/references/framework/size-optimization.md +308 -0
- package/skills/theone-cocos-standards/references/language/modern-typescript.md +658 -0
- package/skills/theone-cocos-standards/references/language/performance.md +580 -0
- package/skills/theone-cocos-standards/references/language/quality-hygiene.md +582 -0
- package/skills/theone-cocos-standards/references/review/architecture-review.md +250 -0
- package/skills/theone-cocos-standards/references/review/performance-review.md +288 -0
- package/skills/theone-cocos-standards/references/review/quality-review.md +239 -0
- package/dist/chunk-RIK7IXSW.js.map +0 -1
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
# 资源管理 — Cocos Creator 3.8
|
|
2
|
+
|
|
3
|
+
> 📖 本文件为 `cocos-creator-3x-cn` 技能参考文件,提供资源管理系统深入指导。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. 资源管理架构
|
|
8
|
+
|
|
9
|
+
Cocos Creator 3.8 使用 **Asset Manager** 进行资源管理,核心模块:
|
|
10
|
+
|
|
11
|
+
| 模块 | 说明 |
|
|
12
|
+
|------|------|
|
|
13
|
+
| `resources` | 加载 `resources` 目录下的资源 |
|
|
14
|
+
| `assetManager` | 全局资源管理器,加载远程/Bundle/自定义资源 |
|
|
15
|
+
| `AssetManager.Bundle` | Asset Bundle 实例,模块化资源加载 |
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 2. resources 动态加载
|
|
20
|
+
|
|
21
|
+
### 2.1 基础加载
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { resources, Prefab, instantiate, SpriteFrame, Sprite, AudioClip, AnimationClip, Animation, JsonAsset } from 'cc';
|
|
25
|
+
|
|
26
|
+
// 加载 Prefab
|
|
27
|
+
resources.load('prefabs/enemy', Prefab, (err, prefab) => {
|
|
28
|
+
if (err) { console.error(err); return; }
|
|
29
|
+
const node = instantiate(prefab);
|
|
30
|
+
this.node.addChild(node);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// 加载 SpriteFrame(注意路径包含子资源名)
|
|
34
|
+
resources.load('images/hero/spriteFrame', SpriteFrame, (err, sf) => {
|
|
35
|
+
this.getComponent(Sprite)!.spriteFrame = sf;
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// 加载 Texture2D
|
|
39
|
+
resources.load('images/bg/texture', Texture2D, (err, texture) => {
|
|
40
|
+
const sf = new SpriteFrame();
|
|
41
|
+
sf.texture = texture;
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// 加载 JSON
|
|
45
|
+
resources.load('configs/level1', JsonAsset, (err, json) => {
|
|
46
|
+
const data = json.json;
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// 加载音频
|
|
50
|
+
resources.load('audio/bgm', AudioClip, (err, clip) => { /* ... */ });
|
|
51
|
+
|
|
52
|
+
// 加载动画
|
|
53
|
+
resources.load('anims/run', AnimationClip, (err, clip) => {
|
|
54
|
+
this.getComponent(Animation)!.addClip(clip, 'run');
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 2.2 图集 SpriteFrame
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import { SpriteAtlas } from 'cc';
|
|
62
|
+
|
|
63
|
+
resources.load('atlas/ui', SpriteAtlas, (err, atlas) => {
|
|
64
|
+
const frame = atlas.getSpriteFrame('btn_normal');
|
|
65
|
+
this.getComponent(Sprite)!.spriteFrame = frame;
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 2.3 批量加载
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
// 加载目录下所有资源
|
|
73
|
+
resources.loadDir('textures', (err, assets) => {
|
|
74
|
+
console.log('Loaded', assets.length, 'assets');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// 指定类型
|
|
78
|
+
resources.loadDir('textures', SpriteFrame, (err, assets) => {
|
|
79
|
+
// assets: SpriteFrame[]
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 2.4 预加载
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
// 预加载(只下载不解析,性能消耗低)
|
|
87
|
+
resources.preload('images/boss/spriteFrame', SpriteFrame);
|
|
88
|
+
|
|
89
|
+
// 后续正常加载会复用预加载的数据,加载更快
|
|
90
|
+
resources.load('images/boss/spriteFrame', SpriteFrame, (err, sf) => {
|
|
91
|
+
// 立即可用
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// 批量预加载
|
|
95
|
+
resources.preloadDir('effects', SpriteFrame);
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## 3. Asset Bundle
|
|
101
|
+
|
|
102
|
+
### 3.1 概述
|
|
103
|
+
|
|
104
|
+
Asset Bundle 是资源模块化工具,允许按需加载资源模块,减少首包大小。
|
|
105
|
+
|
|
106
|
+
**内置 Bundle**:
|
|
107
|
+
|
|
108
|
+
| Bundle | 优先级 | 说明 |
|
|
109
|
+
|--------|--------|------|
|
|
110
|
+
| `internal` | 21 | 引擎内置默认资源 |
|
|
111
|
+
| `start-scene` | 20 | 首场景分包 |
|
|
112
|
+
| `resources` | 8 | resources 目录资源 |
|
|
113
|
+
| `main` | 7 | 参与构建的场景及依赖 |
|
|
114
|
+
|
|
115
|
+
### 3.2 加载 Bundle
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
import { assetManager, Prefab, director } from 'cc';
|
|
119
|
+
|
|
120
|
+
// 通过名称加载
|
|
121
|
+
assetManager.loadBundle('gameBundle', (err, bundle) => {
|
|
122
|
+
if (err) { console.error(err); return; }
|
|
123
|
+
|
|
124
|
+
// 加载 Bundle 中的资源
|
|
125
|
+
bundle.load('prefabs/player', Prefab, (err, prefab) => {
|
|
126
|
+
const node = instantiate(prefab);
|
|
127
|
+
director.getScene()!.addChild(node);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// 通过远程 URL 加载
|
|
132
|
+
assetManager.loadBundle('https://cdn.example.com/bundles/remote', (err, bundle) => {
|
|
133
|
+
bundle.load('xxx', Prefab, (err, prefab) => { /* ... */ });
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// 带版本号加载(热更新)
|
|
137
|
+
assetManager.loadBundle('gameBundle', { version: 'abc123' }, (err, bundle) => {
|
|
138
|
+
// 跳过缓存,加载指定版本
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// 获取已加载的 Bundle
|
|
142
|
+
const bundle = assetManager.getBundle('gameBundle');
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### 3.3 Bundle 资源加载
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
const bundle = assetManager.getBundle('gameBundle')!;
|
|
149
|
+
|
|
150
|
+
// 加载单个资源
|
|
151
|
+
bundle.load('prefabs/enemy', Prefab, (err, prefab) => { /* ... */ });
|
|
152
|
+
|
|
153
|
+
// 加载 SpriteFrame
|
|
154
|
+
bundle.load('images/icon/spriteFrame', SpriteFrame, (err, sf) => { /* ... */ });
|
|
155
|
+
|
|
156
|
+
// 批量加载
|
|
157
|
+
bundle.loadDir('textures', Texture2D, (err, textures) => { /* ... */ });
|
|
158
|
+
|
|
159
|
+
// 加载场景
|
|
160
|
+
bundle.loadScene('level1', (err, scene) => {
|
|
161
|
+
director.runScene(scene);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// 预加载
|
|
165
|
+
bundle.preload('prefabs/boss', Prefab);
|
|
166
|
+
bundle.preloadDir('effects');
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### 3.4 Bundle 释放与移除
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
const bundle = assetManager.getBundle('gameBundle')!;
|
|
173
|
+
|
|
174
|
+
// 释放单个资源
|
|
175
|
+
bundle.release('images/icon', SpriteFrame);
|
|
176
|
+
|
|
177
|
+
// 释放所有资源(慎用!包括外部依赖)
|
|
178
|
+
bundle.releaseAll();
|
|
179
|
+
|
|
180
|
+
// 移除 Bundle(先释放资源,再移除)
|
|
181
|
+
bundle.releaseAll();
|
|
182
|
+
assetManager.removeBundle(bundle);
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## 4. 资源释放
|
|
188
|
+
|
|
189
|
+
### 4.1 手动释放
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
import { assetManager } from 'cc';
|
|
193
|
+
|
|
194
|
+
// 释放单个资源(自动处理依赖)
|
|
195
|
+
assetManager.releaseAsset(asset);
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### 4.2 引用计数管理
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
// 动态加载的资源,引用计数初始为 0
|
|
202
|
+
resources.load('img/spriteFrame', SpriteFrame, (err, sf) => {
|
|
203
|
+
// 需要长期持有时,增加引用
|
|
204
|
+
sf.addRef();
|
|
205
|
+
this._spriteFrame = sf;
|
|
206
|
+
this.getComponent(Sprite)!.spriteFrame = sf;
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// 不再使用时,减少引用
|
|
210
|
+
onDestroy() {
|
|
211
|
+
if (this._spriteFrame) {
|
|
212
|
+
this._spriteFrame.decRef();
|
|
213
|
+
this._spriteFrame = null;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
**引用计数规则**:
|
|
219
|
+
- 加载完成后,资源引用计数初始为 0
|
|
220
|
+
- 资源的静态依赖会自动被统计(引擎自动处理依赖资源的引用计数)
|
|
221
|
+
- 动态引用(脚本中 `load` 后赋值给组件)需要手动 `addRef`/`decRef`
|
|
222
|
+
- 引用计数归 0 时,引擎会尝试自动释放
|
|
223
|
+
|
|
224
|
+
### 4.3 场景自动释放
|
|
225
|
+
|
|
226
|
+
在编辑器中,选中场景节点 → 属性检查器 → 勾选 **自动释放资源**。
|
|
227
|
+
切换场景时会自动释放该场景所有依赖资源。
|
|
228
|
+
|
|
229
|
+
**建议**: 除高频使用的主场景外,其他场景都勾选自动释放。
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## 5. 远程资源加载
|
|
234
|
+
|
|
235
|
+
```typescript
|
|
236
|
+
import { assetManager, ImageAsset, Texture2D, SpriteFrame } from 'cc';
|
|
237
|
+
|
|
238
|
+
// 远程图片(带后缀名)
|
|
239
|
+
assetManager.loadRemote<ImageAsset>('https://example.com/avatar.png', (err, imageAsset) => {
|
|
240
|
+
const texture = new Texture2D();
|
|
241
|
+
texture.image = imageAsset;
|
|
242
|
+
const sf = new SpriteFrame();
|
|
243
|
+
sf.texture = texture;
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// 远程图片(无后缀名需指定 ext)
|
|
247
|
+
assetManager.loadRemote<ImageAsset>('https://example.com/img?id=123', { ext: '.png' }, (err, imageAsset) => {
|
|
248
|
+
// ...
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// 远程音频
|
|
252
|
+
assetManager.loadRemote('https://example.com/sfx.mp3', (err, clip) => { /* ... */ });
|
|
253
|
+
|
|
254
|
+
// 远程文本
|
|
255
|
+
assetManager.loadRemote('https://example.com/data.txt', (err, textAsset) => { /* ... */ });
|
|
256
|
+
|
|
257
|
+
// 本地绝对路径(原生平台)
|
|
258
|
+
assetManager.loadRemote<ImageAsset>('/storage/emulated/0/image.png', (err, img) => { /* ... */ });
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
**限制**:
|
|
262
|
+
- 仅支持原生资源类型(图片、音频、文本)
|
|
263
|
+
- 不支持直接加载 SpriteFrame、SpriteAtlas 等
|
|
264
|
+
- Web 端受 CORS 限制
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## 6. 资源管理最佳实践
|
|
269
|
+
|
|
270
|
+
### 6.1 resources 目录规范
|
|
271
|
+
|
|
272
|
+
```
|
|
273
|
+
assets/
|
|
274
|
+
├── resources/ ← 仅放需要动态加载的资源
|
|
275
|
+
│ ├── prefabs/
|
|
276
|
+
│ ├── configs/
|
|
277
|
+
│ └── dynamic/
|
|
278
|
+
├── textures/ ← 静态引用的资源放外面
|
|
279
|
+
├── scenes/
|
|
280
|
+
└── scripts/
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
**规则**:
|
|
284
|
+
- 仅放 **需要脚本动态加载** 的资源
|
|
285
|
+
- 不需要 `resources.load` 的资源不要放入
|
|
286
|
+
- 放入 resources 会增大 `config.json` 并阻止构建时的资源剔除
|
|
287
|
+
|
|
288
|
+
### 6.2 加载错误处理
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
resources.load('prefabs/player', Prefab, (err, prefab) => {
|
|
292
|
+
if (err) {
|
|
293
|
+
console.error('Failed to load prefab:', err.message);
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
// 安全使用
|
|
297
|
+
});
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### 6.3 加载进度回调
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
resources.load('prefabs/level', Prefab,
|
|
304
|
+
(completed, total) => {
|
|
305
|
+
const progress = completed / total;
|
|
306
|
+
console.log(`Loading: ${(progress * 100).toFixed(0)}%`);
|
|
307
|
+
},
|
|
308
|
+
(err, prefab) => {
|
|
309
|
+
// 完成
|
|
310
|
+
}
|
|
311
|
+
);
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### 6.4 资源释放清单
|
|
315
|
+
|
|
316
|
+
| 场景 | 推荐方案 |
|
|
317
|
+
|------|---------|
|
|
318
|
+
| 场景切换 | 勾选场景自动释放 |
|
|
319
|
+
| 动态加载并长期持有 | `addRef` + `decRef` |
|
|
320
|
+
| 临时加载(一次性使用) | 用完后 `assetManager.releaseAsset()` |
|
|
321
|
+
| Bundle 不再需要 | `bundle.releaseAll()` + `removeBundle()` |
|
|
322
|
+
| 对象池资源 | 池销毁时统一 `decRef` |
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
# 组件系统详解 — Cocos Creator 3.8
|
|
2
|
+
|
|
3
|
+
> 📖 本文件为 `cocos-creator-3x-cn` 技能参考文件,提供组件系统、装饰器、生命周期的深入指导。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. 组件基础架构
|
|
8
|
+
|
|
9
|
+
### 1.1 cc 类注册
|
|
10
|
+
|
|
11
|
+
所有需要被引擎序列化、在编辑器中展示的类都必须使用 `@ccclass` 装饰器注册:
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { _decorator, Component } from 'cc';
|
|
15
|
+
const { ccclass, property } = _decorator;
|
|
16
|
+
|
|
17
|
+
@ccclass('UniqueClassName') // 全局唯一的 cc 类名
|
|
18
|
+
export class UniqueClassName extends Component {
|
|
19
|
+
// ...
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**规则**:
|
|
24
|
+
- cc 类名 **全局唯一**,不同目录下的同名类也不允许
|
|
25
|
+
- 类名不应以 `cc.` 或 `internal.` 作为前缀(引擎保留前缀)
|
|
26
|
+
- 未声明 `@ccclass` 的类无法作为组件添加到节点上
|
|
27
|
+
|
|
28
|
+
### 1.2 组件获取与操作
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
// 获取组件
|
|
32
|
+
const sprite = this.node.getComponent(Sprite); // 单个
|
|
33
|
+
const sprites = this.node.getComponentsInChildren(Sprite); // 递归子节点
|
|
34
|
+
const label = this.getComponent(Label); // 同节点上
|
|
35
|
+
|
|
36
|
+
// 添加/移除组件
|
|
37
|
+
const comp = this.node.addComponent(MyComponent);
|
|
38
|
+
this.node.removeComponent(comp);
|
|
39
|
+
|
|
40
|
+
// 通过 cc 类名获取
|
|
41
|
+
const comp = this.node.getComponent('MyComponent');
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 2. 装饰器详解
|
|
47
|
+
|
|
48
|
+
### 2.1 属性装饰器 @property
|
|
49
|
+
|
|
50
|
+
完整参数列表:
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
@property({
|
|
54
|
+
type: Node, // cc 类型
|
|
55
|
+
visible: true, // 是否在检查器显示
|
|
56
|
+
displayName: '目标', // 显示名
|
|
57
|
+
tooltip: '提示文字', // 悬浮提示
|
|
58
|
+
serializable: true, // 是否序列化
|
|
59
|
+
readonly: false, // 只读
|
|
60
|
+
min: 0, // 最小值
|
|
61
|
+
max: 100, // 最大值
|
|
62
|
+
step: 1, // 步长
|
|
63
|
+
range: [0, 100, 1], // 范围快捷设置
|
|
64
|
+
slide: false, // 滑动条
|
|
65
|
+
multiline: false, // 多行文本
|
|
66
|
+
formerlySerializedAs: 'oldName', // 兼容旧字段名
|
|
67
|
+
editorOnly: false, // 仅编辑器
|
|
68
|
+
override: false, // 覆盖父类同名属性
|
|
69
|
+
group: { name: '分组名', id: 'default', displayOrder: 0, style: 'tab' },
|
|
70
|
+
})
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 2.2 类型声明模式
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
import { _decorator, Component, Node, CCInteger, CCFloat, CCString, CCBoolean, Enum } from 'cc';
|
|
77
|
+
const { ccclass, property, integer, float, type } = _decorator;
|
|
78
|
+
|
|
79
|
+
enum Direction { Up, Down, Left, Right }
|
|
80
|
+
Enum(Direction);
|
|
81
|
+
|
|
82
|
+
@ccclass('TypeExample')
|
|
83
|
+
export class TypeExample extends Component {
|
|
84
|
+
// 自动推导类型
|
|
85
|
+
@property
|
|
86
|
+
private readonly speed = 10; // → CCFloat
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
private readonly name = 'player'; // → CCString
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
private readonly active = true; // → CCBoolean
|
|
93
|
+
|
|
94
|
+
// 显式指定 cc 类型
|
|
95
|
+
@property(Node)
|
|
96
|
+
private readonly target: Node | null = null; // 等价于 @property({type: Node})
|
|
97
|
+
|
|
98
|
+
// 数组类型
|
|
99
|
+
@property({ type: [Node] })
|
|
100
|
+
private readonly children: Node[] = [];
|
|
101
|
+
|
|
102
|
+
// 整数简写
|
|
103
|
+
@integer
|
|
104
|
+
private readonly count = 0;
|
|
105
|
+
|
|
106
|
+
// 浮点简写
|
|
107
|
+
@float
|
|
108
|
+
private readonly ratio = 1.0;
|
|
109
|
+
|
|
110
|
+
// 枚举类型
|
|
111
|
+
@property({ type: Direction })
|
|
112
|
+
private readonly dir: Direction = Direction.Up;
|
|
113
|
+
|
|
114
|
+
// 基础类型数组
|
|
115
|
+
@property({ type: [CCInteger] })
|
|
116
|
+
private readonly scores: number[] = [];
|
|
117
|
+
|
|
118
|
+
// 以 _ 开头:序列化但不显示在面板
|
|
119
|
+
@property
|
|
120
|
+
private readonly _cachedValue = '';
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### 2.3 组件类装饰器
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
const { ccclass, executeInEditMode, requireComponent, executionOrder, disallowMultiple, menu, help } = _decorator;
|
|
128
|
+
|
|
129
|
+
@ccclass('AdvancedComponent')
|
|
130
|
+
@executeInEditMode(true) // 编辑器模式下也执行生命周期
|
|
131
|
+
@requireComponent(Sprite) // 自动添加依赖组件
|
|
132
|
+
@executionOrder(-1) // 优先执行(数值越小越先)
|
|
133
|
+
@disallowMultiple(true) // 同一节点禁止重复添加
|
|
134
|
+
@menu('Custom/Advanced') // 组件菜单路径
|
|
135
|
+
@help('https://docs.example.com') // 帮助文档 URL
|
|
136
|
+
export class AdvancedComponent extends Component {
|
|
137
|
+
// ...
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**executionOrder 规则**:
|
|
142
|
+
- 小于 0 → 优先执行
|
|
143
|
+
- 等于 0 → 默认
|
|
144
|
+
- 大于 0 → 最后执行
|
|
145
|
+
- 仅影响 `onLoad`、`onEnable`、`start`、`update`、`lateUpdate`
|
|
146
|
+
- 对 `onDisable` 和 `onDestroy` 无效
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## 3. 生命周期详解
|
|
151
|
+
|
|
152
|
+
### 3.1 完整生命周期流程
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
┌──────────────────────────────────────────────────────┐
|
|
156
|
+
│ 初始化阶段 │
|
|
157
|
+
│ ┌─────────┐ │
|
|
158
|
+
│ │ onLoad │ ← 节点首次激活(只调用一次) │
|
|
159
|
+
│ └────┬────┘ │
|
|
160
|
+
│ ▼ │
|
|
161
|
+
│ ┌──────────┐ │
|
|
162
|
+
│ │ onEnable │ ← 组件每次启用 │
|
|
163
|
+
│ └────┬─────┘ │
|
|
164
|
+
│ ▼ │
|
|
165
|
+
│ ┌─────────┐ │
|
|
166
|
+
│ │ start │ ← 首次 update 前(只调用一次) │
|
|
167
|
+
│ └────┬────┘ │
|
|
168
|
+
├───────┼──────────────────────────────────────────────┤
|
|
169
|
+
│ ▼ 游戏循环 │
|
|
170
|
+
│ ┌─────────┐ │
|
|
171
|
+
│ │ update │ ← 每帧调用 │
|
|
172
|
+
│ └────┬────┘ │
|
|
173
|
+
│ ▼ │
|
|
174
|
+
│ ┌────────────┐ │
|
|
175
|
+
│ │ lateUpdate │ ← 所有 update 执行完后 │
|
|
176
|
+
│ └────────────┘ │
|
|
177
|
+
├──────────────────────────────────────────────────────┤
|
|
178
|
+
│ 销毁阶段 │
|
|
179
|
+
│ ┌───────────┐ │
|
|
180
|
+
│ │ onDisable │ ← 组件每次禁用 │
|
|
181
|
+
│ └────┬──────┘ │
|
|
182
|
+
│ ▼ │
|
|
183
|
+
│ ┌─────────────┐ │
|
|
184
|
+
│ │ onDestroy │ ← 节点销毁前 │
|
|
185
|
+
│ └─────────────┘ │
|
|
186
|
+
└──────────────────────────────────────────────────────┘
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### 3.2 各回调最佳实践
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
@ccclass('LifecycleDemo')
|
|
193
|
+
export class LifecycleDemo extends Component {
|
|
194
|
+
private _pool: NodePool | null = null;
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* onLoad: 初始化引用、设置常量
|
|
198
|
+
* - 此时节点已挂载到节点树
|
|
199
|
+
* - 可安全获取其他组件引用
|
|
200
|
+
* - 不要在此处理依赖其他组件 onLoad 完成的逻辑
|
|
201
|
+
*/
|
|
202
|
+
protected onLoad(): void {
|
|
203
|
+
this._pool = new NodePool('Bullet');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* onEnable: 注册事件监听
|
|
208
|
+
* - 与 onDisable 配对
|
|
209
|
+
* - 可能被多次调用
|
|
210
|
+
*/
|
|
211
|
+
protected onEnable(): void {
|
|
212
|
+
input.on(Input.EventType.TOUCH_START, this.onTouch, this);
|
|
213
|
+
this.node.on(Node.EventType.TOUCH_START, this.onNodeTouch, this);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* start: 依赖初始化
|
|
218
|
+
* - 可确保同场景所有组件的 onLoad 已完成
|
|
219
|
+
* - 只调用一次
|
|
220
|
+
*/
|
|
221
|
+
protected start(): void {
|
|
222
|
+
const otherComp = find('Canvas/Manager')?.getComponent(GameManager);
|
|
223
|
+
// 安全使用 otherComp
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* update: 每帧逻辑
|
|
228
|
+
* - dt 为上一帧耗时(秒)
|
|
229
|
+
*/
|
|
230
|
+
protected update(dt: number): void {
|
|
231
|
+
this.node.angle += this.rotateSpeed * dt;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* lateUpdate: 后处理
|
|
236
|
+
* - 在所有 update 之后
|
|
237
|
+
* - 适合相机跟随、最终位置调整
|
|
238
|
+
*/
|
|
239
|
+
protected lateUpdate(dt: number): void {
|
|
240
|
+
// 相机跟随逻辑
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* onDisable: 注销事件监听
|
|
245
|
+
* - 与 onEnable 配对
|
|
246
|
+
*/
|
|
247
|
+
protected onDisable(): void {
|
|
248
|
+
input.off(Input.EventType.TOUCH_START, this.onTouch, this);
|
|
249
|
+
this.node.off(Node.EventType.TOUCH_START, this.onNodeTouch, this);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* onDestroy: 清理资源
|
|
254
|
+
* - 释放动态加载的资源
|
|
255
|
+
* - 清理对象池
|
|
256
|
+
*/
|
|
257
|
+
protected onDestroy(): void {
|
|
258
|
+
this._pool?.clear();
|
|
259
|
+
this._pool = null;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## 4. 节点操作
|
|
267
|
+
|
|
268
|
+
### 4.1 节点创建与销毁
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
import { instantiate, Prefab, Node, director } from 'cc';
|
|
272
|
+
|
|
273
|
+
// 从 Prefab 实例化
|
|
274
|
+
resources.load('prefabs/bullet', Prefab, (err, prefab) => {
|
|
275
|
+
const bullet = instantiate(prefab);
|
|
276
|
+
this.node.addChild(bullet);
|
|
277
|
+
bullet.setPosition(0, 0, 0);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
// 销毁节点(延迟到当帧末尾执行)
|
|
281
|
+
bullet.destroy();
|
|
282
|
+
|
|
283
|
+
// 仅从父节点移除(不销毁)
|
|
284
|
+
bullet.removeFromParent();
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### 4.2 节点层级操作
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
290
|
+
// 设置父节点
|
|
291
|
+
node.parent = targetParent;
|
|
292
|
+
node.setParent(targetParent);
|
|
293
|
+
|
|
294
|
+
// 排序
|
|
295
|
+
node.setSiblingIndex(0); // 移到兄弟节点最前
|
|
296
|
+
|
|
297
|
+
// 激活/禁用
|
|
298
|
+
node.active = false; // 会触发 onDisable
|
|
299
|
+
node.active = true; // 会触发 onEnable
|
|
300
|
+
|
|
301
|
+
// 遍历子节点
|
|
302
|
+
this.node.children.forEach(child => { /* ... */ });
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### 4.3 坐标系转换
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
import { Vec3, UITransform } from 'cc';
|
|
309
|
+
|
|
310
|
+
// 世界坐标 ↔ 本地坐标
|
|
311
|
+
const worldPos = this.node.worldPosition;
|
|
312
|
+
const localPos = parent.getComponent(UITransform)!
|
|
313
|
+
.convertToNodeSpaceAR(worldPos);
|
|
314
|
+
|
|
315
|
+
// 屏幕坐标 → UI 坐标
|
|
316
|
+
const uiPos = event.getUILocation(); // 在触摸事件中使用
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
## 5. 定时器与调度
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
// schedule: 重复调度
|
|
325
|
+
this.schedule(this.tick, 1.0); // 每秒
|
|
326
|
+
this.schedule(this.tick, 0.5, 10, 2); // 延迟2秒,每0.5秒,共10次
|
|
327
|
+
|
|
328
|
+
// scheduleOnce: 延迟调用一次
|
|
329
|
+
this.scheduleOnce(() => {
|
|
330
|
+
console.log('delayed');
|
|
331
|
+
}, 2.0);
|
|
332
|
+
|
|
333
|
+
// 取消调度
|
|
334
|
+
this.unschedule(this.tick);
|
|
335
|
+
this.unscheduleAllCallbacks();
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
## 6. 常见反模式
|
|
341
|
+
|
|
342
|
+
| 反模式 | 问题 | 修正 |
|
|
343
|
+
|--------|------|------|
|
|
344
|
+
| 在 `onLoad` 中访问其他组件的 `start` 才初始化的数据 | 数据可能为空 | 移到 `start` 或添加 null 检查 |
|
|
345
|
+
| 在 `onEnable` 注册事件但不在 `onDisable` 取消 | 事件泄漏 | 配对使用 `on`/`off` |
|
|
346
|
+
| 在 `update` 中创建大量临时对象 | GC 压力 | 使用类字段缓存临时变量 |
|
|
347
|
+
| 使用字符串注册事件类型 | 易拼写错误 | 使用 `Node.EventType.TOUCH_START` 枚举 |
|
|
348
|
+
| `@property` 类型不写 `| null` | 运行时空指针 | 使用 `Type \| null = null` 或 `Type = null!` |
|