/** * Wiederverwendbare Projektile (Object Pooling). * * Bei Dauerfeuer entstehen Hunderte Projektile pro Minute. Statt sie jedes Mal neu * zu erzeugen und zu zerstören (Garbage-Collector-Ruckler), werden deaktivierte * Projektile wiederverwendet. * @module entities/ProjectilePool */ export class ProjectilePool { /** * @param {Phaser.Scene} scene * @param {string} textureKey * @param {number} maxSize Obergrenze gleichzeitig fliegender Projektile. * @param {number} depth */ constructor(scene, textureKey, maxSize, depth) { this.scene = scene; this.textureKey = textureKey; this.depth = depth; this.group = scene.physics.add.group({ classType: Phaser.Physics.Arcade.Image, maxSize, allowGravity: false, }); } /** * Feuert ein Projektil ab. * @param {number} x * @param {number} y * @param {number} angle Bogenmaß. * @param {number} speed px/s * @param {number} damage * @param {number} range Maximale Flugstrecke in px. * @param {string} [textureKey] Abweichende Textur (z. B. Geschütz). * @returns {Phaser.Physics.Arcade.Image|null} null, wenn der Pool erschöpft ist. */ fire(x, y, angle, speed, damage, range, textureKey) { const projectile = this.group.get(x, y, textureKey ?? this.textureKey); if (!projectile) return null; projectile.enableBody(true, x, y, true, true); projectile.setTexture(textureKey ?? this.textureKey); projectile.setDepth(this.depth); projectile.setRotation(angle); this.scene.physics.velocityFromRotation(angle, speed, projectile.body.velocity); projectile.damage = damage; projectile.spawnX = x; projectile.spawnY = y; projectile.maxTravelSq = range * range; return projectile; } /** * Deaktiviert Projektile, die ihre Reichweite überschritten oder die Welt verlassen haben. * @param {Phaser.Geom.Rectangle} bounds */ update(bounds) { for (const projectile of this.group.getChildren()) { if (!projectile.active) continue; const dx = projectile.x - projectile.spawnX; const dy = projectile.y - projectile.spawnY; if (dx * dx + dy * dy > projectile.maxTravelSq || !bounds.contains(projectile.x, projectile.y)) { this.kill(projectile); } } } /** @param {Phaser.Physics.Arcade.Image} projectile */ kill(projectile) { projectile.disableBody(true, true); } /** @returns {number} Aktive Projektile. */ get activeCount() { return this.group.countActive(true); } }