@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,645 @@
|
|
|
1
|
+
# Cocos Creator Component System
|
|
2
|
+
|
|
3
|
+
## Entity-Component (EC) System Overview
|
|
4
|
+
|
|
5
|
+
Cocos Creator uses an Entity-Component (EC) architecture where:
|
|
6
|
+
- **Node** = Entity (game object container)
|
|
7
|
+
- **Component** = Behavior/functionality attached to Node
|
|
8
|
+
- **Scene** = Collection of Node hierarchies
|
|
9
|
+
|
|
10
|
+
```typescript
|
|
11
|
+
import { _decorator, Component, Node } from 'cc';
|
|
12
|
+
const { ccclass, property } = _decorator;
|
|
13
|
+
|
|
14
|
+
// ✅ EXCELLENT: Complete component structure
|
|
15
|
+
@ccclass('PlayerController')
|
|
16
|
+
export class PlayerController extends Component {
|
|
17
|
+
// @property decorator exposes fields to Inspector
|
|
18
|
+
@property(Node)
|
|
19
|
+
private readonly targetNode: Node | null = null;
|
|
20
|
+
|
|
21
|
+
@property(Number)
|
|
22
|
+
private readonly moveSpeed: number = 100;
|
|
23
|
+
|
|
24
|
+
// Private fields not exposed
|
|
25
|
+
private currentHealth: number = 100;
|
|
26
|
+
private static readonly MAX_HEALTH: number = 100;
|
|
27
|
+
|
|
28
|
+
// Lifecycle methods in execution order:
|
|
29
|
+
// 1. onLoad() - Component initialization
|
|
30
|
+
// 2. start() - After all components loaded
|
|
31
|
+
// 3. onEnable() - When enabled (can be called multiple times)
|
|
32
|
+
// 4. update(dt) - Every frame
|
|
33
|
+
// 5. lateUpdate(dt) - After all update() calls
|
|
34
|
+
// 6. onDisable() - When disabled
|
|
35
|
+
// 7. onDestroy() - Cleanup
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## @ccclass Decorator
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
import { _decorator, Component } from 'cc';
|
|
43
|
+
const { ccclass } = _decorator;
|
|
44
|
+
|
|
45
|
+
// ✅ EXCELLENT: @ccclass with explicit name
|
|
46
|
+
@ccclass('GameManager')
|
|
47
|
+
export class GameManager extends Component {
|
|
48
|
+
// Component implementation
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ✅ GOOD: @ccclass without name (uses class name)
|
|
52
|
+
@ccclass
|
|
53
|
+
export class PlayerController extends Component {
|
|
54
|
+
// Component implementation
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ❌ WRONG: Missing @ccclass decorator
|
|
58
|
+
export class GameManager extends Component {
|
|
59
|
+
// Won't work - Cocos can't serialize this component
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ❌ WRONG: Not extending Component
|
|
63
|
+
@ccclass('GameManager')
|
|
64
|
+
export class GameManager {
|
|
65
|
+
// Won't work - must extend Component
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## @property Decorator
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import { _decorator, Component, Node, Sprite, Label } from 'cc';
|
|
73
|
+
const { ccclass, property } = _decorator;
|
|
74
|
+
|
|
75
|
+
@ccclass('PropertyExamples')
|
|
76
|
+
export class PropertyExamples extends Component {
|
|
77
|
+
// ✅ EXCELLENT: Node reference
|
|
78
|
+
@property(Node)
|
|
79
|
+
private readonly playerNode: Node | null = null;
|
|
80
|
+
|
|
81
|
+
// ✅ EXCELLENT: Component reference
|
|
82
|
+
@property(Sprite)
|
|
83
|
+
private readonly spriteComponent: Sprite | null = null;
|
|
84
|
+
|
|
85
|
+
// ✅ EXCELLENT: Primitive types
|
|
86
|
+
@property(Number)
|
|
87
|
+
private readonly moveSpeed: number = 100;
|
|
88
|
+
|
|
89
|
+
@property(String)
|
|
90
|
+
private readonly playerName: string = 'Player';
|
|
91
|
+
|
|
92
|
+
@property(Boolean)
|
|
93
|
+
private readonly enableDebug: boolean = false;
|
|
94
|
+
|
|
95
|
+
// ✅ EXCELLENT: Array of nodes
|
|
96
|
+
@property([Node])
|
|
97
|
+
private readonly enemyNodes: Node[] = [];
|
|
98
|
+
|
|
99
|
+
// ✅ EXCELLENT: Array of numbers
|
|
100
|
+
@property([Number])
|
|
101
|
+
private readonly levelScores: number[] = [];
|
|
102
|
+
|
|
103
|
+
// ✅ EXCELLENT: Enum property
|
|
104
|
+
@property({ type: Enum(GameState) })
|
|
105
|
+
private currentState: GameState = GameState.LOADING;
|
|
106
|
+
|
|
107
|
+
// ✅ EXCELLENT: Property with custom display name and tooltip
|
|
108
|
+
@property({
|
|
109
|
+
type: Number,
|
|
110
|
+
displayName: 'Movement Speed',
|
|
111
|
+
tooltip: 'Player movement speed in units per second',
|
|
112
|
+
min: 0,
|
|
113
|
+
max: 500,
|
|
114
|
+
step: 10,
|
|
115
|
+
})
|
|
116
|
+
private readonly speed: number = 100;
|
|
117
|
+
|
|
118
|
+
// ✅ EXCELLENT: readonly for properties that shouldn't be reassigned
|
|
119
|
+
@property(Node)
|
|
120
|
+
private readonly targetNode: Node | null = null; // Can't reassign after initialization
|
|
121
|
+
|
|
122
|
+
// Private field (not exposed to Inspector)
|
|
123
|
+
private currentHealth: number = 100;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ❌ WRONG: Property without type
|
|
127
|
+
@property
|
|
128
|
+
private playerNode: Node | null = null; // Won't serialize correctly
|
|
129
|
+
|
|
130
|
+
// ❌ WRONG: Mutable property that should be readonly
|
|
131
|
+
@property(Node)
|
|
132
|
+
private targetNode: Node | null = null; // Should be readonly if not reassigned
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Component Lifecycle Methods
|
|
136
|
+
|
|
137
|
+
### 1. onLoad() - Initialization
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
import { _decorator, Component, Node } from 'cc';
|
|
141
|
+
const { ccclass, property } = _decorator;
|
|
142
|
+
|
|
143
|
+
@ccclass('GameManager')
|
|
144
|
+
export class GameManager extends Component {
|
|
145
|
+
@property(Node)
|
|
146
|
+
private readonly playerNode: Node | null = null;
|
|
147
|
+
|
|
148
|
+
@property(Node)
|
|
149
|
+
private readonly uiRoot: Node | null = null;
|
|
150
|
+
|
|
151
|
+
// ✅ EXCELLENT: onLoad for initialization and validation
|
|
152
|
+
protected onLoad(): void {
|
|
153
|
+
// Validate required references
|
|
154
|
+
if (!this.playerNode) {
|
|
155
|
+
throw new Error('GameManager: playerNode is required');
|
|
156
|
+
}
|
|
157
|
+
if (!this.uiRoot) {
|
|
158
|
+
throw new Error('GameManager: uiRoot is required');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Initialize component state
|
|
162
|
+
this.initializeGameState();
|
|
163
|
+
|
|
164
|
+
// Cache references (DO NOT reference other components yet)
|
|
165
|
+
this.cacheNodeReferences();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
private initializeGameState(): void {
|
|
169
|
+
// Setup initial state
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
private cacheNodeReferences(): void {
|
|
173
|
+
// Cache child nodes for faster access
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// ❌ WRONG: Accessing other components in onLoad
|
|
178
|
+
protected onLoad(): void {
|
|
179
|
+
// Don't do this - other components may not be loaded yet
|
|
180
|
+
const playerController = this.playerNode!.getComponent(PlayerController);
|
|
181
|
+
playerController.initialize(); // May be undefined!
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ❌ WRONG: Heavy operations in onLoad
|
|
185
|
+
protected onLoad(): void {
|
|
186
|
+
// Avoid expensive operations - onLoad should be fast
|
|
187
|
+
this.loadAllLevelData(); // Should be async in start()
|
|
188
|
+
this.generateProceduralContent(); // Too expensive for onLoad
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### 2. start() - Post-Initialization
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
import { _decorator, Component, Node } from 'cc';
|
|
196
|
+
const { ccclass, property } = _decorator;
|
|
197
|
+
|
|
198
|
+
@ccclass('PlayerController')
|
|
199
|
+
export class PlayerController extends Component {
|
|
200
|
+
@property(Node)
|
|
201
|
+
private readonly enemyManagerNode: Node | null = null;
|
|
202
|
+
|
|
203
|
+
private enemyManager!: EnemyManager;
|
|
204
|
+
|
|
205
|
+
protected onLoad(): void {
|
|
206
|
+
// Validate references
|
|
207
|
+
if (!this.enemyManagerNode) {
|
|
208
|
+
throw new Error('PlayerController: enemyManagerNode is required');
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// ✅ EXCELLENT: start() for referencing other components
|
|
213
|
+
protected start(): void {
|
|
214
|
+
// Safe to get components from other nodes now
|
|
215
|
+
const enemyManager = this.enemyManagerNode!.getComponent(EnemyManager);
|
|
216
|
+
if (!enemyManager) {
|
|
217
|
+
throw new Error('EnemyManager component not found');
|
|
218
|
+
}
|
|
219
|
+
this.enemyManager = enemyManager;
|
|
220
|
+
|
|
221
|
+
// Initialize based on other components
|
|
222
|
+
this.setupPlayerBasedOnEnemies();
|
|
223
|
+
|
|
224
|
+
// Start async operations
|
|
225
|
+
this.loadPlayerDataAsync();
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
private setupPlayerBasedOnEnemies(): void {
|
|
229
|
+
const enemyCount = this.enemyManager.getEnemyCount();
|
|
230
|
+
this.adjustDifficultyBasedOnEnemies(enemyCount);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
private async loadPlayerDataAsync(): Promise<void> {
|
|
234
|
+
// Async loading is safe in start()
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// ❌ WRONG: Using start() instead of onLoad for validation
|
|
239
|
+
protected start(): void {
|
|
240
|
+
// Too late - might be used before start() is called
|
|
241
|
+
if (!this.playerNode) {
|
|
242
|
+
throw new Error('playerNode is required');
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### 3. onEnable() - Activation
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
import { _decorator, Component, Node, EventTouch } from 'cc';
|
|
251
|
+
const { ccclass, property } = _decorator;
|
|
252
|
+
|
|
253
|
+
@ccclass('InputHandler')
|
|
254
|
+
export class InputHandler extends Component {
|
|
255
|
+
@property(Node)
|
|
256
|
+
private readonly buttonNode: Node | null = null;
|
|
257
|
+
|
|
258
|
+
// ✅ EXCELLENT: onEnable() for registering listeners
|
|
259
|
+
protected onEnable(): void {
|
|
260
|
+
// Register event listeners
|
|
261
|
+
if (this.buttonNode) {
|
|
262
|
+
this.buttonNode.on(Node.EventType.TOUCH_START, this.onTouchStart, this);
|
|
263
|
+
this.buttonNode.on(Node.EventType.TOUCH_END, this.onTouchEnd, this);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Subscribe to global events
|
|
267
|
+
EventManager.on(GameEvent.LEVEL_COMPLETE, this.onLevelComplete, this);
|
|
268
|
+
|
|
269
|
+
// Resume component operations
|
|
270
|
+
this.resumeGameLogic();
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
protected onDisable(): void {
|
|
274
|
+
// ✅ CRITICAL: Always unregister in onDisable
|
|
275
|
+
if (this.buttonNode) {
|
|
276
|
+
this.buttonNode.off(Node.EventType.TOUCH_START, this.onTouchStart, this);
|
|
277
|
+
this.buttonNode.off(Node.EventType.TOUCH_END, this.onTouchEnd, this);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
EventManager.off(GameEvent.LEVEL_COMPLETE, this.onLevelComplete, this);
|
|
281
|
+
|
|
282
|
+
// Pause component operations
|
|
283
|
+
this.pauseGameLogic();
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
private onTouchStart(event: EventTouch): void {
|
|
287
|
+
// Handle touch
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
private onTouchEnd(event: EventTouch): void {
|
|
291
|
+
// Handle touch end
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
private onLevelComplete(): void {
|
|
295
|
+
// Handle level complete
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// ❌ WRONG: Registering listeners in onLoad
|
|
300
|
+
protected onLoad(): void {
|
|
301
|
+
// Don't register here - won't be unregistered properly when disabled
|
|
302
|
+
this.node.on(Node.EventType.TOUCH_START, this.onTouchStart, this);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// ❌ WRONG: Not unregistering in onDisable
|
|
306
|
+
protected onEnable(): void {
|
|
307
|
+
this.node.on(Node.EventType.TOUCH_START, this.onTouchStart, this);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
protected onDisable(): void {
|
|
311
|
+
// Missing unregistration - memory leak!
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### 4. update(dt) - Per-Frame Logic
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
import { _decorator, Component, Node, Vec3 } from 'cc';
|
|
319
|
+
const { ccclass, property } = _decorator;
|
|
320
|
+
|
|
321
|
+
@ccclass('PlayerMovement')
|
|
322
|
+
export class PlayerMovement extends Component {
|
|
323
|
+
@property(Number)
|
|
324
|
+
private readonly moveSpeed: number = 100;
|
|
325
|
+
|
|
326
|
+
private readonly tempVec3: Vec3 = new Vec3();
|
|
327
|
+
private inputDirection: Vec3 = new Vec3(1, 0, 0);
|
|
328
|
+
|
|
329
|
+
// ✅ EXCELLENT: Efficient update implementation
|
|
330
|
+
protected update(dt: number): void {
|
|
331
|
+
// Reuse preallocated vector
|
|
332
|
+
this.node.getPosition(this.tempVec3);
|
|
333
|
+
|
|
334
|
+
// Calculate movement
|
|
335
|
+
this.tempVec3.x += this.inputDirection.x * this.moveSpeed * dt;
|
|
336
|
+
this.tempVec3.y += this.inputDirection.y * this.moveSpeed * dt;
|
|
337
|
+
|
|
338
|
+
// Apply new position
|
|
339
|
+
this.node.setPosition(this.tempVec3);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Throttled expensive operations
|
|
344
|
+
@ccclass('AIController')
|
|
345
|
+
export class AIController extends Component {
|
|
346
|
+
private frameCount: number = 0;
|
|
347
|
+
private static readonly AI_UPDATE_INTERVAL: number = 10;
|
|
348
|
+
|
|
349
|
+
// ✅ EXCELLENT: Throttle expensive operations
|
|
350
|
+
protected update(dt: number): void {
|
|
351
|
+
this.frameCount++;
|
|
352
|
+
|
|
353
|
+
// Cheap operations every frame
|
|
354
|
+
this.moveTowardsTarget(dt);
|
|
355
|
+
|
|
356
|
+
// Expensive AI decisions every 10 frames
|
|
357
|
+
if (this.frameCount % AIController.AI_UPDATE_INTERVAL === 0) {
|
|
358
|
+
this.updateAIDecision();
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
private moveTowardsTarget(dt: number): void {
|
|
363
|
+
// Simple movement calculation
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
private updateAIDecision(): void {
|
|
367
|
+
// Complex AI logic
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// ❌ WRONG: Allocations in update
|
|
372
|
+
protected update(dt: number): void {
|
|
373
|
+
const currentPos = this.node.position.clone(); // Allocates every frame!
|
|
374
|
+
currentPos.x += this.moveSpeed * dt;
|
|
375
|
+
this.node.setPosition(currentPos);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// ❌ WRONG: Expensive operations every frame
|
|
379
|
+
protected update(dt: number): void {
|
|
380
|
+
this.recalculatePathfinding(); // A* algorithm 60 times per second!
|
|
381
|
+
this.updateComplexAI(); // Too expensive for every frame
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// ❌ WRONG: Component lookups in update
|
|
385
|
+
protected update(dt: number): void {
|
|
386
|
+
const sprite = this.node.getComponent(Sprite); // Cache this in onLoad!
|
|
387
|
+
sprite?.doSomething();
|
|
388
|
+
}
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### 5. lateUpdate(dt) - Post-Update Logic
|
|
392
|
+
|
|
393
|
+
```typescript
|
|
394
|
+
import { _decorator, Component, Node, Camera } from 'cc';
|
|
395
|
+
const { ccclass, property } = _decorator;
|
|
396
|
+
|
|
397
|
+
@ccclass('CameraFollow')
|
|
398
|
+
export class CameraFollow extends Component {
|
|
399
|
+
@property(Node)
|
|
400
|
+
private readonly target: Node | null = null;
|
|
401
|
+
|
|
402
|
+
@property(Camera)
|
|
403
|
+
private readonly camera: Camera | null = null;
|
|
404
|
+
|
|
405
|
+
// ✅ EXCELLENT: lateUpdate for camera following
|
|
406
|
+
// Runs after all update() calls, ensuring target has moved
|
|
407
|
+
protected lateUpdate(dt: number): void {
|
|
408
|
+
if (!this.target || !this.camera) return;
|
|
409
|
+
|
|
410
|
+
// Follow target position after target has been updated
|
|
411
|
+
const targetPos = this.target.position;
|
|
412
|
+
this.camera.node.setPosition(targetPos.x, targetPos.y, this.camera.node.position.z);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// ✅ GOOD: lateUpdate for UI that depends on game state
|
|
417
|
+
@ccclass('HealthBarUpdater')
|
|
418
|
+
export class HealthBarUpdater extends Component {
|
|
419
|
+
@property(Node)
|
|
420
|
+
private readonly healthBar: Node | null = null;
|
|
421
|
+
|
|
422
|
+
private playerHealth: number = 100;
|
|
423
|
+
|
|
424
|
+
// Health is updated in PlayerController.update()
|
|
425
|
+
// UI is updated in lateUpdate() to reflect final health value
|
|
426
|
+
protected lateUpdate(dt: number): void {
|
|
427
|
+
if (!this.healthBar) return;
|
|
428
|
+
|
|
429
|
+
const healthPercentage = this.playerHealth / 100;
|
|
430
|
+
this.healthBar.scale = new Vec3(healthPercentage, 1, 1);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// ❌ WRONG: Using lateUpdate for regular logic
|
|
435
|
+
protected lateUpdate(dt: number): void {
|
|
436
|
+
// This should be in update(), not lateUpdate()
|
|
437
|
+
this.movePlayer(dt);
|
|
438
|
+
}
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### 6. onDestroy() - Cleanup
|
|
442
|
+
|
|
443
|
+
```typescript
|
|
444
|
+
import { _decorator, Component, Node } from 'cc';
|
|
445
|
+
const { ccclass, property } = _decorator;
|
|
446
|
+
|
|
447
|
+
@ccclass('ResourceManager')
|
|
448
|
+
export class ResourceManager extends Component {
|
|
449
|
+
private readonly loadedAssets: Map<string, Asset> = new Map();
|
|
450
|
+
private readonly eventListeners: Set<Function> = new Set();
|
|
451
|
+
private readonly scheduledCallbacks: Set<Function> = new Set();
|
|
452
|
+
|
|
453
|
+
// ✅ EXCELLENT: Complete cleanup in onDestroy
|
|
454
|
+
protected onDestroy(): void {
|
|
455
|
+
// Unregister all event listeners
|
|
456
|
+
this.node.off(Node.EventType.TOUCH_START);
|
|
457
|
+
EventManager.off(GameEvent.LEVEL_COMPLETE, this.onLevelComplete, this);
|
|
458
|
+
|
|
459
|
+
// Clear collections
|
|
460
|
+
this.eventListeners.clear();
|
|
461
|
+
this.scheduledCallbacks.clear();
|
|
462
|
+
|
|
463
|
+
// Release loaded assets
|
|
464
|
+
for (const [id, asset] of this.loadedAssets) {
|
|
465
|
+
asset.decRef();
|
|
466
|
+
}
|
|
467
|
+
this.loadedAssets.clear();
|
|
468
|
+
|
|
469
|
+
// Unschedule all callbacks
|
|
470
|
+
this.unscheduleAllCallbacks();
|
|
471
|
+
|
|
472
|
+
// Clear any references to prevent memory leaks
|
|
473
|
+
this.clearReferences();
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
private clearReferences(): void {
|
|
477
|
+
// Clear any cached references
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// ❌ WRONG: Missing cleanup
|
|
482
|
+
protected onDestroy(): void {
|
|
483
|
+
// Forgot to unregister events - memory leak!
|
|
484
|
+
// Forgot to release assets - memory leak!
|
|
485
|
+
// Forgot to unschedule callbacks - may cause errors!
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// ❌ WRONG: Incomplete cleanup
|
|
489
|
+
protected onDestroy(): void {
|
|
490
|
+
this.loadedAssets.clear(); // Cleared map but didn't decRef assets!
|
|
491
|
+
}
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
## Component Execution Order
|
|
495
|
+
|
|
496
|
+
```typescript
|
|
497
|
+
// Execution order when scene loads:
|
|
498
|
+
// 1. All components: onLoad() (in hierarchy order)
|
|
499
|
+
// 2. All components: start() (in hierarchy order)
|
|
500
|
+
// 3. All components: onEnable() (if not already enabled)
|
|
501
|
+
// 4. Begin frame loop:
|
|
502
|
+
// - All components: update(dt)
|
|
503
|
+
// - All components: lateUpdate(dt)
|
|
504
|
+
// 5. When component disabled:
|
|
505
|
+
// - Component: onDisable()
|
|
506
|
+
// 6. When component destroyed:
|
|
507
|
+
// - Component: onDestroy()
|
|
508
|
+
|
|
509
|
+
// ✅ EXCELLENT: Lifecycle method organization
|
|
510
|
+
@ccclass('CompleteLifecycle')
|
|
511
|
+
export class CompleteLifecycle extends Component {
|
|
512
|
+
// 1. INITIALIZATION PHASE
|
|
513
|
+
protected onLoad(): void {
|
|
514
|
+
// Initialize component
|
|
515
|
+
// Validate required references
|
|
516
|
+
// Cache node references
|
|
517
|
+
// DO NOT access other components yet
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
protected start(): void {
|
|
521
|
+
// Access other components (safe now)
|
|
522
|
+
// Start async operations
|
|
523
|
+
// Initialize based on other components
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// 2. ACTIVATION PHASE
|
|
527
|
+
protected onEnable(): void {
|
|
528
|
+
// Register event listeners
|
|
529
|
+
// Subscribe to global events
|
|
530
|
+
// Resume operations
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// 3. UPDATE PHASE
|
|
534
|
+
protected update(dt: number): void {
|
|
535
|
+
// Per-frame game logic
|
|
536
|
+
// Movement, input, AI
|
|
537
|
+
// Keep allocations zero
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
protected lateUpdate(dt: number): void {
|
|
541
|
+
// Logic that depends on update()
|
|
542
|
+
// Camera follow, UI updates
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// 4. DEACTIVATION PHASE
|
|
546
|
+
protected onDisable(): void {
|
|
547
|
+
// Unregister event listeners
|
|
548
|
+
// Unsubscribe from events
|
|
549
|
+
// Pause operations
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// 5. CLEANUP PHASE
|
|
553
|
+
protected onDestroy(): void {
|
|
554
|
+
// Release resources
|
|
555
|
+
// Clear collections
|
|
556
|
+
// Unschedule callbacks
|
|
557
|
+
// Final cleanup
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
## Required Reference Validation
|
|
563
|
+
|
|
564
|
+
```typescript
|
|
565
|
+
import { _decorator, Component, Node, Sprite } from 'cc';
|
|
566
|
+
const { ccclass, property } = _decorator;
|
|
567
|
+
|
|
568
|
+
@ccclass('RequiredReferences')
|
|
569
|
+
export class RequiredReferences extends Component {
|
|
570
|
+
@property(Node)
|
|
571
|
+
private readonly targetNode: Node | null = null;
|
|
572
|
+
|
|
573
|
+
@property(Sprite)
|
|
574
|
+
private readonly spriteComponent: Sprite | null = null;
|
|
575
|
+
|
|
576
|
+
@property([Node])
|
|
577
|
+
private readonly enemyNodes: Node[] = [];
|
|
578
|
+
|
|
579
|
+
// ✅ EXCELLENT: Validate all required references in onLoad
|
|
580
|
+
protected onLoad(): void {
|
|
581
|
+
if (!this.targetNode) {
|
|
582
|
+
throw new Error('RequiredReferences: targetNode is required');
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
if (!this.spriteComponent) {
|
|
586
|
+
throw new Error('RequiredReferences: spriteComponent is required');
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
if (this.enemyNodes.length === 0) {
|
|
590
|
+
throw new Error('RequiredReferences: at least one enemy node is required');
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// All references validated - safe to use
|
|
594
|
+
this.initialize();
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
private initialize(): void {
|
|
598
|
+
// Can safely use all references here
|
|
599
|
+
this.targetNode!.setPosition(0, 0, 0);
|
|
600
|
+
this.spriteComponent!.sizeMode = Sprite.SizeMode.CUSTOM;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// ❌ WRONG: No validation
|
|
605
|
+
protected onLoad(): void {
|
|
606
|
+
// Assuming references exist - may crash at runtime
|
|
607
|
+
this.targetNode!.setPosition(0, 0, 0);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// ❌ WRONG: Silent validation
|
|
611
|
+
protected onLoad(): void {
|
|
612
|
+
if (!this.targetNode) {
|
|
613
|
+
console.error('targetNode is missing'); // Don't just log
|
|
614
|
+
return; // Silent failure
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
## Summary: Component System Checklist
|
|
620
|
+
|
|
621
|
+
**Component Structure:**
|
|
622
|
+
- [ ] @ccclass decorator on class
|
|
623
|
+
- [ ] Extends Component base class
|
|
624
|
+
- [ ] @property decorator for Inspector-exposed fields
|
|
625
|
+
- [ ] readonly for properties that aren't reassigned
|
|
626
|
+
- [ ] Access modifiers (public/private/protected)
|
|
627
|
+
|
|
628
|
+
**Lifecycle Implementation:**
|
|
629
|
+
- [ ] onLoad() - Validate required references, initialize state
|
|
630
|
+
- [ ] start() - Access other components, start async operations
|
|
631
|
+
- [ ] onEnable() - Register event listeners
|
|
632
|
+
- [ ] update(dt) - Per-frame logic (zero allocations)
|
|
633
|
+
- [ ] lateUpdate(dt) - Post-update logic (camera, UI)
|
|
634
|
+
- [ ] onDisable() - Unregister event listeners
|
|
635
|
+
- [ ] onDestroy() - Release resources, clear references
|
|
636
|
+
|
|
637
|
+
**Best Practices:**
|
|
638
|
+
- [ ] Validate required @property references in onLoad()
|
|
639
|
+
- [ ] Throw exceptions for missing required references
|
|
640
|
+
- [ ] Cache component references (don't lookup in update)
|
|
641
|
+
- [ ] Zero allocations in update/lateUpdate
|
|
642
|
+
- [ ] Always unregister listeners in onDisable/onDestroy
|
|
643
|
+
- [ ] Use readonly for @property fields when appropriate
|
|
644
|
+
|
|
645
|
+
**The component lifecycle is the foundation of Cocos Creator architecture.**
|