Extract explosion logic to ExplosionManager class
Some checks failed
Build / build (push) Failing after 25s
Some checks failed
Build / build (push) Failing after 25s
Refactored explosion particle system management from RockFactory to a dedicated ExplosionManager class for better code organization and reusability. - Created ExplosionManager with particle system pooling - Removed explosion-specific code from RockFactory - Simplified collision handler in RockFactory 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
cb96b4ea6c
commit
bf5d33e1cb
147
src/explosionManager.ts
Normal file
147
src/explosionManager.ts
Normal file
@ -0,0 +1,147 @@
|
||||
import {
|
||||
MeshBuilder,
|
||||
ParticleHelper,
|
||||
ParticleSystem,
|
||||
ParticleSystemSet,
|
||||
Scene,
|
||||
Vector3
|
||||
} from "@babylonjs/core";
|
||||
|
||||
/**
|
||||
* Configuration for explosion effects
|
||||
*/
|
||||
export interface ExplosionConfig {
|
||||
/** Size of the explosion pool */
|
||||
poolSize?: number;
|
||||
/** Duration of explosion in milliseconds */
|
||||
duration?: number;
|
||||
/** Rendering group ID for particles */
|
||||
renderingGroupId?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages explosion particle effects with pooling for performance
|
||||
*/
|
||||
export class ExplosionManager {
|
||||
private explosionPool: ParticleSystemSet[] = [];
|
||||
private scene: Scene;
|
||||
private config: Required<ExplosionConfig>;
|
||||
|
||||
// Default configuration
|
||||
private static readonly DEFAULT_CONFIG: Required<ExplosionConfig> = {
|
||||
poolSize: 10,
|
||||
duration: 2000,
|
||||
renderingGroupId: 1
|
||||
};
|
||||
|
||||
constructor(scene: Scene, config?: ExplosionConfig) {
|
||||
this.scene = scene;
|
||||
this.config = { ...ExplosionManager.DEFAULT_CONFIG, ...config };
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the explosion pool by pre-creating particle systems
|
||||
*/
|
||||
public async initialize(): Promise<void> {
|
||||
console.log(`Pre-creating ${this.config.poolSize} explosion particle systems...`);
|
||||
|
||||
for (let i = 0; i < this.config.poolSize; i++) {
|
||||
const set = await ParticleHelper.CreateAsync("explosion", this.scene);
|
||||
set.systems.forEach((system) => {
|
||||
system.renderingGroupId = this.config.renderingGroupId;
|
||||
});
|
||||
this.explosionPool.push(set);
|
||||
}
|
||||
|
||||
console.log(`Created ${this.config.poolSize} explosion particle systems in pool`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an explosion from the pool
|
||||
*/
|
||||
private getExplosionFromPool(): ParticleSystemSet | null {
|
||||
return this.explosionPool.pop() || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an explosion to the pool after use
|
||||
*/
|
||||
private returnExplosionToPool(explosion: ParticleSystemSet): void {
|
||||
explosion.dispose();
|
||||
ParticleHelper.CreateAsync("explosion", this.scene).then((set) => {
|
||||
set.systems.forEach((system) => {
|
||||
system.renderingGroupId = this.config.renderingGroupId;
|
||||
});
|
||||
this.explosionPool.push(set);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Play an explosion at the specified position with optional scaling
|
||||
*/
|
||||
public playExplosion(position: Vector3, scaling: Vector3 = Vector3.One()): void {
|
||||
const explosion = this.getExplosionFromPool();
|
||||
|
||||
if (!explosion) {
|
||||
// Pool is empty, create explosion on the fly
|
||||
console.log("Explosion pool empty, creating new explosion on demand");
|
||||
ParticleHelper.CreateAsync("explosion", this.scene).then((set) => {
|
||||
const point = MeshBuilder.CreateSphere("explosionPoint", {
|
||||
diameter: 0.1
|
||||
}, this.scene);
|
||||
point.position = position.clone();
|
||||
point.isVisible = false;
|
||||
|
||||
set.start(point);
|
||||
|
||||
setTimeout(() => {
|
||||
set.dispose();
|
||||
point.dispose();
|
||||
}, this.config.duration);
|
||||
});
|
||||
} else {
|
||||
// Use pooled explosion
|
||||
const point = MeshBuilder.CreateSphere("explosionPoint", {
|
||||
diameter: 10
|
||||
}, this.scene);
|
||||
point.position = position.clone();
|
||||
point.isVisible = false;
|
||||
point.scaling = scaling.multiplyByFloats(0.2, 0.3, 0.2);
|
||||
|
||||
console.log("Using pooled explosion with", explosion.systems.length, "systems at", position);
|
||||
|
||||
// Set emitter and start each system individually
|
||||
explosion.systems.forEach((system: ParticleSystem, idx: number) => {
|
||||
system.emitter = point;
|
||||
system.start();
|
||||
console.log(` System ${idx}: emitter set to`, system.emitter, "activeCount=", system.getActiveCount());
|
||||
});
|
||||
|
||||
// Stop and return to pool after duration
|
||||
setTimeout(() => {
|
||||
explosion.systems.forEach((system: ParticleSystem) => {
|
||||
system.stop();
|
||||
});
|
||||
this.returnExplosionToPool(explosion);
|
||||
point.dispose();
|
||||
}, this.config.duration);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current number of available explosions in the pool
|
||||
*/
|
||||
public getPoolSize(): number {
|
||||
return this.explosionPool.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispose of all pooled explosions
|
||||
*/
|
||||
public dispose(): void {
|
||||
this.explosionPool.forEach(explosion => {
|
||||
explosion.dispose();
|
||||
});
|
||||
this.explosionPool = [];
|
||||
}
|
||||
}
|
||||
@ -19,6 +19,7 @@ import {Debug} from "@babylonjs/core/Legacy/legacy";
|
||||
import {createSphereLightmap} from "./sphereLightmap";
|
||||
import { GameConfig } from "./gameConfig";
|
||||
import { MaterialFactory } from "./materialFactory";
|
||||
import { ExplosionManager } from "./explosionManager";
|
||||
let _particleData: any = null;
|
||||
|
||||
export class Rock {
|
||||
@ -38,20 +39,17 @@ export class RockFactory {
|
||||
private static _rockMesh: AbstractMesh;
|
||||
private static _rockMaterial: PBRMaterial;
|
||||
private static _originalMaterial: PBRMaterial = null;
|
||||
private static _explosionPool: ParticleSystemSet[] = [];
|
||||
private static _poolSize: number = 10;
|
||||
private static _explosionManager: ExplosionManager;
|
||||
private static _viewer: PhysicsViewer = null;
|
||||
|
||||
public static async init() {
|
||||
// Pre-create explosion particle systems for pooling
|
||||
console.log("Pre-creating explosion particle systems...");
|
||||
for (let i = 0; i < this._poolSize; i++) {
|
||||
const set = await ParticleHelper.CreateAsync("explosion", DefaultScene.MainScene);
|
||||
set.systems.forEach((system) => {
|
||||
system.renderingGroupId =1;
|
||||
})
|
||||
this._explosionPool.push(set);
|
||||
}
|
||||
console.log(`Created ${this._poolSize} explosion particle systems in pool`);
|
||||
// Initialize explosion manager
|
||||
this._explosionManager = new ExplosionManager(DefaultScene.MainScene, {
|
||||
poolSize: 10,
|
||||
duration: 2000,
|
||||
renderingGroupId: 1
|
||||
});
|
||||
await this._explosionManager.initialize();
|
||||
|
||||
if (!this._rockMesh) {
|
||||
await this.loadMesh();
|
||||
@ -85,16 +83,6 @@ export class RockFactory {
|
||||
importMesh.meshes[0].dispose();
|
||||
}
|
||||
}
|
||||
private static getExplosionFromPool(): ParticleSystemSet | null {
|
||||
return this._explosionPool.pop() || null;
|
||||
}
|
||||
|
||||
private static returnExplosionToPool(explosion: ParticleSystemSet) {
|
||||
explosion.dispose();
|
||||
ParticleHelper.CreateAsync("explosion", DefaultScene.MainScene).then((set) => {
|
||||
this._explosionPool.push(set);
|
||||
})
|
||||
}
|
||||
|
||||
public static async createRock(i: number, position: Vector3, size: Vector3,
|
||||
score: Observable<ScoreEvent>): Promise<Rock> {
|
||||
@ -145,46 +133,8 @@ export class RockFactory {
|
||||
eventData.collidedAgainst.transformNode.dispose();
|
||||
eventData.collidedAgainst.dispose();
|
||||
|
||||
// Get explosion from pool (or create new if pool empty)
|
||||
let explosion = RockFactory.getExplosionFromPool();
|
||||
|
||||
if (!explosion) {
|
||||
console.log("Pool empty, creating new explosion");
|
||||
ParticleHelper.CreateAsync("explosion", DefaultScene.MainScene).then((set) => {
|
||||
const point = MeshBuilder.CreateSphere("point", {diameter: 0.1}, DefaultScene.MainScene);
|
||||
point.position = position.clone();
|
||||
point.isVisible = false;
|
||||
|
||||
set.start(point);
|
||||
|
||||
setTimeout(() => {
|
||||
set.dispose();
|
||||
point.dispose();
|
||||
}, 2000);
|
||||
});
|
||||
} else {
|
||||
// Use pooled explosion
|
||||
const point = MeshBuilder.CreateSphere("point", {diameter: 10}, DefaultScene.MainScene);
|
||||
point.position = position.clone();
|
||||
point.isVisible = false;
|
||||
point.scaling = scaling.multiplyByFloats(.2,.3,.2);
|
||||
console.log("Using pooled explosion with", explosion.systems.length, "systems at", position);
|
||||
|
||||
// Set emitter and start each system individually
|
||||
explosion.systems.forEach((system: ParticleSystem, idx: number) => {
|
||||
system.emitter = point; // Set emitter to the collision point
|
||||
system.start(); // Start this specific system
|
||||
console.log(` System ${idx}: emitter set to`, system.emitter, "activeCount=", system.getActiveCount());
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
explosion.systems.forEach((system: ParticleSystem) => {
|
||||
system.stop();
|
||||
});
|
||||
RockFactory.returnExplosionToPool(explosion);
|
||||
point.dispose();
|
||||
}, 2000);
|
||||
}
|
||||
// Play explosion using ExplosionManager
|
||||
RockFactory._explosionManager.playExplosion(position, scaling);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user