diff --git a/src/audio/systems/audio-system.test.ts b/src/audio/systems/audio-system.test.ts index f4771f15..5c5d7743 100644 --- a/src/audio/systems/audio-system.test.ts +++ b/src/audio/systems/audio-system.test.ts @@ -3,6 +3,10 @@ import { createAudioEcsSystem } from './audio-system'; import { type AudioEcsComponent, audioId } from '../components'; import { EcsWorld } from '../../new-ecs'; +interface MockHowl { + play: () => void; +} + describe('createAudioEcsSystem (Audio)', () => { it('should play sound when playSound is true', () => { const ecsWorld = new EcsWorld(); @@ -14,7 +18,7 @@ describe('createAudioEcsSystem (Audio)', () => { const audioComponent: AudioEcsComponent = { sound: { play: mockPlay, - } as any, + } as MockHowl, playSound: true, }; @@ -35,7 +39,7 @@ describe('createAudioEcsSystem (Audio)', () => { const audioComponent: AudioEcsComponent = { sound: { play: mockPlay, - } as any, + } as MockHowl, playSound: false, }; @@ -56,7 +60,7 @@ describe('createAudioEcsSystem (Audio)', () => { const audioComponent: AudioEcsComponent = { sound: { play: mockPlay, - } as any, + } as MockHowl, playSound: true, }; @@ -82,7 +86,7 @@ describe('createAudioEcsSystem (Audio)', () => { const audioComponent1: AudioEcsComponent = { sound: { play: mockPlay1, - } as any, + } as MockHowl, playSound: true, }; @@ -91,7 +95,7 @@ describe('createAudioEcsSystem (Audio)', () => { const audioComponent2: AudioEcsComponent = { sound: { play: mockPlay2, - } as any, + } as MockHowl, playSound: false, }; @@ -100,7 +104,7 @@ describe('createAudioEcsSystem (Audio)', () => { const audioComponent3: AudioEcsComponent = { sound: { play: mockPlay3, - } as any, + } as MockHowl, playSound: true, }; @@ -128,7 +132,7 @@ describe('createAudioEcsSystem (Audio)', () => { const audioComponent: AudioEcsComponent = { sound: { play: mockPlay, - } as any, + } as MockHowl, playSound: true, }; diff --git a/src/common/systems/transform-system.ts b/src/common/systems/transform-system.ts index 92a7009f..f8c9ace8 100644 --- a/src/common/systems/transform-system.ts +++ b/src/common/systems/transform-system.ts @@ -13,78 +13,41 @@ type TransformCache = { visiting: Set; }; -function computeWorld( +function setTransformsToLocal( entity: number, - cache: TransformCache, world: EcsWorld, + cache: TransformCache, ): void { - if (cache.computed.has(entity)) { - return; - } - - // Cycle detection: if we re-enter an entity, break the cycle by treating it as a root. - if (cache.visiting.has(entity)) { - const positionComponent = world.getComponent(entity, positionId); - - if (positionComponent) { - positionComponent.world.x = positionComponent.local.x; - positionComponent.world.y = positionComponent.local.y; - } - - const rotationComponent = world.getComponent(entity, rotationId); - - if (rotationComponent) { - rotationComponent.world = rotationComponent.local; - } - - const scaleComponent = world.getComponent(entity, scaleId); - - if (scaleComponent) { - scaleComponent.world = scaleComponent.local; - } - - cache.computed.add(entity); + const positionComponent = world.getComponent(entity, positionId); - return; + if (positionComponent) { + positionComponent.world.x = positionComponent.local.x; + positionComponent.world.y = positionComponent.local.y; } - cache.visiting.add(entity); - - const positionComponent = world.getComponent(entity, positionId); const rotationComponent = world.getComponent(entity, rotationId); - const scaleComponent = world.getComponent(entity, scaleId); - if (!positionComponent && !rotationComponent && !scaleComponent) { - cache.visiting.delete(entity); - - return; + if (rotationComponent) { + rotationComponent.world = rotationComponent.local; } - const parentComponent = world.getComponent(entity, parentId); - - if (!parentComponent) { - if (positionComponent) { - positionComponent.world.x = positionComponent.local.x; - positionComponent.world.y = positionComponent.local.y; - } - - if (rotationComponent) { - rotationComponent.world = rotationComponent.local; - } - - if (scaleComponent) { - scaleComponent.world = scaleComponent.local; - } - - cache.visiting.delete(entity); - cache.computed.add(entity); + const scaleComponent = world.getComponent(entity, scaleId); - return; + if (scaleComponent) { + scaleComponent.world = scaleComponent.local; } - const parentEntity = parentComponent.parent; + cache.computed.add(entity); +} - computeWorld(parentEntity, cache, world); +function applyParentTransforms( + entity: number, + parentEntity: number, + world: EcsWorld, +): void { + const positionComponent = world.getComponent(entity, positionId); + const rotationComponent = world.getComponent(entity, rotationId); + const scaleComponent = world.getComponent(entity, scaleId); const parentPosition = world.getComponent(parentEntity, positionId); const parentRotation = world.getComponent(parentEntity, rotationId); @@ -119,6 +82,50 @@ function computeWorld( scaleComponent.world.y = scaleComponent.local.y; } } +} + +function computeWorld( + entity: number, + cache: TransformCache, + world: EcsWorld, +): void { + if (cache.computed.has(entity)) { + return; + } + + // Cycle detection: if we re-enter an entity, break the cycle by treating it as a root. + if (cache.visiting.has(entity)) { + setTransformsToLocal(entity, world, cache); + + return; + } + + cache.visiting.add(entity); + + const positionComponent = world.getComponent(entity, positionId); + const rotationComponent = world.getComponent(entity, rotationId); + const scaleComponent = world.getComponent(entity, scaleId); + + if (!positionComponent && !rotationComponent && !scaleComponent) { + cache.visiting.delete(entity); + + return; + } + + const parentComponent = world.getComponent(entity, parentId); + + if (!parentComponent) { + setTransformsToLocal(entity, world, cache); + cache.visiting.delete(entity); + + return; + } + + const parentEntity = parentComponent.parent; + + computeWorld(parentEntity, cache, world); + + applyParentTransforms(entity, parentEntity, world); cache.visiting.delete(entity); cache.computed.add(entity); diff --git a/src/particles/systems/particle-emitter-system.test.ts b/src/particles/systems/particle-emitter-system.test.ts index e71c741f..6aefbd05 100644 --- a/src/particles/systems/particle-emitter-system.test.ts +++ b/src/particles/systems/particle-emitter-system.test.ts @@ -54,6 +54,12 @@ describe('ParticleEmitterSystem', () => { time.update(100); world.update(); + + // Verify no particles were created - expect the component set to not exist + expect(() => { + const particles: number[] = []; + world.queryEntities([ParticleId], particles); + }).toThrow('No components found for the given name: Symbol(Particle).'); }); it('should start emitting when startEmitting is true', () => { diff --git a/src/rendering/systems/render-system.ts b/src/rendering/systems/render-system.ts index baae42ac..6da871e2 100644 --- a/src/rendering/systems/render-system.ts +++ b/src/rendering/systems/render-system.ts @@ -48,16 +48,6 @@ const includeBatch = ( renderable.bind(gl); - // if (camera.scissorRect) { - // gl.enable(gl.SCISSOR_TEST); - // gl.scissor( - // Math.floor(camera.scissorRect.origin.x * gl.canvas.width), - // Math.floor(camera.scissorRect.origin.y * gl.canvas.height), - // Math.floor(camera.scissorRect.size.x * gl.canvas.width), - // Math.floor(camera.scissorRect.size.y * gl.canvas.height), - // ); - // } - const requiredBatchSize = entities.length * renderable.floatsPerInstance; if (!batch.buffer || batch.buffer.length < requiredBatchSize) { diff --git a/src/utilities/game.ts b/src/utilities/game.ts index 624b800d..69eabe4e 100644 --- a/src/utilities/game.ts +++ b/src/utilities/game.ts @@ -5,12 +5,12 @@ import { EcsWorld } from '../new-ecs/ecs-world.js'; * Manages the game loop and coordinates updates between systems. */ export class Game implements Stoppable { + public readonly container: HTMLElement; + private _isRunning = false; private _animationFrameId: number | null = null; - private readonly _time: Time; private readonly _world: EcsWorld; - public readonly container: HTMLElement; /** * Creates a new Game instance.