diff --git a/src/gameConfig.ts b/src/gameConfig.ts new file mode 100644 index 0000000..ddc0aa3 --- /dev/null +++ b/src/gameConfig.ts @@ -0,0 +1,87 @@ +/** + * Texture detail levels for game objects + */ +export enum TextureLevel { + WIREFRAME = 'WIREFRAME', + SIMPLE_MATERIAL = 'SIMPLE_MATERIAL', + FULL_TEXTURE = 'FULL_TEXTURE', + PBR_TEXTURE = 'PBR_TEXTURE' +} + +/** + * Global game configuration settings + * Singleton class for managing game-wide settings + */ +export class GameConfig { + private static _instance: GameConfig; + + // Texture detail settings + public planetTextureLevel: TextureLevel = TextureLevel.FULL_TEXTURE; + public asteroidTextureLevel: TextureLevel = TextureLevel.FULL_TEXTURE; + public sunTextureLevel: TextureLevel = TextureLevel.FULL_TEXTURE; + + // Physics settings + public physicsEnabled: boolean = true; + + /** + * Private constructor for singleton pattern + */ + private constructor() { + // Load settings from localStorage if available + this.loadFromStorage(); + } + + /** + * Get the singleton instance + */ + public static getInstance(): GameConfig { + if (!GameConfig._instance) { + GameConfig._instance = new GameConfig(); + } + return GameConfig._instance; + } + + /** + * Save current configuration to localStorage + */ + public save(): void { + const config = { + planetTextureLevel: this.planetTextureLevel, + asteroidTextureLevel: this.asteroidTextureLevel, + sunTextureLevel: this.sunTextureLevel, + physicsEnabled: this.physicsEnabled + }; + localStorage.setItem('game-config', JSON.stringify(config)); + } + + /** + * Load configuration from localStorage + */ + private loadFromStorage(): void { + try { + const stored = localStorage.getItem('game-config'); + if (stored) { + const config = JSON.parse(stored); + this.planetTextureLevel = config.planetTextureLevel ?? TextureLevel.FULL_TEXTURE; + this.asteroidTextureLevel = config.asteroidTextureLevel ?? TextureLevel.FULL_TEXTURE; + this.sunTextureLevel = config.sunTextureLevel ?? TextureLevel.FULL_TEXTURE; + this.physicsEnabled = config.physicsEnabled ?? true; + } else { + this.save(); + } + } catch (error) { + console.warn('Failed to load game config from localStorage:', error); + } + } + + /** + * Reset to default settings + */ + public reset(): void { + this.planetTextureLevel = TextureLevel.FULL_TEXTURE; + this.asteroidTextureLevel = TextureLevel.FULL_TEXTURE; + this.sunTextureLevel = TextureLevel.FULL_TEXTURE; + this.physicsEnabled = true; + this.save(); + } +} diff --git a/src/level1.ts b/src/level1.ts index 05b8e56..4d85dbf 100644 --- a/src/level1.ts +++ b/src/level1.ts @@ -117,16 +117,18 @@ export class Level1 implements Level { const shipConfig = this._deserializer.getShipConfig(); this._ship.position = new Vector3(shipConfig.position[0], shipConfig.position[1], shipConfig.position[2]); - // Add distance constraints to asteroids + // Add distance constraints to asteroids (if physics enabled) setLoadingMessage("Configuring physics constraints..."); const asteroidMeshes = entities.asteroids; - for (let i = 0; i < asteroidMeshes.length; i++) { - const asteroidMesh = asteroidMeshes[i]; - if (asteroidMesh.physicsBody) { - // Calculate distance from start base - const dist = Vector3.Distance(asteroidMesh.position, this._startBase.position); - const constraint = new DistanceConstraint(dist, DefaultScene.MainScene); - this._startBase.physicsBody.addConstraint(asteroidMesh.physicsBody, constraint); + if (this._startBase.physicsBody) { + for (let i = 0; i < asteroidMeshes.length; i++) { + const asteroidMesh = asteroidMeshes[i]; + if (asteroidMesh.physicsBody) { + // Calculate distance from start base + const dist = Vector3.Distance(asteroidMesh.position, this._startBase.position); + const constraint = new DistanceConstraint(dist, DefaultScene.MainScene); + this._startBase.physicsBody.addConstraint(asteroidMesh.physicsBody, constraint); + } } } diff --git a/src/levelDeserializer.ts b/src/levelDeserializer.ts index 6986f0f..0a51cf2 100644 --- a/src/levelDeserializer.ts +++ b/src/levelDeserializer.ts @@ -27,6 +27,8 @@ import { } from "./levelConfig"; import { FireProceduralTexture } from "@babylonjs/procedural-textures"; import {createSphereLightmap} from "./sphereLightmap"; +import { GameConfig } from "./gameConfig"; +import { MaterialFactory } from "./materialFactory"; /** * Deserializes a LevelConfig JSON object and creates all entities in the scene @@ -92,8 +94,12 @@ export class LevelDeserializer { } mesh.material = material; - const agg = new PhysicsAggregate(mesh, PhysicsShapeType.CONVEX_HULL, { mass: 0 }, this.scene); - agg.body.setMotionType(PhysicsMotionType.ANIMATED); + // Only create physics if enabled in config + const gameConfig = GameConfig.getInstance(); + if (gameConfig.physicsEnabled) { + const agg = new PhysicsAggregate(mesh, PhysicsShapeType.CONVEX_HULL, { mass: 0 }, this.scene); + agg.body.setMotionType(PhysicsMotionType.ANIMATED); + } return mesh; } @@ -116,11 +122,13 @@ export class LevelDeserializer { sun.position = this.arrayToVector3(config.position); - // Create material with procedural fire texture - const material = new StandardMaterial("sunMaterial", this.scene); - material.emissiveTexture = new FireProceduralTexture("fire", 1024, this.scene); - material.emissiveColor = new Color3(0.5, 0.5, 0.1); - material.disableLighting = true; + // Create material using GameConfig texture level + const gameConfig = GameConfig.getInstance(); + const material = MaterialFactory.createSunMaterial( + "sunMaterial", + gameConfig.sunTextureLevel, + this.scene + ); sun.material = material; // Create glow layer @@ -151,33 +159,16 @@ export class LevelDeserializer { // Calculate direction from planet to sun const toSun = sunPosition.subtract(planetPosition).normalize(); - // Apply texture - const material = new StandardMaterial(planetConfig.name + "-material", this.scene); - const texture = new Texture(planetConfig.texturePath, this.scene); - - // Create lightmap with bright light pointing toward sun - const lightmap = createSphereLightmap( - planetConfig.name + "-lightmap", - 256, // texture size - DefaultScene.MainScene, - toSun, // bright light from sun direction - 1, // bright intensity - toSun.negate(), // dim light from opposite direction - 0.3, // dim intensity - 0.3 // ambient + // Create material using GameConfig texture level + const config = GameConfig.getInstance(); + const material = MaterialFactory.createPlanetMaterial( + planetConfig.name + "-material", + planetConfig.texturePath, + config.planetTextureLevel, + this.scene, + toSun ); - // Apply to material - // Use emissiveTexture (self-lit) instead of diffuseTexture when lighting is disabled - material.emissiveTexture = texture; - material.lightmapTexture = lightmap; - material.useLightmapAsShadowmap = true; - - // Disable standard lighting since we're using baked lightmap - material.disableLighting = true; - - material.roughness = 1; - material.specularColor = Color3.Black(); planet.material = material; planets.push(planet); diff --git a/src/materialFactory.ts b/src/materialFactory.ts new file mode 100644 index 0000000..58d01e6 --- /dev/null +++ b/src/materialFactory.ts @@ -0,0 +1,304 @@ +import { + Color3, + DynamicTexture, + NoiseProceduralTexture, + PBRMaterial, + Scene, + StandardMaterial, + Texture, + Vector3 +} from "@babylonjs/core"; +import { TextureLevel } from "./gameConfig"; +import { FireProceduralTexture } from "@babylonjs/procedural-textures"; +import { createSphereLightmap } from "./sphereLightmap"; + +/** + * Factory for creating materials at different quality levels + */ +export class MaterialFactory { + /** + * Create a planet material based on texture level + */ + public static createPlanetMaterial( + name: string, + texturePath: string, + textureLevel: TextureLevel, + scene: Scene, + sunDirection: Vector3 + ): StandardMaterial | PBRMaterial { + switch (textureLevel) { + case TextureLevel.WIREFRAME: + return this.createWireframeMaterial(name, scene, new Color3(0.5, 0.5, 0.8)); + + case TextureLevel.SIMPLE_MATERIAL: + return this.createSimplePlanetMaterial(name, scene); + + case TextureLevel.FULL_TEXTURE: + return this.createFullTexturePlanetMaterial(name, texturePath, scene, sunDirection); + + case TextureLevel.PBR_TEXTURE: + return this.createPBRPlanetMaterial(name, texturePath, scene, sunDirection); + + default: + return this.createFullTexturePlanetMaterial(name, texturePath, scene, sunDirection); + } + } + + /** + * Create an asteroid material based on texture level + */ + public static createAsteroidMaterial( + name: string, + textureLevel: TextureLevel, + scene: Scene, + originalMaterial?: PBRMaterial + ): StandardMaterial | PBRMaterial { + switch (textureLevel) { + case TextureLevel.WIREFRAME: + return this.createWireframeMaterial(name, scene, new Color3(0.5, 0.5, 0.5)); + + case TextureLevel.SIMPLE_MATERIAL: + return this.createSimpleAsteroidMaterial(name, scene); + + case TextureLevel.FULL_TEXTURE: + return this.createFullTextureAsteroidMaterial(name, scene, originalMaterial); + + case TextureLevel.PBR_TEXTURE: + return this.createPBRAsteroidMaterial(name, scene, originalMaterial); + + default: + return this.createFullTextureAsteroidMaterial(name, scene, originalMaterial); + } + } + + /** + * Create a sun material based on texture level + */ + public static createSunMaterial( + name: string, + textureLevel: TextureLevel, + scene: Scene + ): StandardMaterial | PBRMaterial { + switch (textureLevel) { + case TextureLevel.WIREFRAME: + return this.createWireframeMaterial(name, scene, new Color3(1, 1, 0)); + + case TextureLevel.SIMPLE_MATERIAL: + return this.createSimpleSunMaterial(name, scene); + + case TextureLevel.FULL_TEXTURE: + return this.createFullTextureSunMaterial(name, scene); + + case TextureLevel.PBR_TEXTURE: + return this.createPBRSunMaterial(name, scene); + + default: + return this.createFullTextureSunMaterial(name, scene); + } + } + + // ========== Private helper methods ========== + + /** + * Create wireframe material + */ + private static createWireframeMaterial( + name: string, + scene: Scene, + color: Color3 + ): StandardMaterial { + const material = new StandardMaterial(name, scene); + material.wireframe = true; + material.emissiveColor = color; + material.disableLighting = true; + return material; + } + + /** + * Create simple planet material with solid color + */ + private static createSimplePlanetMaterial(name: string, scene: Scene): StandardMaterial { + const material = new StandardMaterial(name, scene); + material.diffuseColor = new Color3(0.4, 0.6, 0.8); + material.specularColor = Color3.Black(); + return material; + } + + /** + * Create full texture planet material (current implementation) + */ + private static createFullTexturePlanetMaterial( + name: string, + texturePath: string, + scene: Scene, + sunDirection: Vector3 + ): StandardMaterial { + const material = new StandardMaterial(name, scene); + const texture = new Texture(texturePath, scene); + + // Create lightmap with bright light pointing toward sun + const lightmap = createSphereLightmap( + name + "-lightmap", + 256, + scene, + sunDirection, + 1, + sunDirection.negate(), + 0.3, + 0.3 + ); + + material.emissiveTexture = texture; + material.lightmapTexture = lightmap; + material.useLightmapAsShadowmap = true; + material.disableLighting = true; + material.roughness = 1; + material.specularColor = Color3.Black(); + + return material; + } + + /** + * Create PBR planet material + */ + private static createPBRPlanetMaterial( + name: string, + texturePath: string, + scene: Scene, + sunDirection: Vector3 + ): PBRMaterial { + const material = new PBRMaterial(name, scene); + const texture = new Texture(texturePath, scene); + + // Create lightmap with bright light pointing toward sun + const lightmap = createSphereLightmap( + name + "-lightmap", + 256, + scene, + sunDirection, + 1, + sunDirection.negate(), + 0.3, + 0.3 + ); + + material.albedoTexture = texture; + material.lightmapTexture = lightmap; + material.useLightmapAsShadowmap = true; + material.roughness = 0.8; + material.metallic = 0; + + return material; + } + + /** + * Create simple asteroid material with solid color + */ + private static createSimpleAsteroidMaterial(name: string, scene: Scene): StandardMaterial { + const material = new StandardMaterial(name, scene); + material.diffuseColor = new Color3(0.4, 0.4, 0.4); + material.specularColor = Color3.Black(); + return material; + } + + /** + * Create full texture asteroid material (current implementation) + */ + private static createFullTextureAsteroidMaterial(name: string, scene: Scene, originalMaterial?: PBRMaterial): StandardMaterial { + // If we have the original material from GLB, use it as a base + if (originalMaterial) { + // Clone the original material to preserve bump texture and other properties + const material = originalMaterial.clone(name) as PBRMaterial; + + // Create noise texture for color variation + const noiseTexture = new NoiseProceduralTexture(name + "-noise", 256, scene); + noiseTexture.brightness = 0.6; + noiseTexture.octaves = 4; + + // Replace only the albedo texture, keeping bump and other textures + material.albedoTexture = noiseTexture; + material.roughness = 1; + + return material as any as StandardMaterial; + } + + // Fallback if no original material + const material = new StandardMaterial(name, scene); + const noiseTexture = new NoiseProceduralTexture(name + "-noise", 256, scene); + noiseTexture.brightness = 0.6; + noiseTexture.octaves = 4; + + material.ambientTexture = noiseTexture; + material.diffuseTexture = noiseTexture; + material.roughness = 1; + + return material; + } + + /** + * Create PBR asteroid material + */ + private static createPBRAsteroidMaterial(name: string, scene: Scene, originalMaterial?: PBRMaterial): PBRMaterial { + // If we have the original material from GLB, use it as a base + if (originalMaterial) { + // Clone the original material to preserve bump texture and other properties + const material = originalMaterial.clone(name) as PBRMaterial; + + // Create noise texture for color variation + const noiseTexture = new NoiseProceduralTexture(name + "-noise", 256, scene); + noiseTexture.brightness = 0.6; + noiseTexture.octaves = 4; + + // Replace only the albedo texture, keeping bump and other textures + material.albedoTexture = noiseTexture; + material.roughness = 1; + material.metallic = 0; + + return material; + } + + // Fallback if no original material + const material = new PBRMaterial(name, scene); + const noiseTexture = new NoiseProceduralTexture(name + "-noise", 256, scene); + noiseTexture.brightness = 0.6; + noiseTexture.octaves = 4; + + material.albedoTexture = noiseTexture; + material.roughness = 1; + material.metallic = 0; + + return material; + } + + /** + * Create simple sun material with solid color + */ + private static createSimpleSunMaterial(name: string, scene: Scene): StandardMaterial { + const material = new StandardMaterial(name, scene); + material.emissiveColor = new Color3(1, 0.9, 0.2); + material.disableLighting = true; + return material; + } + + /** + * Create full texture sun material (current implementation) + */ + private static createFullTextureSunMaterial(name: string, scene: Scene): StandardMaterial { + const material = new StandardMaterial(name, scene); + material.emissiveTexture = new FireProceduralTexture("fire", 1024, scene); + material.emissiveColor = new Color3(0.5, 0.5, 0.1); + material.disableLighting = true; + return material; + } + + /** + * Create PBR sun material + */ + private static createPBRSunMaterial(name: string, scene: Scene): PBRMaterial { + const material = new PBRMaterial(name, scene); + material.emissiveTexture = new FireProceduralTexture("fire", 1024, scene); + material.emissiveColor = new Color3(0.5, 0.5, 0.1); + material.unlit = true; + return material; + } +} diff --git a/src/ship.ts b/src/ship.ts index a9e13a0..6a64586 100644 --- a/src/ship.ts +++ b/src/ship.ts @@ -21,6 +21,7 @@ import { } from "@babylonjs/core"; import type {AudioEngineV2, StaticSound} from "@babylonjs/core"; import {DefaultScene} from "./defaultScene"; +import { GameConfig } from "./gameConfig"; const MAX_FORWARD_THRUST = 40; const controllerComponents = [ @@ -97,6 +98,12 @@ export class Ship { } private shoot() { + // Only allow shooting if physics is enabled + const config = GameConfig.getInstance(); + if (!config.physicsEnabled) { + return; + } + this._shot?.play(); const ammo = new InstancedMesh("ammo", this._ammoBaseMesh as Mesh); ammo.parent = this._ship; @@ -202,38 +209,41 @@ export class Ship { shipMesh.name = "shipMesh"; shipMesh.parent = this._ship; - // Create physics aggregate based on the loaded mesh - // Find the actual geometry mesh (usually meshes[1] or a child) - //const geometryMesh = importMesh.meshes.find(m => m instanceof Mesh && m.getTotalVertices() > 0) as Mesh; - const geo = shipMesh.getChildMeshes()[0] - if (geo) { + // Create physics aggregate based on the loaded mesh (if physics enabled) + const config = GameConfig.getInstance(); + if (config.physicsEnabled) { + // Find the actual geometry mesh (usually meshes[1] or a child) + //const geometryMesh = importMesh.meshes.find(m => m instanceof Mesh && m.getTotalVertices() > 0) as Mesh; + const geo = shipMesh.getChildMeshes()[0] + if (geo) { - // Create physics aggregate on the ship TransformNode using the mesh shape - const agg = new PhysicsAggregate(this._ship, PhysicsShapeType.CONVEX_HULL, { - mass: 100, - mesh: (geo as Mesh) // Use the actual ship geometry - }, DefaultScene.MainScene); + // Create physics aggregate on the ship TransformNode using the mesh shape + const agg = new PhysicsAggregate(this._ship, PhysicsShapeType.CONVEX_HULL, { + mass: 100, + mesh: (geo as Mesh) // Use the actual ship geometry + }, DefaultScene.MainScene); - agg.body.setMotionType(PhysicsMotionType.DYNAMIC); - agg.body.setLinearDamping(.1); - agg.body.setAngularDamping(.2); - agg.body.setAngularVelocity(new Vector3(0, 0, 0)); - agg.body.setCollisionCallbackEnabled(true); - } else { - console.warn("No geometry mesh found in ship1.glb, falling back to box shape"); - // Fallback to box shape if mesh not found - const agg = new PhysicsAggregate(this._ship, PhysicsShapeType.BOX, { - mass: 100, - extents: new Vector3(4, 4, 7.4), - center: new Vector3(0, 1, 1.8) - }, DefaultScene.MainScene); + agg.body.setMotionType(PhysicsMotionType.DYNAMIC); + agg.body.setLinearDamping(.1); + agg.body.setAngularDamping(.2); + agg.body.setAngularVelocity(new Vector3(0, 0, 0)); + agg.body.setCollisionCallbackEnabled(true); + } else { + console.warn("No geometry mesh found in ship1.glb, falling back to box shape"); + // Fallback to box shape if mesh not found + const agg = new PhysicsAggregate(this._ship, PhysicsShapeType.BOX, { + mass: 100, + extents: new Vector3(4, 4, 7.4), + center: new Vector3(0, 1, 1.8) + }, DefaultScene.MainScene); - agg.body.setMotionType(PhysicsMotionType.DYNAMIC); - agg.body.setLinearDamping(.1); - agg.body.setAngularDamping(.2); - agg.body.setAngularVelocity(new Vector3(0, 0, 0)); - agg.body.setCollisionCallbackEnabled(true); + agg.body.setMotionType(PhysicsMotionType.DYNAMIC); + agg.body.setLinearDamping(.1); + agg.body.setAngularDamping(.2); + agg.body.setAngularVelocity(new Vector3(0, 0, 0)); + agg.body.setCollisionCallbackEnabled(true); + } } //shipMesh.rotation.y = Angle.FromDegrees(90).radians(); //shipMesh.rotation.y = Math.PI; diff --git a/src/starfield.ts b/src/starfield.ts index 55301da..85271f7 100644 --- a/src/starfield.ts +++ b/src/starfield.ts @@ -17,6 +17,8 @@ import {DefaultScene} from "./defaultScene"; import {ScoreEvent} from "./scoreboard"; import {Debug} from "@babylonjs/core/Legacy/legacy"; import {createSphereLightmap} from "./sphereLightmap"; +import { GameConfig } from "./gameConfig"; +import { MaterialFactory } from "./materialFactory"; let _particleData: any = null; export class Rock { @@ -24,8 +26,8 @@ export class Rock { constructor(mesh: AbstractMesh) { this._rockMesh = mesh; } - public get physicsBody(): PhysicsBody { - return this._rockMesh.physicsBody; + public get physicsBody(): PhysicsBody | null { + return this._rockMesh.physicsBody || null; } public get position(): Vector3 { return this._rockMesh.getAbsolutePosition(); @@ -35,6 +37,7 @@ export class Rock { 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 _viewer: PhysicsViewer = null; @@ -64,18 +67,20 @@ export class RockFactory { //importMesh.meshes[1].dispose(); console.log(importMesh.meshes); if (!this._rockMaterial) { - this._rockMaterial = this._rockMesh.material.clone("asteroid") as PBRMaterial; + // Clone the original material from GLB to preserve all textures + this._originalMaterial = this._rockMesh.material.clone("asteroid-original") as PBRMaterial; + console.log('Cloned original material from GLB:', this._originalMaterial); - this._rockMaterial.name = 'asteroid-material'; - this._rockMaterial.id = 'asteroid-material'; - const material = (this._rockMaterial as PBRMaterial) - const noiseTexture = new NoiseProceduralTexture("asteroid-noise", 256, DefaultScene.MainScene); - noiseTexture.brightness = 0.6; // Brighter base color - noiseTexture.octaves = 4; // More detaila - material.albedoTexture = noiseTexture; - material.roughness = 1; + // Create material using GameConfig texture level + const config = GameConfig.getInstance(); + this._rockMaterial = MaterialFactory.createAsteroidMaterial( + 'asteroid-material', + config.asteroidTextureLevel, + DefaultScene.MainScene, + this._originalMaterial + ) as PBRMaterial; - this._rockMesh.material = material; + this._rockMesh.material = this._rockMaterial; importMesh.meshes[1].dispose(false, true); importMesh.meshes[0].dispose(); } @@ -104,84 +109,89 @@ export class RockFactory { rock.metadata = {type: 'asteroid'}; rock.setEnabled(true); - // PhysicsAggregate will automatically compute sphere size from mesh bounding info - // The mesh scaling is already applied, so Babylon will create correctly sized physics shape - const agg = new PhysicsAggregate(rock, PhysicsShapeType.SPHERE, { - mass: 10000, - restitution: .5 - // Don't pass radius - let Babylon compute from scaled mesh bounds - }, DefaultScene.MainScene); - const body = agg.body; + // Only create physics if enabled in config + const config = GameConfig.getInstance(); + if (config.physicsEnabled) { + // PhysicsAggregate will automatically compute sphere size from mesh bounding info + // The mesh scaling is already applied, so Babylon will create correctly sized physics shape + const agg = new PhysicsAggregate(rock, PhysicsShapeType.SPHERE, { + mass: 10000, + restitution: .5 + // Don't pass radius - let Babylon compute from scaled mesh bounds + }, DefaultScene.MainScene); + const body = agg.body; - if (!this._viewer) { - // this._viewer = new PhysicsViewer(DefaultScene.MainScene); - } + if (!this._viewer) { + // this._viewer = new PhysicsViewer(DefaultScene.MainScene); + } - // this._viewer.showBody(body); - body.setLinearDamping(0) - body.setMotionType(PhysicsMotionType.DYNAMIC); - body.setCollisionCallbackEnabled(true); - let scaling = Vector3.One(); - body.getCollisionObservable().add((eventData) => { - if (eventData.type == 'COLLISION_STARTED') { - if ( eventData.collidedAgainst.transformNode.id == 'ammo') { - score.notifyObservers({score: 1, remaining: -1, message: "Asteroid Destroyed"}); - const position = eventData.point; + // this._viewer.showBody(body); + body.setLinearDamping(0) + body.setMotionType(PhysicsMotionType.DYNAMIC); + body.setCollisionCallbackEnabled(true); + let scaling = Vector3.One(); + body.getCollisionObservable().add((eventData) => { + if (eventData.type == 'COLLISION_STARTED') { + if ( eventData.collidedAgainst.transformNode.id == 'ammo') { + score.notifyObservers({score: 1, remaining: -1, message: "Asteroid Destroyed"}); + const position = eventData.point; - eventData.collider.shape.dispose(); - eventData.collider.transformNode.dispose(); - eventData.collider.dispose(); - scaling = eventData.collider.transformNode.scaling.clone(); - console.log(scaling); - eventData.collidedAgainst.shape.dispose(); - eventData.collidedAgainst.transformNode.dispose(); - eventData.collidedAgainst.dispose(); + eventData.collider.shape.dispose(); + eventData.collider.transformNode.dispose(); + eventData.collider.dispose(); + scaling = eventData.collider.transformNode.scaling.clone(); + console.log(scaling); + eventData.collidedAgainst.shape.dispose(); + eventData.collidedAgainst.transformNode.dispose(); + eventData.collidedAgainst.dispose(); - // Get explosion from pool (or create new if pool empty) - let explosion = RockFactory.getExplosionFromPool(); + // 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); + 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.start(point); + // 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(() => { - set.dispose(); + explosion.systems.forEach((system: ParticleSystem) => { + system.stop(); + }); + RockFactory.returnExplosionToPool(explosion); 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); + } } } - } - }); - //body.setAngularVelocity(new Vector3(Math.random(), Math.random(), Math.random())); - // body.setLinearVelocity(Vector3.Random(-10, 10)); + }); + //body.setAngularVelocity(new Vector3(Math.random(), Math.random(), Math.random())); + // body.setLinearVelocity(Vector3.Random(-10, 10)); + } + return new Rock(rock); } }