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 {createSphereLightmap} from "./sphereLightmap";
|
||||||
import { GameConfig } from "./gameConfig";
|
import { GameConfig } from "./gameConfig";
|
||||||
import { MaterialFactory } from "./materialFactory";
|
import { MaterialFactory } from "./materialFactory";
|
||||||
|
import { ExplosionManager } from "./explosionManager";
|
||||||
let _particleData: any = null;
|
let _particleData: any = null;
|
||||||
|
|
||||||
export class Rock {
|
export class Rock {
|
||||||
@ -38,20 +39,17 @@ export class RockFactory {
|
|||||||
private static _rockMesh: AbstractMesh;
|
private static _rockMesh: AbstractMesh;
|
||||||
private static _rockMaterial: PBRMaterial;
|
private static _rockMaterial: PBRMaterial;
|
||||||
private static _originalMaterial: PBRMaterial = null;
|
private static _originalMaterial: PBRMaterial = null;
|
||||||
private static _explosionPool: ParticleSystemSet[] = [];
|
private static _explosionManager: ExplosionManager;
|
||||||
private static _poolSize: number = 10;
|
|
||||||
private static _viewer: PhysicsViewer = null;
|
private static _viewer: PhysicsViewer = null;
|
||||||
|
|
||||||
public static async init() {
|
public static async init() {
|
||||||
// Pre-create explosion particle systems for pooling
|
// Initialize explosion manager
|
||||||
console.log("Pre-creating explosion particle systems...");
|
this._explosionManager = new ExplosionManager(DefaultScene.MainScene, {
|
||||||
for (let i = 0; i < this._poolSize; i++) {
|
poolSize: 10,
|
||||||
const set = await ParticleHelper.CreateAsync("explosion", DefaultScene.MainScene);
|
duration: 2000,
|
||||||
set.systems.forEach((system) => {
|
renderingGroupId: 1
|
||||||
system.renderingGroupId =1;
|
});
|
||||||
})
|
await this._explosionManager.initialize();
|
||||||
this._explosionPool.push(set);
|
|
||||||
}
|
|
||||||
console.log(`Created ${this._poolSize} explosion particle systems in pool`);
|
|
||||||
|
|
||||||
if (!this._rockMesh) {
|
if (!this._rockMesh) {
|
||||||
await this.loadMesh();
|
await this.loadMesh();
|
||||||
@ -85,16 +83,6 @@ export class RockFactory {
|
|||||||
importMesh.meshes[0].dispose();
|
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,
|
public static async createRock(i: number, position: Vector3, size: Vector3,
|
||||||
score: Observable<ScoreEvent>): Promise<Rock> {
|
score: Observable<ScoreEvent>): Promise<Rock> {
|
||||||
@ -145,46 +133,8 @@ export class RockFactory {
|
|||||||
eventData.collidedAgainst.transformNode.dispose();
|
eventData.collidedAgainst.transformNode.dispose();
|
||||||
eventData.collidedAgainst.dispose();
|
eventData.collidedAgainst.dispose();
|
||||||
|
|
||||||
// Get explosion from pool (or create new if pool empty)
|
// Play explosion using ExplosionManager
|
||||||
let explosion = RockFactory.getExplosionFromPool();
|
RockFactory._explosionManager.playExplosion(position, scaling);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user