smolworld / src /js /scene.js
p3nGu1nZz's picture
✨ Refactor physics module; add math utilities and event handling classes; update state structure for 3D entities
7ffaa9e
import { Layer, GameLayer, GUILayer, DebugLayer, TerminalLayer, LayerType } from './layer.js'; // Add LayerType
import { Camera } from './camera.js';
import { FPSCounter, ZoomControls } from './gui.js';
import { Entity } from './entity.js';
import { getConfig } from './config.js';
import { state } from './state.js';
export class Scene {
constructor() {
this.rootLayer = new Layer('root');
this.camera = new Camera();
this.entities = new Map();
}
start() {
// Create layer hierarchy with correct order
const gameLayer = new GameLayer();
const guiLayer = new GUILayer();
const debugLayer = new DebugLayer();
const terminalLayer = new TerminalLayer();
// Set up layer hierarchy (bottom to top)
gameLayer.setParent(this.rootLayer); // game -> root
guiLayer.setParent(gameLayer); // gui -> game
debugLayer.setParent(guiLayer); // debug -> gui
terminalLayer.setParent(debugLayer); // terminal -> debug (top-most)
// Create and add debug controls
const fpsCounter = new FPSCounter();
const zoomControls = new ZoomControls();
debugLayer.addEntity(fpsCounter);
debugLayer.addEntity(zoomControls);
// Store references needed by state
state.fpsCounter = fpsCounter;
state.zoomControls = zoomControls;
// Create test entity with circular motion
const config = getConfig();
const testEntity = new Entity();
testEntity.boundingBox = { width: config.squareSize, height: config.squareSize };
// Set initial position
const centerX = 0;
const centerY = 0;
testEntity.transform.setPosition(
centerX + Math.cos(0) * config.radius,
centerY + Math.sin(0) * config.radius,
0
);
// Store animation state with the entity
testEntity.animationState = {
angle: 0,
speed: config.initialAngularSpeed,
centerX,
centerY,
radius: config.radius
};
// Add update method to the entity
testEntity.update = (deltaTime) => {
const state = testEntity.animationState;
state.speed += config.angularAcceleration * deltaTime;
state.angle += state.speed * deltaTime;
// Update position based on circular motion
testEntity.transform.setPosition(
state.centerX + Math.cos(state.angle) * state.radius,
state.centerY + Math.sin(state.angle) * state.radius,
0
);
};
// Add entity to game layer
gameLayer.addEntity(testEntity);
state.testEntity = testEntity; // Store reference in state
// Initial camera setup
this.camera.moveTo(0, 0, 0);
}
getLayer(type) {
let current = this.rootLayer;
while (current) {
if (current.type === type) {
return current;
}
current = current.child;
}
return null;
}
getLayers() {
const layers = [];
let current = this.rootLayer;
while (current) {
if (current.type !== 'root') { // Don't include root layer in handlers
layers.push(current);
}
current = current.child;
}
return layers;
}
draw(ctx) {
// Start drawing from root layer
this.rootLayer.draw(ctx, this.camera);
}
update(deltaTime) {
// Update all entities in the game layer
const gameLayer = this.getLayer(LayerType.GAME);
gameLayer?.entities.forEach(entity => {
if (entity.update) {
entity.update(deltaTime);
}
});
// Update debug layer's FPS counter
const debugLayer = this.getLayer(LayerType.DEBUG);
if (debugLayer) {
debugLayer.entities.forEach(entity => {
if (entity.constructor.name === 'FPSCounter' && entity.update) {
entity.update(
// We store fps in state.fps
window.state?.fps ?? 0,
// Pass camera velocity
this.camera?.currentVelocity ?? { x: 0, y: 0 }
);
}
});
}
}
}