Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions src/audio/systems/audio-system.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -14,7 +18,7 @@ describe('createAudioEcsSystem (Audio)', () => {
const audioComponent: AudioEcsComponent = {
sound: {
play: mockPlay,
} as any,
} as MockHowl,
playSound: true,
};

Expand All @@ -35,7 +39,7 @@ describe('createAudioEcsSystem (Audio)', () => {
const audioComponent: AudioEcsComponent = {
sound: {
play: mockPlay,
} as any,
} as MockHowl,
playSound: false,
};

Expand All @@ -56,7 +60,7 @@ describe('createAudioEcsSystem (Audio)', () => {
const audioComponent: AudioEcsComponent = {
sound: {
play: mockPlay,
} as any,
} as MockHowl,
playSound: true,
};

Expand All @@ -82,7 +86,7 @@ describe('createAudioEcsSystem (Audio)', () => {
const audioComponent1: AudioEcsComponent = {
sound: {
play: mockPlay1,
} as any,
} as MockHowl,
playSound: true,
};

Expand All @@ -91,7 +95,7 @@ describe('createAudioEcsSystem (Audio)', () => {
const audioComponent2: AudioEcsComponent = {
sound: {
play: mockPlay2,
} as any,
} as MockHowl,
playSound: false,
};

Expand All @@ -100,7 +104,7 @@ describe('createAudioEcsSystem (Audio)', () => {
const audioComponent3: AudioEcsComponent = {
sound: {
play: mockPlay3,
} as any,
} as MockHowl,
playSound: true,
};

Expand Down Expand Up @@ -128,7 +132,7 @@ describe('createAudioEcsSystem (Audio)', () => {
const audioComponent: AudioEcsComponent = {
sound: {
play: mockPlay,
} as any,
} as MockHowl,
playSound: true,
};

Expand Down
123 changes: 65 additions & 58 deletions src/common/systems/transform-system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,78 +13,41 @@ type TransformCache = {
visiting: Set<number>;
};

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);
Expand Down Expand Up @@ -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);
Expand Down
6 changes: 6 additions & 0 deletions src/particles/systems/particle-emitter-system.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down
10 changes: 0 additions & 10 deletions src/rendering/systems/render-system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
4 changes: 2 additions & 2 deletions src/utilities/game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down