Feature/Refactor to use ecs architecture (#14)
Reviewed-on: #14 Co-authored-by: Juan Sebastian Montoya <juansmm@outlook.com> Co-committed-by: Juan Sebastian Montoya <juansmm@outlook.com>
This commit is contained in:
parent
e0436e7769
commit
cec1fccc22
23 changed files with 1709 additions and 650 deletions
157
src/game/EntityFactory.js
Normal file
157
src/game/EntityFactory.js
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
import { Transform } from '../components/Transform.js';
|
||||
import { Velocity } from '../components/Velocity.js';
|
||||
import { MeshComponent } from '../components/MeshComponent.js';
|
||||
import { Collidable } from '../components/Collidable.js';
|
||||
import { Health } from '../components/Health.js';
|
||||
import { PlayerTag, CoinTag, ObstacleTag, BoundaryConstrained } from '../components/Tags.js';
|
||||
|
||||
/**
|
||||
* EntityFactory - creates pre-configured game entities with appropriate components.
|
||||
* Centralizes entity creation logic for consistency.
|
||||
*
|
||||
* @typedef {import('../ecs/World.js').EntityId} EntityId
|
||||
*/
|
||||
export class EntityFactory {
|
||||
/**
|
||||
* @param {import('../ecs/World.js').World} world - The ECS world
|
||||
* @param {THREE.Scene} scene - The Three.js scene
|
||||
*/
|
||||
constructor(world, scene) {
|
||||
/** @type {import('../ecs/World.js').World} */
|
||||
this.world = world;
|
||||
|
||||
/** @type {THREE.Scene} */
|
||||
this.scene = scene;
|
||||
|
||||
/** @type {number} Size of the game ground/play area */
|
||||
this.groundSize = 30;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the player entity
|
||||
* @returns {EntityId} The player entity ID
|
||||
*/
|
||||
createPlayer() {
|
||||
const entity = this.world.createEntity();
|
||||
|
||||
// Create mesh
|
||||
const geometry = new window.THREE.BoxGeometry(1, 1, 1);
|
||||
const material = new window.THREE.MeshStandardMaterial({
|
||||
color: 0x4169E1,
|
||||
metalness: 0.3,
|
||||
roughness: 0.4
|
||||
});
|
||||
const mesh = new window.THREE.Mesh(geometry, material);
|
||||
mesh.castShadow = true;
|
||||
mesh.receiveShadow = true;
|
||||
this.scene.add(mesh);
|
||||
|
||||
// Add components
|
||||
this.world.addComponent(entity, new Transform(0, 0.5, 0));
|
||||
this.world.addComponent(entity, new Velocity());
|
||||
this.world.addComponent(entity, new MeshComponent(mesh));
|
||||
this.world.addComponent(entity, new Collidable(0, 'player')); // Player center point (original behavior)
|
||||
this.world.addComponent(entity, new Health(100));
|
||||
this.world.addComponent(entity, new PlayerTag());
|
||||
this.world.addComponent(entity, new BoundaryConstrained(this.groundSize));
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a collectible coin entity
|
||||
* @param {number} [index=0] - Unique index for animation offset
|
||||
* @returns {EntityId} The coin entity ID
|
||||
*/
|
||||
createCoin(index = 0) {
|
||||
const entity = this.world.createEntity();
|
||||
|
||||
// Create mesh
|
||||
const geometry = new window.THREE.SphereGeometry(0.3, 16, 16);
|
||||
const material = new window.THREE.MeshStandardMaterial({
|
||||
color: 0xFFD700,
|
||||
metalness: 0.8,
|
||||
roughness: 0.2,
|
||||
emissive: 0xFFD700,
|
||||
emissiveIntensity: 0.3
|
||||
});
|
||||
const mesh = new window.THREE.Mesh(geometry, material);
|
||||
mesh.castShadow = true;
|
||||
mesh.receiveShadow = true;
|
||||
this.scene.add(mesh);
|
||||
|
||||
// Random position
|
||||
const x = (Math.random() - 0.5) * (this.groundSize - 4);
|
||||
const z = (Math.random() - 0.5) * (this.groundSize - 4);
|
||||
|
||||
// Add components
|
||||
this.world.addComponent(entity, new Transform(x, 0.5, z));
|
||||
this.world.addComponent(entity, new MeshComponent(mesh));
|
||||
this.world.addComponent(entity, new Collidable(0.8, 'coin'));
|
||||
this.world.addComponent(entity, new CoinTag(index));
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an obstacle entity
|
||||
* @returns {EntityId} The obstacle entity ID
|
||||
*/
|
||||
createObstacle() {
|
||||
const entity = this.world.createEntity();
|
||||
|
||||
// Create mesh
|
||||
const geometry = new window.THREE.BoxGeometry(1.5, 2, 1.5);
|
||||
const material = new window.THREE.MeshStandardMaterial({
|
||||
color: 0xFF4500,
|
||||
metalness: 0.3,
|
||||
roughness: 0.7
|
||||
});
|
||||
const mesh = new window.THREE.Mesh(geometry, material);
|
||||
mesh.castShadow = true;
|
||||
mesh.receiveShadow = true;
|
||||
this.scene.add(mesh);
|
||||
|
||||
// Random position (away from center)
|
||||
let posX, posZ;
|
||||
do {
|
||||
posX = (Math.random() - 0.5) * (this.groundSize - 4);
|
||||
posZ = (Math.random() - 0.5) * (this.groundSize - 4);
|
||||
} while (Math.abs(posX) < 3 && Math.abs(posZ) < 3);
|
||||
|
||||
// Random velocity
|
||||
const velocity = new Velocity(
|
||||
(Math.random() - 0.5) * 0.05,
|
||||
0,
|
||||
(Math.random() - 0.5) * 0.05
|
||||
);
|
||||
|
||||
// Add components
|
||||
this.world.addComponent(entity, new Transform(posX, 1, posZ));
|
||||
this.world.addComponent(entity, velocity);
|
||||
this.world.addComponent(entity, new MeshComponent(mesh));
|
||||
this.world.addComponent(entity, new Collidable(1.5, 'obstacle'));
|
||||
this.world.addComponent(entity, new ObstacleTag());
|
||||
this.world.addComponent(entity, new BoundaryConstrained(this.groundSize));
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove entity and its mesh from scene
|
||||
* @param {EntityId} entityId - The entity to destroy
|
||||
*/
|
||||
destroyEntity(entityId) {
|
||||
// Remove mesh from scene if it exists
|
||||
const meshComp = this.world.getComponent(entityId, MeshComponent);
|
||||
if (meshComp) {
|
||||
this.scene.remove(meshComp.mesh);
|
||||
meshComp.mesh.geometry.dispose();
|
||||
meshComp.mesh.material.dispose();
|
||||
}
|
||||
|
||||
// Remove entity from world
|
||||
this.world.removeEntity(entityId);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue