diff --git a/index.html b/index.html index 6613bf0..4ae5d46 100644 --- a/index.html +++ b/index.html @@ -2,14 +2,29 @@ - - Motion + + Entity Component System Animation Engine - Gaston Morixe +
+

Entity Component System (ECS)

+

Physics Animation Engine (WIP)

+

+ by Gaston Morixe 2024 + Github Repo + - MIT +

+
1
2
3
- +
+ +
diff --git a/src/dom.ts b/src/dom.ts index 6480c49..b4bde18 100644 --- a/src/dom.ts +++ b/src/dom.ts @@ -91,27 +91,14 @@ export class DOMComponent extends Component { } export class DOMUpdateSystem extends System { - private domLinks: Map = new Map(); - - // Link an entity with a DOM element - linkEntityToDOM(entity: Entity, domElement: HTMLElement) { - this.domLinks.set(entity.id, domElement); - } - // Update the DOM element positions based on the entity's position component update(entities: Entity[]) { entities.forEach((entity) => { - // const position = entity.getComponent(PositionComponent); - // if (position && this.domLinks.has(entity.id)) { - // const domElement = this.domLinks.get(entity.id); - // if (domElement) { - // domElement.style.left = `${position.x}px`; - // domElement.style.top = `${position.y}px`; - // } - // } const position = entity.getComponent(PositionComponent); - if (position && this.domLinks.has(entity.id)) { - const domElement = this.domLinks.get(entity.id); + const domComponent = entity.getComponent(DOMComponent); + + if (position && domComponent) { + const domElement = domComponent.domElement; if (domElement) { domElement.style.transform = `translate(${position.x}px, ${position.y}px)`; } diff --git a/src/entities.ts b/src/entities.ts new file mode 100644 index 0000000..c55e1ab --- /dev/null +++ b/src/entities.ts @@ -0,0 +1,52 @@ +import { Entity } from "./ecs"; +import { + PositionComponent, + VelocityComponent, + ForceComponent, + FrictionComponent, + MassComponent, + AccumulatedForceComponent, +} from "./components"; +import { DOMComponent, MouseDragComponent } from "./dom"; + +// Define BoxEntity to represent a box that can be dragged +export class BoxEntity extends Entity { + constructor( + domElement: HTMLElement, + initialPositon: { x: number; y: number }, + name?: string, + initialVelocity: { vx: number; vy: number } = { vx: 0, vy: 0 }, + mass: number = 1, + friction: number = 0.05, + ) { + super(); + + this.name = name; + this.addComponent( + new PositionComponent(initialPositon.x, initialPositon.y), + ); // Initial position + this.addComponent( + new VelocityComponent(initialVelocity.vx, initialVelocity.vy), + ); // Initial velocity + this.addComponent(new MassComponent(mass)); // Mass of the entity + this.addComponent(new ForceComponent()); // Force acting on the entity + this.addComponent(new AccumulatedForceComponent()); // Force acting on the entity + this.addComponent(new MouseDragComponent()); // Component for mouse dragging + this.addComponent(new FrictionComponent(friction)); // Friction acting on the entity + this.addComponent(new DOMComponent(domElement)); // DOM element associated with the entity + } +} + +// Define an AnchorEntity to represent a fixed anchor point +export class AnchorEntity extends Entity { + constructor(initialPositon: { x: number; y: number }, name?: string) { + super(); + this.name = name; + this.addComponent( + new PositionComponent(initialPositon.x, initialPositon.y), + ); // Initial position + this.addComponent(new VelocityComponent(0, 0)); // Initial velocity + this.addComponent(new ForceComponent()); // Force acting on the entity + this.addComponent(new AccumulatedForceComponent()); // Force acting on the entity + } +} diff --git a/src/main.ts b/src/main.ts index f8a0ed1..ecd8ed0 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,97 +4,49 @@ import "./style.css"; import { AnimationEngine } from "./engine"; // ECS -import { Entity } from "./ecs"; -import { - PositionComponent, - VelocityComponent, - MassComponent, - ForceComponent, - AccumulatedForceComponent, - FrictionComponent, -} from "./components"; import { MovementSystem, FrictionSystem } from "./systems"; -// Specific Components and Systems -import { - MouseDragComponent, - MouseForceSystem, - DOMComponent, - DOMUpdateSystem, - DOMMouseDragHandler, -} from "./dom"; +// Specialized Systems and Entities +import { MouseForceSystem, DOMUpdateSystem, DOMMouseDragHandler } from "./dom"; +import { BoxEntity, AnchorEntity } from "./entities"; import { SpringEntity, SpringPhysicsSystem } from "./spring"; import { ChartSystem } from "./chart"; // -// Application Logic +// -- Entities -- // -// Create the ECS engine -const engine = new AnimationEngine(); - // Create the box entity -const boxEntity = new Entity(); -boxEntity.name = "box1"; -boxEntity.addComponent(new PositionComponent(100, 100)); // Initial position -boxEntity.addComponent(new VelocityComponent(0, 0)); // Initial velocity -boxEntity.addComponent(new MassComponent(1)); // Mass of the entity -boxEntity.addComponent(new ForceComponent()); // Force acting on the entity -boxEntity.addComponent(new AccumulatedForceComponent()); // Force acting on the entity TODO: may not be needed -boxEntity.addComponent(new MouseDragComponent()); // Component for mouse dragging -boxEntity.addComponent(new FrictionComponent(0.05)); - const boxElement = document.getElementById("box1") as HTMLElement; -boxEntity.addComponent(new DOMComponent(boxElement)); +const boxEntity = new BoxEntity(boxElement, { x: 100, y: 100 }, "box1"); -// Creating the spring force -const anchorEntity = new Entity(); -anchorEntity.name = "anchor"; -anchorEntity.addComponent(new PositionComponent(100, 100)); // Fixed point for the spring -anchorEntity.addComponent(new VelocityComponent(0, 0)); // Initial velocity of the anchor point -anchorEntity.addComponent(new ForceComponent()); // Force acting on the anchor point -anchorEntity.addComponent(new AccumulatedForceComponent()); // Force acting on the anchor point +// Creating a fixed anchor +const anchorEntity = new AnchorEntity({ x: 100, y: 100 }, "anchor"); -// Create a spring entity that connects entityA and entityB +// Create a spring entity that connects box1 and anchor const springEntity = new SpringEntity(boxEntity, anchorEntity, 0.2, 0.05, 1.0); springEntity.name = "spring"; // Create second box entity -const boxEntity2 = new Entity(); -boxEntity2.name = "box2"; -boxEntity2.addComponent(new PositionComponent(250, 100)); // Initial position -boxEntity2.addComponent(new VelocityComponent(0, 0)); // Initial velocity -boxEntity2.addComponent(new MassComponent(1)); // Mass of the entity -boxEntity2.addComponent(new ForceComponent()); // Force acting on the entity -boxEntity2.addComponent(new AccumulatedForceComponent()); // Force acting on the entity TODO: may not be needed -boxEntity2.addComponent(new MouseDragComponent()); // Component for mouse dragging -boxEntity2.addComponent(new FrictionComponent(0.05)); - const boxElement2 = document.getElementById("box2") as HTMLElement; -boxEntity2.addComponent(new DOMComponent(boxElement2)); +const boxEntity2 = new BoxEntity(boxElement2, { x: 250, y: 100 }, "box2"); // Creating the spring force connecting box and box2 const springEntity2 = new SpringEntity(boxEntity, boxEntity2, 0.2, 0.05, 2.0); springEntity2.name = "spring2"; // Create third box entity -const boxEntity3 = new Entity(); -boxEntity3.name = "box3"; -boxEntity3.addComponent(new PositionComponent(400, 100)); // Initial position -boxEntity3.addComponent(new VelocityComponent(0, 0)); // Initial velocity -boxEntity3.addComponent(new MassComponent(1)); // Mass of the entity -boxEntity3.addComponent(new ForceComponent()); // Force acting on the entity -boxEntity3.addComponent(new AccumulatedForceComponent()); // Force acting on the entity -boxEntity3.addComponent(new MouseDragComponent()); // Component for mouse dragging -boxEntity3.addComponent(new FrictionComponent(0.05)); - const boxElement3 = document.getElementById("box3") as HTMLElement; -boxEntity3.addComponent(new DOMComponent(boxElement3)); +const boxEntity3 = new BoxEntity(boxElement3, { x: 400, y: 100 }, "box3"); // Creating the spring force connecting box2 and box3 const springEntity3 = new SpringEntity(boxEntity2, boxEntity3, 0.1, 0.05, 1.0); springEntity3.name = "spring3"; +// +// --- Systems --- +// + // Set up the movement system (handles physics and movement) const movementSystem = new MovementSystem(); @@ -109,9 +61,6 @@ const mouseForceSystem = new MouseForceSystem(0.2, 0.1); // Drag strength and da // Set up the DOM update system (handles syncing the DOM with the entity position) const domUpdateSystem = new DOMUpdateSystem(); -domUpdateSystem.linkEntityToDOM(boxEntity, boxElement); -domUpdateSystem.linkEntityToDOM(boxEntity2, boxElement2); -domUpdateSystem.linkEntityToDOM(boxEntity3, boxElement3); // Set up the DOM mouse drag handler to handle mouse events via the DOM component const domMouseDragHandler = new DOMMouseDragHandler(); @@ -123,6 +72,13 @@ domMouseDragHandler2.initializeDragListeners(boxEntity2); const domMouseDragHandler3 = new DOMMouseDragHandler(); domMouseDragHandler3.initializeDragListeners(boxEntity3); +// +// -- Engine -- +// + +// Create the ECS engine +const engine = new AnimationEngine(); + // Add Entities to the engine engine.addEntity(anchorEntity); engine.addEntity(boxEntity); diff --git a/src/style.css b/src/style.css index 75bd84f..849550f 100644 --- a/src/style.css +++ b/src/style.css @@ -4,7 +4,8 @@ font-weight: 400; /*color-scheme: light dark;*/ - color: rgba(255, 255, 255, 0.87); + /*color: rgba(255, 255, 255, 0.87);*/ + color: black; background-color: #fff; font-synthesis: none; @@ -13,6 +14,23 @@ -moz-osx-font-smoothing: grayscale; } +html, +body { + width: 100%; + position: relative; + height: 100%; +} + +h1 { + margin: 0; +} +h2 { + margin: 0; +} +p { + margin: 0; +} + .box { --bg-color: blue; background-color: var(--bg-color); @@ -33,6 +51,7 @@ justify-content: center; font-size: 1.5rem; font-weight: 600; + color: white; &.dragging { cursor: grabbing; box-shadow: 0 15px 15px rgba(0, 0, 0, 0.15); @@ -47,10 +66,23 @@ --bg-color: green; } -#chart { +#chart-container { pointer-events: none; user-select: none; position: absolute; bottom: 20px; - width: 100%; + width: calc(100% - 20px); + left: 0; + right: 0; + height: 90%; + /*background-color: yellow;*/ + display: flex; + flex-direction: column; + justify-content: flex-end; + z-index: -1; + canvas { + /*height: 100%;*/ + width: 100%; + /*background-color: green;*/ + } }