Refactor GameLoop and MovementSystem to improve unit positioning and enemy spawning logic. Adjust unit height calculations for better alignment with floor surfaces. Enhance movement highlights with multiple glow layers for improved visibility. Update reachable position checks to utilize accurate walkable heights.

This commit is contained in:
Matthew Mone 2025-12-23 20:28:12 -08:00
parent ea4e327585
commit 525a92a2eb
2 changed files with 117 additions and 22 deletions

View file

@ -334,9 +334,10 @@ export class GameLoop {
// Update unit mesh position
const mesh = this.unitMeshes.get(activeUnit.id);
if (mesh) {
// Floor surface is at pos.y - 0.5, unit should be 0.6 above: pos.y + 0.1
mesh.position.set(
activeUnit.position.x,
activeUnit.position.y + 0.6,
activeUnit.position.y + 0.1,
activeUnit.position.z
);
}
@ -513,7 +514,8 @@ export class GameLoop {
// Update Mesh
const mesh = this.unitMeshes.get(existingUnit.id);
if (mesh) {
mesh.position.set(targetTile.x, targetTile.y + 0.6, targetTile.z);
// Floor surface is at pos.y - 0.5, unit should be 0.6 above: pos.y + 0.1
mesh.position.set(targetTile.x, targetTile.y + 0.1, targetTile.z);
}
console.log(
`Moved ${existingUnit.name} to ${targetTile.x},${targetTile.y},${targetTile.z}`
@ -554,14 +556,36 @@ export class GameLoop {
)
return;
const enemyCount = 2;
for (let i = 0; i < enemyCount; i++) {
let attempts = 0;
const maxAttempts = this.enemySpawnZone.length * 2; // Try up to 2x the zone size
for (let i = 0; i < enemyCount && attempts < maxAttempts; attempts++) {
const spotIndex = Math.floor(Math.random() * this.enemySpawnZone.length);
const spot = this.enemySpawnZone[spotIndex];
if (spot && !this.grid.isOccupied(spot)) {
if (!spot) continue;
// Check if position is walkable (not just unoccupied)
// Find the correct walkable Y for this position
const walkableY = this.movementSystem?.findWalkableY(
spot.x,
spot.z,
spot.y
);
if (walkableY === null) continue;
const walkablePos = { x: spot.x, y: walkableY, z: spot.z };
// Check if position is not occupied and is walkable (not solid)
if (
!this.grid.isOccupied(walkablePos) &&
!this.grid.isSolid(walkablePos)
) {
const enemy = this.unitManager.createUnit("ENEMY_DEFAULT", "ENEMY");
this.grid.placeUnit(enemy, spot);
this.createUnitMesh(enemy, spot);
this.grid.placeUnit(enemy, walkablePos);
this.createUnitMesh(enemy, walkablePos);
this.enemySpawnZone.splice(spotIndex, 1);
i++; // Only increment if we successfully placed an enemy
}
}
@ -651,24 +675,92 @@ export class GameLoop {
const reachablePositions =
this.movementSystem.getReachableTiles(activeUnit);
// Create blue highlight material
const highlightMaterial = new THREE.MeshBasicMaterial({
color: 0x0066ff, // Blue color
// Create glowing blue outline materials with multiple layers for enhanced glow
// Outer glow layers (fade outward, decreasing opacity)
const outerGlowMaterial = new THREE.LineBasicMaterial({
color: 0x0066ff,
transparent: true,
opacity: 0.4,
opacity: 0.3,
});
// Create geometry for highlights (plane on the ground)
const geometry = new THREE.PlaneGeometry(1, 1);
geometry.rotateX(-Math.PI / 2);
const midGlowMaterial = new THREE.LineBasicMaterial({
color: 0x0088ff,
transparent: true,
opacity: 0.5,
});
// Create highlight meshes for each reachable position
// Inner bright outline (main glow - brightest)
const highlightMaterial = new THREE.LineBasicMaterial({
color: 0x00ccff, // Very bright cyan-blue for maximum visibility
transparent: true,
opacity: 1.0,
});
// Thick inner outline (for thickness simulation)
const thickMaterial = new THREE.LineBasicMaterial({
color: 0x00aaff,
transparent: true,
opacity: 0.8,
});
// Create base plane geometry for the tile
const baseGeometry = new THREE.PlaneGeometry(1, 1);
baseGeometry.rotateX(-Math.PI / 2);
// Create highlight outlines for each reachable position
reachablePositions.forEach((pos) => {
const mesh = new THREE.Mesh(geometry, highlightMaterial);
// Position just above floor surface (pos.y is the air space, floor surface is at pos.y)
mesh.position.set(pos.x, pos.y + 0.01, pos.z);
this.scene.add(mesh);
this.movementHighlights.add(mesh);
// Get the correct floor surface height for this position
const walkableY = this.movementSystem.findWalkableY(pos.x, pos.z, pos.y);
if (walkableY === null) return; // Skip if no valid floor found
// Floor surface is at the walkable Y coordinate (top of the floor block)
// Adjust by -0.5 to account for voxel centering
const floorSurfaceY = walkableY - 0.5;
// Create multiple glow layers for enhanced visibility and fade effect
// Outer glow (largest, most transparent)
const outerGlowGeometry = new THREE.PlaneGeometry(1.15, 1.15);
outerGlowGeometry.rotateX(-Math.PI / 2);
const outerGlowEdges = new THREE.EdgesGeometry(outerGlowGeometry);
const outerGlowLines = new THREE.LineSegments(
outerGlowEdges,
outerGlowMaterial
);
outerGlowLines.position.set(pos.x, floorSurfaceY + 0.003, pos.z);
this.scene.add(outerGlowLines);
this.movementHighlights.add(outerGlowLines);
// Mid glow (medium size)
const midGlowGeometry = new THREE.PlaneGeometry(1.08, 1.08);
midGlowGeometry.rotateX(-Math.PI / 2);
const midGlowEdges = new THREE.EdgesGeometry(midGlowGeometry);
const midGlowLines = new THREE.LineSegments(
midGlowEdges,
midGlowMaterial
);
midGlowLines.position.set(pos.x, floorSurfaceY + 0.002, pos.z);
this.scene.add(midGlowLines);
this.movementHighlights.add(midGlowLines);
// Thick inner outline (slightly larger than base for thickness)
const thickGeometry = new THREE.PlaneGeometry(1.02, 1.02);
thickGeometry.rotateX(-Math.PI / 2);
const thickEdges = new THREE.EdgesGeometry(thickGeometry);
const thickLines = new THREE.LineSegments(thickEdges, thickMaterial);
thickLines.position.set(pos.x, floorSurfaceY + 0.001, pos.z);
this.scene.add(thickLines);
this.movementHighlights.add(thickLines);
// Main bright outline (exact size, brightest)
const edgesGeometry = new THREE.EdgesGeometry(baseGeometry);
const lineSegments = new THREE.LineSegments(
edgesGeometry,
highlightMaterial
);
// Position exactly on floor surface
lineSegments.position.set(pos.x, floorSurfaceY, pos.z);
this.scene.add(lineSegments);
this.movementHighlights.add(lineSegments);
});
}
@ -684,7 +776,9 @@ export class GameLoop {
else if (unit.team === "ENEMY") color = 0x550000;
const material = new THREE.MeshStandardMaterial({ color: color });
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set(pos.x, pos.y + 0.6, pos.z);
// Floor surface is at pos.y - 0.5 (floor block at pos.y-1, top at pos.y-0.5)
// Unit should be 0.6 units above floor surface: (pos.y - 0.5) + 0.6 = pos.y + 0.1
mesh.position.set(pos.x, pos.y + 0.1, pos.z);
this.scene.add(mesh);
this.unitMeshes.set(unit.id, mesh);
}

View file

@ -106,11 +106,12 @@ export class MovementSystem {
if (walkableY === null) continue;
const pos = { x, y: walkableY, z };
const posKey = `${x},${y},${z}`;
// Use walkableY in the key, not the reference y
const posKey = `${x},${walkableY},${z}`;
// Check if position is not occupied (or is the starting position)
// Starting position is always reachable (unit is already there)
const isStartPos = x === start.x && z === start.z && y === start.y;
const isStartPos = x === start.x && z === start.z && walkableY === start.y;
if (!this.grid.isOccupied(pos) || isStartPos) {
reachable.add(posKey);
}