Spaces:
Running
Running
import * as THREE from "three"; | |
import { PATH_POINTS, GRID_CELL_SIZE } from "../config/gameConfig.js"; | |
// Snap value to grid (to nearest grid line) | |
export function snapToGrid(value, size = GRID_CELL_SIZE) { | |
return Math.round(value / size) * size; | |
} | |
// Grid helpers to work with cell centers | |
export function worldToCell(x, z, size = GRID_CELL_SIZE) { | |
// Map world coords to integer col/row indices | |
const col = Math.floor(x / size); | |
const row = Math.floor(z / size); | |
return { col, row }; | |
} | |
export function cellToWorldCenter(col, row, size = GRID_CELL_SIZE) { | |
// Center of the tile: (col + 0.5, row + 0.5) * size | |
return new THREE.Vector3((col + 0.5) * size, 0, (row + 0.5) * size); | |
} | |
// Check if point is on road | |
export function isOnRoad(p, pathPoints = PATH_POINTS) { | |
const halfWidth = 1.6; | |
for (let i = 0; i < pathPoints.length - 1; i++) { | |
const a = pathPoints[i]; | |
const b = pathPoints[i + 1]; | |
if (pointSegmentDistance2D(p, a, b) <= halfWidth) return true; | |
} | |
return false; | |
} | |
// Calculate distance from point to line segment in 2D | |
export function pointSegmentDistance2D(p, a, b) { | |
const apx = p.x - a.x, | |
apz = p.z - a.z; | |
const abx = b.x - a.x, | |
abz = b.z - a.z; | |
const abLenSq = abx * abx + abz * abz; | |
let t = 0; | |
if (abLenSq > 0) t = (apx * abx + apz * abz) / abLenSq; | |
t = Math.max(0, Math.min(1, t)); | |
const cx = a.x + t * abx, | |
cz = a.z + t * abz; | |
const dx = p.x - cx, | |
dz = p.z - cz; | |
return Math.hypot(dx, dz); | |
} | |
// Simple particle/hit effect system | |
export class EffectSystem { | |
constructor(scene) { | |
this.scene = scene; | |
this.effects = []; | |
} | |
spawnHitEffect(pos) { | |
const geo = new THREE.SphereGeometry(0.2, 6, 6); | |
const mat = new THREE.MeshBasicMaterial({ | |
color: 0xfff176, | |
transparent: true, | |
opacity: 0.9, | |
}); | |
const m = new THREE.Mesh(geo, mat); | |
m.position.copy(pos); | |
this.scene.add(m); | |
this.effects.push({ mesh: m, life: 0.25 }); | |
} | |
update(dt) { | |
for (let i = this.effects.length - 1; i >= 0; i--) { | |
const e = this.effects[i]; | |
e.life -= dt; | |
e.mesh.scale.addScalar(6 * dt); | |
e.mesh.material.opacity = Math.max(0, e.life / 0.25); | |
if (e.life <= 0) { | |
this.scene.remove(e.mesh); | |
this.effects.splice(i, 1); | |
} | |
} | |
} | |
} | |