Point de départ
Ouvrez le lab arcade
Ouvrez le workspace 02-arcade-profiling et lancez le sandbox. On termine ici le tour des outils Memory ; la page suivante passe aux bases du CPU profiling.
Outil Memory
Allocation sampling
Dans Memory, choisissez Allocation sampling. DevTools rattache les allocations aux fonctions qui les ont déclenchées.
Allocation sampling regarde un échantillon des allocations plutôt que de compter chaque object. Les chiffres ne sont donc pas exacts au byte près, mais ils montrent très vite quelles fonctions créent le plus de bazar.
- Lancez l’enregistrement.
- Pendant 15 à 20 secondes, jouez normalement : tirez, détruisez quelques mines et déclenchez trois attaques spéciales avec la touche X. Trichez avec O pour recharger le condensateur et reproduire l’action.
- Arrêtez l’enregistrement.
Qu’est-ce que je regarde ???
La vue répond à une seule question : « dans quelles fonctions DevTools a-t-il vu le plus d’allocations ? » Ce n’est ni le décompte des GameObjects vivants, ni la liste complète de tout ce qui s’est passé. Chaque ligne est un échantillon, regroupé par JavaScript execution stack. Ici, l’échantillon remonte jusqu’à spawnEnemy : une mine a été créée sur ce chemin.
Premier cas utile : les explosions de mines. Chaque mine détruite crée 32 particles, donc beaucoup d’allocations d’un coup. Explorez les piles qui apparaissent autour de l’explosion et essayez de relier ce que vous voyez à cette action du jeu.
Le second cas utile est l’attaque spéciale avec la touche X : chaque pression crée 30 particles d’un coup. Trichez avec la touche O pour recharger le condensateur, puis répétez l’action. Elles traversent ensuite l’espace sans jamais être détruites. Cherchez la pile qui remonte vers SpecialAttack.fire, puis suivez-la jusqu’au new SpecialEnergyParticle(...). On obtient ainsi une piste de code qui alloue beaucoup, sans prétendre avoir prouvé une fuite.
Pour établir les trois memory leaks, choisissez plutôt Allocations on timeline ou comparez deux heap snapshots : ces outils permettent de voir les Bullet, SpecialEnergyParticle ou ExplosionParticle qui restent vivants après l’action. Ici, Allocation sampling sert à trouver où regarder ; les autres mesures servent à prouver ce qui reste.
Le HUD du jeu affiche aussi Objets retenus : les tirs manqués, particles de l’attaque spéciale et débris d’explosion fondus mais jamais détruits. C’est un contrôle pratique du scénario, pas un remplacement de la mesure DevTools.
- Dans Heavy (Bottom Up), commencez par ce qui occupe le haut de la liste.
- Remontez le JavaScript execution stack pour voir d’où viennent les allocations : particules, collisions, sprites, ou juste une mauvaise idée dans la boucle de jeu.
À vous de jouer
Pratiquez ce que vous avez appris
Trois memory leaks se sont glissés dans le jeu. Rejouez le même petit scénario, servez-vous de l’outil qui vous paraît le plus utile, puis regardez ce qui change.
Petit repère : dans Phaser, un tir, une mine ou une particule est un Game Object. Pour le retirer de la scène et de la physique, appelez destroy() sur cet objet.
Essayez d’abord. Les spoilers disent où regarder, mais ne remplacent ni le profile ni le contrôle après le correctif. Si vous ne trouvez pas, appuyez sur D pour afficher une ligne de debug supplémentaire qui devrait vous mettre sur la piste.
Leak 1 — les débris d’explosion fondent, mais ne disparaissent pas
Chaque mine détruite laisse 32 particles physiques. Elles deviennent transparentes, puis continuent hors écran avec leur body.
02-arcade-profiling/src/entities/ExplosionParticle.jsif (this.fadeRemaining <= 0) {
this.setAlpha(0);
// this.destroy(); manque ici
return;
}
Body restent retenus par les ExplosionParticle, même après la disparition visuelle de l’effet.Leak 2 — les tirs manqués traversent le vide sans fin
Un projectile qui ne touche rien ne quitte jamais le groupe physique. Il continue hors écran, avec son body.
02-arcade-profiling/src/entities/Bullet.jsconstructor(scene) {
super(scene, 0, 0, ASSET_KEYS.space, 'blaster');
scene.add.existing(this);
scene.physics.add.existing(this);
this.lifespan = BULLETS.lifespan;
}
fire(ship) {
this.lifespan = BULLETS.lifespan;
this.setPosition(ship.x, ship.y);
this.body.reset(ship.x, ship.y);
const angle = Phaser.Math.DegToRad(ship.body.rotation);
this.scene.physics.velocityFromRotation(angle, BULLETS.speed, this.body.velocity);
}
update(_, delta) {
this.lifespan -= delta;
if (this.lifespan <= 0) {
this.setVisible(false); // le tir est seulement masqué
// this.destroy(); correctif attendu
}
}
Leak 3 — l’attaque spéciale traverse l’espace sans fin
Les 30 particles de chaque attaque restent dans le groupe physique, même lorsqu’elles sont très loin du jeu.
02-arcade-profiling/src/entities/SpecialEnergyParticle.jsconstructor(scene) {
super(scene, 0, 0, ASSET_KEYS.space, 'blue');
scene.add.existing(this);
scene.physics.add.existing(this);
this.lifespan = 0;
}
fire(ship, angle) {
this.lifespan = SPECIAL_ATTACK.particleLifespan;
this.setPosition(ship.x, ship.y);
this.body.reset(ship.x, ship.y);
this.scene.physics.velocityFromRotation(
angle, SPECIAL_ATTACK.particleSpeed, this.body.velocity
);
}
update(_, delta) {
this.lifespan -= delta;
if (this.lifespan <= 0) {
this.setVisible(false); // l’effet est seulement masqué
// this.destroy(); correctif attendu
}
}
Pour aller plus loin
La solution propre
Ce n’est pas un cours de game development. Mais pour une version propre, on évite de créer puis détruire sans arrêt des images et des objets simples ; avec de la physique, le coût est encore plus sensible. Des pools précréent des objets dormants, puis les activent ou les désactivent au besoin : moins d’allocations et moins de risques de memory leaks.
Le dossier 02-arcade-profiling/_clean-code contient cette version avec des pools d’objets pour les trois cas que vous venez de corriger.