tower-defense / src /scene /PathBuilder.js
victor's picture
victor HF Staff
Initial commit
b29710c
import * as THREE from "three";
import {
PATH_POINTS,
ROAD_HALF_WIDTH,
GRID_CELL_SIZE,
} from "../config/gameConfig.js";
export class PathBuilder {
constructor(scene) {
this.scene = scene;
// Clone and snap path points to the grid to ensure alignment
this.pathPoints = PATH_POINTS.map((p) => {
const snapped = p.clone
? p.clone()
: new THREE.Vector3(p.x, p.y ?? 0, p.z);
snapped.x = Math.round(snapped.x / GRID_CELL_SIZE) * GRID_CELL_SIZE;
snapped.z = Math.round(snapped.z / GRID_CELL_SIZE) * GRID_CELL_SIZE;
return snapped;
});
this.roadMeshes = [];
// Materials
this.roadMat = new THREE.MeshStandardMaterial({
color: 0x393c41,
metalness: 0.1,
roughness: 0.9,
});
}
buildPath() {
// Visualize path line
this.createPathLine();
// Build straight road segments only (no bevels or rounded corners)
for (let i = 0; i < this.pathPoints.length - 1; i++) {
this.addSegment(this.pathPoints[i], this.pathPoints[i + 1]);
}
}
createPathLine() {
const pathLineMat = new THREE.LineBasicMaterial({ color: 0xffff00 });
const pathLineGeo = new THREE.BufferGeometry().setFromPoints(
this.pathPoints
);
const pathLine = new THREE.Line(pathLineGeo, pathLineMat);
pathLine.position.y = 0.01;
this.scene.add(pathLine);
}
addSegment(a, b) {
const seg = new THREE.Vector3().subVectors(b, a);
const len = seg.length();
if (len <= 0.0001) return;
const mid = new THREE.Vector3().addVectors(a, b).multiplyScalar(0.5);
const roadGeo = new THREE.BoxGeometry(len, 0.1, ROAD_HALF_WIDTH * 2);
const road = new THREE.Mesh(roadGeo, this.roadMat);
road.castShadow = false;
road.receiveShadow = true;
road.position.set(mid.x, 0.05, mid.z);
const angle = Math.atan2(seg.z, seg.x);
road.rotation.y = -angle;
this.scene.add(road);
this.roadMeshes.push(road);
}
}