@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.
Files changed (99) hide show
  1. package/cli.js +63 -0
  2. package/dist/__tests__/checkpoint.test.d.ts +2 -0
  3. package/dist/__tests__/checkpoint.test.d.ts.map +1 -0
  4. package/dist/__tests__/fs-handler-decode.test.d.ts +2 -0
  5. package/dist/__tests__/fs-handler-decode.test.d.ts.map +1 -0
  6. package/dist/__tests__/idle-sweeper.test.d.ts +2 -0
  7. package/dist/__tests__/idle-sweeper.test.d.ts.map +1 -0
  8. package/dist/__tests__/mcp-config.test.d.ts +2 -0
  9. package/dist/__tests__/mcp-config.test.d.ts.map +1 -0
  10. package/dist/__tests__/schedule-runtime-spawn.test.d.ts +2 -0
  11. package/dist/__tests__/schedule-runtime-spawn.test.d.ts.map +1 -0
  12. package/dist/__tests__/schedule-runtime.test.d.ts +2 -0
  13. package/dist/__tests__/schedule-runtime.test.d.ts.map +1 -0
  14. package/dist/__tests__/session-limit.test.d.ts +2 -0
  15. package/dist/__tests__/session-limit.test.d.ts.map +1 -0
  16. package/dist/__tests__/swarm-cli-client.test.d.ts +2 -0
  17. package/dist/__tests__/swarm-cli-client.test.d.ts.map +1 -0
  18. package/dist/__tests__/swarm-control-dispatch.test.d.ts +2 -0
  19. package/dist/__tests__/swarm-control-dispatch.test.d.ts.map +1 -0
  20. package/dist/__tests__/swarm-session-bridge.test.d.ts +2 -0
  21. package/dist/__tests__/swarm-session-bridge.test.d.ts.map +1 -0
  22. package/dist/backend-adapter.d.ts +43 -0
  23. package/dist/backend-adapter.d.ts.map +1 -1
  24. package/dist/checkpoint.d.ts +67 -0
  25. package/dist/checkpoint.d.ts.map +1 -0
  26. package/dist/{chunk-RIK7IXSW.js → chunk-WJWCYGLQ.js} +1130 -147
  27. package/dist/chunk-WJWCYGLQ.js.map +1 -0
  28. package/dist/errors.d.ts +40 -0
  29. package/dist/errors.d.ts.map +1 -0
  30. package/dist/fs-handler.d.ts.map +1 -1
  31. package/dist/hub-connection.d.ts +13 -0
  32. package/dist/hub-connection.d.ts.map +1 -1
  33. package/dist/index.js +1 -1
  34. package/dist/schedule-runtime.d.ts +125 -0
  35. package/dist/schedule-runtime.d.ts.map +1 -0
  36. package/dist/session-manager.d.ts +102 -0
  37. package/dist/session-manager.d.ts.map +1 -1
  38. package/dist/skill-loader.d.ts +20 -0
  39. package/dist/skill-loader.d.ts.map +1 -1
  40. package/dist/start.d.ts +2 -0
  41. package/dist/start.d.ts.map +1 -1
  42. package/dist/start.js +1 -1
  43. package/dist/swarm-cli-client.d.ts +24 -0
  44. package/dist/swarm-cli-client.d.ts.map +1 -0
  45. package/dist/swarm-cli-client.js +83 -0
  46. package/dist/swarm-cli-client.js.map +1 -0
  47. package/dist/swarm-control-dispatch.d.ts +47 -0
  48. package/dist/swarm-control-dispatch.d.ts.map +1 -0
  49. package/dist/swarm-session-bridge.d.ts +22 -0
  50. package/dist/swarm-session-bridge.d.ts.map +1 -0
  51. package/package.json +6 -4
  52. package/skills/cocos-creator-3x-cn/SKILL.md +475 -0
  53. package/skills/cocos-creator-3x-cn/references/framework/asset-management.md +322 -0
  54. package/skills/cocos-creator-3x-cn/references/framework/component-system.md +348 -0
  55. package/skills/cocos-creator-3x-cn/references/framework/event-patterns.md +410 -0
  56. package/skills/cocos-creator-3x-cn/references/framework/playable-optimization.md +257 -0
  57. package/skills/cocos-creator-3x-cn/references/language/performance.md +363 -0
  58. package/skills/cocos-creator-3x-cn/references/language/quality-hygiene.md +307 -0
  59. package/skills/cocos-creator-3x-cn/references/review/architecture-review.md +183 -0
  60. package/skills/cocos-creator-3x-cn/references/review/quality-review.md +251 -0
  61. package/skills/cocos-performance-optimizer/SKILL.md +214 -0
  62. package/skills/game-development/2d-games/SKILL.md +129 -0
  63. package/skills/game-development/3d-games/SKILL.md +145 -0
  64. package/skills/game-development/SKILL.md +175 -0
  65. package/skills/game-development/game-art/SKILL.md +195 -0
  66. package/skills/game-development/game-audio/SKILL.md +200 -0
  67. package/skills/game-development/game-design/SKILL.md +139 -0
  68. package/skills/game-development/mobile-games/SKILL.md +118 -0
  69. package/skills/game-development/multiplayer/SKILL.md +142 -0
  70. package/skills/game-development/pc-games/SKILL.md +154 -0
  71. package/skills/game-development/vr-ar/SKILL.md +133 -0
  72. package/skills/game-development/web-games/SKILL.md +160 -0
  73. package/skills/game-engine/SKILL.md +140 -0
  74. package/skills/game-engine/assets/2d-maze-game.md +528 -0
  75. package/skills/game-engine/assets/2d-platform-game.md +1855 -0
  76. package/skills/game-engine/assets/gameBase-template-repo.md +310 -0
  77. package/skills/game-engine/assets/paddle-game-template.md +1528 -0
  78. package/skills/game-engine/assets/simple-2d-engine.md +507 -0
  79. package/skills/game-engine/references/3d-web-games.md +754 -0
  80. package/skills/game-engine/references/algorithms.md +843 -0
  81. package/skills/game-engine/references/basics.md +343 -0
  82. package/skills/game-engine/references/game-control-mechanisms.md +617 -0
  83. package/skills/game-engine/references/game-engine-core-principles.md +695 -0
  84. package/skills/game-engine/references/game-publishing.md +352 -0
  85. package/skills/game-engine/references/techniques.md +894 -0
  86. package/skills/game-engine/references/terminology.md +354 -0
  87. package/skills/game-engine/references/web-apis.md +1394 -0
  88. package/skills/theone-cocos-standards/SKILL.md +557 -0
  89. package/skills/theone-cocos-standards/references/framework/component-system.md +645 -0
  90. package/skills/theone-cocos-standards/references/framework/event-patterns.md +433 -0
  91. package/skills/theone-cocos-standards/references/framework/playable-optimization.md +429 -0
  92. package/skills/theone-cocos-standards/references/framework/size-optimization.md +308 -0
  93. package/skills/theone-cocos-standards/references/language/modern-typescript.md +658 -0
  94. package/skills/theone-cocos-standards/references/language/performance.md +580 -0
  95. package/skills/theone-cocos-standards/references/language/quality-hygiene.md +582 -0
  96. package/skills/theone-cocos-standards/references/review/architecture-review.md +250 -0
  97. package/skills/theone-cocos-standards/references/review/performance-review.md +288 -0
  98. package/skills/theone-cocos-standards/references/review/quality-review.md +239 -0
  99. package/dist/chunk-RIK7IXSW.js.map +0 -1
@@ -0,0 +1,580 @@
1
+ # TypeScript Performance Optimization
2
+
3
+ ## Zero Allocations in update()
4
+
5
+ **Critical Rule**: Never allocate objects in `update()`, `lateUpdate()`, or any method called every frame.
6
+
7
+ ```typescript
8
+ import { _decorator, Component, Node, Vec3, Quat } from 'cc';
9
+ const { ccclass, property } = _decorator;
10
+
11
+ @ccclass('OptimizedController')
12
+ export class OptimizedController extends Component {
13
+ @property(Node)
14
+ private readonly targetNode: Node | null = null;
15
+
16
+ // ✅ EXCELLENT: Preallocated reusable objects
17
+ private readonly tempVec3: Vec3 = new Vec3();
18
+ private readonly tempQuat: Quat = new Quat();
19
+ private readonly tempVec3Array: Vec3[] = [];
20
+
21
+ // ✅ EXCELLENT: No allocations in update
22
+ protected update(dt: number): void {
23
+ if (!this.targetNode) return;
24
+
25
+ // Reuse preallocated vector
26
+ this.targetNode.getPosition(this.tempVec3);
27
+ this.tempVec3.y += 10 * dt;
28
+ this.targetNode.setPosition(this.tempVec3);
29
+
30
+ // Reuse preallocated quaternion
31
+ this.targetNode.getRotation(this.tempQuat);
32
+ Quat.rotateY(this.tempQuat, this.tempQuat, dt);
33
+ this.targetNode.setRotation(this.tempQuat);
34
+ }
35
+
36
+ // ✅ EXCELLENT: Reuse array instead of creating new one
37
+ public updateMultipleNodes(nodes: Node[]): void {
38
+ this.tempVec3Array.length = 0; // Clear without allocating
39
+
40
+ for (const node of nodes) {
41
+ node.getPosition(this.tempVec3);
42
+ this.tempVec3Array.push(this.tempVec3.clone());
43
+ }
44
+ }
45
+ }
46
+
47
+ // ❌ WRONG: Allocating in update
48
+ protected update(dt: number): void {
49
+ if (!this.targetNode) return;
50
+
51
+ // Creates new Vec3 every frame (60 allocations/second)
52
+ const currentPos = this.targetNode.position.clone();
53
+ currentPos.y += 10 * dt;
54
+ this.targetNode.setPosition(currentPos);
55
+
56
+ // Creates new array every frame
57
+ const positions = this.nodes.map(n => n.position.clone());
58
+ }
59
+
60
+ // ❌ WRONG: String concatenation in update
61
+ protected update(dt: number): void {
62
+ // Creates new string every frame
63
+ const debugInfo = `Position: ${this.node.position.x}, ${this.node.position.y}`;
64
+ console.log(debugInfo);
65
+ }
66
+ ```
67
+
68
+ ## Object Pooling Pattern
69
+
70
+ ```typescript
71
+ import { _decorator, Component, Node, Prefab, instantiate, NodePool } from 'cc';
72
+ const { ccclass, property } = _decorator;
73
+
74
+ @ccclass('BulletPool')
75
+ export class BulletPool extends Component {
76
+ @property(Prefab)
77
+ private readonly bulletPrefab: Prefab | null = null;
78
+
79
+ private readonly pool: NodePool = new NodePool();
80
+ private static readonly INITIAL_POOL_SIZE: number = 20;
81
+
82
+ // ✅ EXCELLENT: Prewarm pool on initialization
83
+ protected onLoad(): void {
84
+ if (!this.bulletPrefab) {
85
+ throw new Error('BulletPool: bulletPrefab is required');
86
+ }
87
+
88
+ for (let i = 0; i < BulletPool.INITIAL_POOL_SIZE; i++) {
89
+ const bullet = instantiate(this.bulletPrefab);
90
+ this.pool.put(bullet);
91
+ }
92
+ }
93
+
94
+ // ✅ EXCELLENT: Get from pool (no allocation if available)
95
+ public getBullet(): Node {
96
+ let bullet: Node;
97
+
98
+ if (this.pool.size() > 0) {
99
+ bullet = this.pool.get()!;
100
+ } else {
101
+ // Only allocate if pool is empty
102
+ if (!this.bulletPrefab) {
103
+ throw new Error('BulletPool: bulletPrefab is required');
104
+ }
105
+ bullet = instantiate(this.bulletPrefab);
106
+ }
107
+
108
+ bullet.active = true;
109
+ return bullet;
110
+ }
111
+
112
+ // ✅ EXCELLENT: Return to pool (no deallocation)
113
+ public returnBullet(bullet: Node): void {
114
+ bullet.active = false;
115
+ this.pool.put(bullet);
116
+ }
117
+
118
+ // ✅ EXCELLENT: Clear pool on cleanup
119
+ protected onDestroy(): void {
120
+ this.pool.clear();
121
+ }
122
+ }
123
+
124
+ // Usage in game
125
+ @ccclass('Gun')
126
+ export class Gun extends Component {
127
+ private readonly bulletPool!: BulletPool;
128
+
129
+ public shoot(): void {
130
+ // ✅ GOOD: Get from pool instead of instantiate
131
+ const bullet = this.bulletPool.getBullet();
132
+ bullet.setPosition(this.node.position);
133
+
134
+ // Set up bullet with timeout to return to pool
135
+ this.scheduleOnce(() => {
136
+ this.bulletPool.returnBullet(bullet);
137
+ }, 3.0);
138
+ }
139
+ }
140
+
141
+ // ❌ WRONG: Creating new instances every time
142
+ public shoot(): void {
143
+ // Allocates and deallocates constantly
144
+ const bullet = instantiate(this.bulletPrefab!);
145
+ bullet.setPosition(this.node.position);
146
+
147
+ this.scheduleOnce(() => {
148
+ bullet.destroy(); // Triggers garbage collection
149
+ }, 3.0);
150
+ }
151
+ ```
152
+
153
+ ## Caching Expensive Operations
154
+
155
+ ```typescript
156
+ import { _decorator, Component, Node } from 'cc';
157
+ const { ccclass, property } = _decorator;
158
+
159
+ @ccclass('EnemyManager')
160
+ export class EnemyManager extends Component {
161
+ @property([Node])
162
+ private readonly enemyNodes: Node[] = [];
163
+
164
+ // ✅ EXCELLENT: Cache component references
165
+ private readonly enemyControllers: EnemyController[] = [];
166
+ private cachedActiveEnemies: EnemyController[] = [];
167
+ private activeEnemiesDirty: boolean = true;
168
+
169
+ protected onLoad(): void {
170
+ // Cache component references on initialization
171
+ for (const node of this.enemyNodes) {
172
+ const controller = node.getComponent(EnemyController);
173
+ if (controller) {
174
+ this.enemyControllers.push(controller);
175
+ }
176
+ }
177
+ }
178
+
179
+ // ✅ EXCELLENT: Mark cache as dirty instead of recalculating
180
+ public onEnemyStateChanged(): void {
181
+ this.activeEnemiesDirty = true;
182
+ }
183
+
184
+ // ✅ EXCELLENT: Lazy recalculation only when needed
185
+ public getActiveEnemies(): EnemyController[] {
186
+ if (this.activeEnemiesDirty) {
187
+ this.cachedActiveEnemies = this.enemyControllers.filter(e => e.isActive);
188
+ this.activeEnemiesDirty = false;
189
+ }
190
+ return this.cachedActiveEnemies;
191
+ }
192
+
193
+ protected update(dt: number): void {
194
+ // ✅ GOOD: Use cached active enemies
195
+ const activeEnemies = this.getActiveEnemies();
196
+
197
+ for (const enemy of activeEnemies) {
198
+ enemy.update(dt);
199
+ }
200
+ }
201
+ }
202
+
203
+ // ❌ WRONG: Finding components every frame
204
+ protected update(dt: number): void {
205
+ for (const node of this.enemyNodes) {
206
+ const controller = node.getComponent(EnemyController); // Expensive lookup!
207
+ if (controller?.isActive) {
208
+ controller.update(dt);
209
+ }
210
+ }
211
+ }
212
+
213
+ // ❌ WRONG: Filtering every frame
214
+ protected update(dt: number): void {
215
+ const activeEnemies = this.enemyControllers.filter(e => e.isActive); // Allocates array every frame!
216
+ for (const enemy of activeEnemies) {
217
+ enemy.update(dt);
218
+ }
219
+ }
220
+ ```
221
+
222
+ ## Throttling Expensive Operations
223
+
224
+ ```typescript
225
+ import { _decorator, Component } from 'cc';
226
+ const { ccclass } = _decorator;
227
+
228
+ @ccclass('AIController')
229
+ export class AIController extends Component {
230
+ private frameCount: number = 0;
231
+ private static readonly AI_UPDATE_INTERVAL: number = 10; // Every 10 frames
232
+ private static readonly PATHFINDING_INTERVAL: number = 60; // Every 60 frames (1 second at 60fps)
233
+
234
+ // ✅ EXCELLENT: Update AI every N frames, not every frame
235
+ protected update(dt: number): void {
236
+ this.frameCount++;
237
+
238
+ // Run expensive AI logic every 10 frames instead of every frame
239
+ if (this.frameCount % AIController.AI_UPDATE_INTERVAL === 0) {
240
+ this.updateAIDecision();
241
+ }
242
+
243
+ // Run very expensive pathfinding every 60 frames (1 second)
244
+ if (this.frameCount % AIController.PATHFINDING_INTERVAL === 0) {
245
+ this.recalculatePath();
246
+ }
247
+
248
+ // Cheap operations can run every frame
249
+ this.moveTowardsTarget(dt);
250
+ }
251
+
252
+ private updateAIDecision(): void {
253
+ // Expensive: Check all enemies, evaluate threats, etc.
254
+ }
255
+
256
+ private recalculatePath(): void {
257
+ // Very expensive: A* pathfinding
258
+ }
259
+
260
+ private moveTowardsTarget(dt: number): void {
261
+ // Cheap: Simple movement
262
+ }
263
+ }
264
+
265
+ // ❌ WRONG: Expensive operations every frame
266
+ protected update(dt: number): void {
267
+ this.recalculatePath(); // A* pathfinding 60 times per second!
268
+ this.updateAIDecision(); // Complex AI logic 60 times per second!
269
+ this.moveTowardsTarget(dt);
270
+ }
271
+ ```
272
+
273
+ ## Time-Based Throttling
274
+
275
+ ```typescript
276
+ import { _decorator, Component } from 'cc';
277
+ const { ccclass } = _decorator;
278
+
279
+ @ccclass('PerformanceMonitor')
280
+ export class PerformanceMonitor extends Component {
281
+ private lastUpdateTime: number = 0;
282
+ private static readonly UPDATE_INTERVAL: number = 1.0; // 1 second
283
+
284
+ // ✅ EXCELLENT: Time-based throttling
285
+ protected update(dt: number): void {
286
+ const currentTime = Date.now() / 1000;
287
+
288
+ if (currentTime - this.lastUpdateTime >= PerformanceMonitor.UPDATE_INTERVAL) {
289
+ this.performExpensiveOperation();
290
+ this.lastUpdateTime = currentTime;
291
+ }
292
+ }
293
+
294
+ private performExpensiveOperation(): void {
295
+ // Expensive operation that runs once per second
296
+ }
297
+ }
298
+
299
+ // Alternative using scheduleOnce
300
+ @ccclass('TimerBased')
301
+ export class TimerBased extends Component {
302
+ private static readonly CHECK_INTERVAL: number = 2.0; // 2 seconds
303
+
304
+ protected start(): void {
305
+ this.scheduleCheckRecurring();
306
+ }
307
+
308
+ private scheduleCheckRecurring(): void {
309
+ this.performCheck();
310
+ this.scheduleOnce(this.scheduleCheckRecurring, TimerBased.CHECK_INTERVAL);
311
+ }
312
+
313
+ private performCheck(): void {
314
+ // Expensive check operation
315
+ }
316
+ }
317
+ ```
318
+
319
+ ## Avoid Expensive Lookups
320
+
321
+ ```typescript
322
+ import { _decorator, Component, Node, find } from 'cc';
323
+ const { ccclass } = _decorator;
324
+
325
+ @ccclass('GameManager')
326
+ export class GameManager extends Component {
327
+ // ✅ EXCELLENT: Cache references in onLoad
328
+ private uiRootNode!: Node;
329
+ private playerNode!: Node;
330
+ private enemyNodes: Node[] = [];
331
+
332
+ protected onLoad(): void {
333
+ // Cache node references once
334
+ const uiRoot = find('Canvas/UI');
335
+ if (!uiRoot) {
336
+ throw new Error('GameManager: UI root not found');
337
+ }
338
+ this.uiRootNode = uiRoot;
339
+
340
+ const player = find('Canvas/Player');
341
+ if (!player) {
342
+ throw new Error('GameManager: Player not found');
343
+ }
344
+ this.playerNode = player;
345
+
346
+ // Cache array of enemy nodes
347
+ const enemyParent = find('Canvas/Enemies');
348
+ if (enemyParent) {
349
+ this.enemyNodes = enemyParent.children.slice();
350
+ }
351
+ }
352
+
353
+ protected update(dt: number): void {
354
+ // ✅ GOOD: Use cached references
355
+ this.updatePlayer(this.playerNode, dt);
356
+ this.updateEnemies(this.enemyNodes, dt);
357
+ }
358
+ }
359
+
360
+ // ❌ WRONG: Finding nodes every frame
361
+ protected update(dt: number): void {
362
+ const player = find('Canvas/Player'); // Expensive search every frame!
363
+ const enemies = find('Canvas/Enemies')?.children; // Expensive search every frame!
364
+
365
+ if (player) {
366
+ this.updatePlayer(player, dt);
367
+ }
368
+ if (enemies) {
369
+ this.updateEnemies(enemies, dt);
370
+ }
371
+ }
372
+
373
+ // ❌ WRONG: getComponent every frame
374
+ protected update(dt: number): void {
375
+ const playerController = this.playerNode.getComponent(PlayerController); // Expensive lookup!
376
+ playerController?.update(dt);
377
+ }
378
+
379
+ // ✅ BETTER: Cache component reference
380
+ private playerController!: PlayerController;
381
+
382
+ protected onLoad(): void {
383
+ const controller = this.playerNode.getComponent(PlayerController);
384
+ if (!controller) {
385
+ throw new Error('PlayerController not found');
386
+ }
387
+ this.playerController = controller;
388
+ }
389
+
390
+ protected update(dt: number): void {
391
+ this.playerController.update(dt);
392
+ }
393
+ ```
394
+
395
+ ## String Concatenation Performance
396
+
397
+ ```typescript
398
+ import { _decorator, Component } from 'cc';
399
+ const { ccclass } = _decorator;
400
+
401
+ @ccclass('DebugDisplay')
402
+ export class DebugDisplay extends Component {
403
+ // ✅ EXCELLENT: Use template literals for readability
404
+ public getDebugInfo(player: Player): string {
405
+ return `Player: ${player.name}, HP: ${player.health}/${player.maxHealth}, Level: ${player.level}`;
406
+ }
407
+
408
+ // ✅ EXCELLENT: Build strings efficiently with array join (for large strings)
409
+ public generateReport(players: Player[]): string {
410
+ const lines: string[] = [];
411
+ lines.push('=== Player Report ===');
412
+
413
+ for (const player of players) {
414
+ lines.push(`${player.name}: Level ${player.level}, HP ${player.health}`);
415
+ }
416
+
417
+ lines.push('=== End Report ===');
418
+ return lines.join('\n');
419
+ }
420
+
421
+ // ✅ EXCELLENT: Avoid string operations in update loop
422
+ private debugText: string = '';
423
+ private frameCount: number = 0;
424
+
425
+ protected update(dt: number): void {
426
+ this.frameCount++;
427
+
428
+ // Only update debug text every 30 frames
429
+ if (this.frameCount % 30 === 0) {
430
+ this.debugText = this.generateDebugText();
431
+ }
432
+ }
433
+ }
434
+
435
+ // ❌ WRONG: String concatenation in loop
436
+ public generateReport(players: Player[]): string {
437
+ let report = '=== Player Report ===\n';
438
+
439
+ for (const player of players) {
440
+ report += `${player.name}: Level ${player.level}\n`; // Allocates new string each iteration
441
+ }
442
+
443
+ report += '=== End Report ===';
444
+ return report;
445
+ }
446
+
447
+ // ❌ WRONG: Building strings in update
448
+ protected update(dt: number): void {
449
+ this.debugText = `FPS: ${1/dt}, Position: ${this.node.position}`; // Allocates every frame
450
+ }
451
+ ```
452
+
453
+ ## Number Operations Performance
454
+
455
+ ```typescript
456
+ import { _decorator, Component } from 'cc';
457
+ const { ccclass } = _decorator;
458
+
459
+ @ccclass('MathOptimizations')
460
+ export class MathOptimizations extends Component {
461
+ // ✅ EXCELLENT: Use multiplication instead of division
462
+ private static readonly INV_FRAME_RATE: number = 1 / 60;
463
+
464
+ public calculateTimedValue(value: number): number {
465
+ return value * MathOptimizations.INV_FRAME_RATE; // Faster than value / 60
466
+ }
467
+
468
+ // ✅ EXCELLENT: Use bitwise operations for integer math
469
+ public fastFloor(value: number): number {
470
+ return value | 0; // Faster than Math.floor for positive numbers
471
+ }
472
+
473
+ public isPowerOfTwo(value: number): boolean {
474
+ return (value & (value - 1)) === 0; // Faster than logarithm check
475
+ }
476
+
477
+ // ✅ EXCELLENT: Cache expensive math operations
478
+ private readonly sinCache: Map<number, number> = new Map();
479
+
480
+ public getCachedSin(angle: number): number {
481
+ if (!this.sinCache.has(angle)) {
482
+ this.sinCache.set(angle, Math.sin(angle));
483
+ }
484
+ return this.sinCache.get(angle)!;
485
+ }
486
+
487
+ // ✅ EXCELLENT: Use squared distance to avoid sqrt
488
+ public isWithinRange(pos1: Vec3, pos2: Vec3, range: number): boolean {
489
+ const dx = pos2.x - pos1.x;
490
+ const dy = pos2.y - pos1.y;
491
+ const dz = pos2.z - pos1.z;
492
+ const distSquared = dx * dx + dy * dy + dz * dz;
493
+ const rangeSquared = range * range;
494
+ return distSquared <= rangeSquared; // No sqrt needed
495
+ }
496
+ }
497
+
498
+ // ❌ WRONG: Using expensive operations
499
+ public isWithinRange(pos1: Vec3, pos2: Vec3, range: number): boolean {
500
+ const distance = Vec3.distance(pos1, pos2); // Uses sqrt internally
501
+ return distance <= range;
502
+ }
503
+
504
+ // ❌ WRONG: Division in hot path
505
+ protected update(dt: number): void {
506
+ const value = this.baseValue / 60; // Division is slower than multiplication
507
+ }
508
+ ```
509
+
510
+ ## Memory Management Best Practices
511
+
512
+ ```typescript
513
+ import { _decorator, Component, Node } from 'cc';
514
+ const { ccclass } = _decorator;
515
+
516
+ @ccclass('ResourceManager')
517
+ export class ResourceManager extends Component {
518
+ private readonly loadedAssets: Map<string, Asset> = new Map();
519
+ private readonly nodeReferences: Set<Node> = new Set();
520
+
521
+ // ✅ EXCELLENT: Clear references on cleanup
522
+ protected onDestroy(): void {
523
+ // Clear maps and sets
524
+ this.loadedAssets.clear();
525
+ this.nodeReferences.clear();
526
+
527
+ // Remove event listeners
528
+ this.node.off(Node.EventType.TOUCH_START);
529
+ }
530
+
531
+ // ✅ EXCELLENT: Remove unused assets
532
+ public unloadAsset(assetId: string): void {
533
+ const asset = this.loadedAssets.get(assetId);
534
+ if (asset) {
535
+ asset.decRef(); // Release reference
536
+ this.loadedAssets.delete(assetId);
537
+ }
538
+ }
539
+
540
+ // ✅ EXCELLENT: Weak references for caches
541
+ private readonly weakNodeCache: WeakMap<Node, CachedData> = new WeakMap();
542
+
543
+ public getCachedData(node: Node): CachedData | undefined {
544
+ return this.weakNodeCache.get(node);
545
+ }
546
+
547
+ public setCachedData(node: Node, data: CachedData): void {
548
+ this.weakNodeCache.set(node, data);
549
+ // Node is garbage collected → cache entry is automatically removed
550
+ }
551
+ }
552
+
553
+ // ❌ WRONG: Memory leaks
554
+ protected onDestroy(): void {
555
+ // Forgot to clear references - memory leak!
556
+ // this.loadedAssets.clear();
557
+ // this.nodeReferences.clear();
558
+ }
559
+
560
+ // ❌ WRONG: Strong references prevent garbage collection
561
+ private readonly nodeCache: Map<Node, CachedData> = new Map();
562
+ // Nodes are never garbage collected even when destroyed
563
+ ```
564
+
565
+ ## Summary: Performance Checklist
566
+
567
+ **Critical for playable ads (<5MB, <10 DrawCalls):**
568
+
569
+ - [ ] Zero allocations in update() (preallocate and reuse)
570
+ - [ ] Object pooling for frequently created/destroyed objects
571
+ - [ ] Cache component and node references (no getComponent in update)
572
+ - [ ] Throttle expensive operations (every N frames, not every frame)
573
+ - [ ] Avoid string operations in hot paths
574
+ - [ ] Use multiplication instead of division
575
+ - [ ] Use squared distance instead of distance (avoid sqrt)
576
+ - [ ] Clear references in onDestroy() to prevent memory leaks
577
+ - [ ] Use WeakMap for caches that should be garbage collected
578
+ - [ ] Array.length = 0 to clear arrays (don't create new arrays)
579
+
580
+ **Performance is critical for smooth 60fps playable ads.**