fix(babylon_setup.js): fix exit not generating properly

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
pobadoba
2026-05-06 01:47:27 +09:00
parent 4bb46115ca
commit 176ad34214

View File

@@ -126,6 +126,7 @@ let chestMap = new Map(); // key: "x,y" -> {mesh, opened}
let keyChestKey = null; let keyChestKey = null;
let exitBox = null; let exitBox = null;
let exitGridPos = null; // track exit grid position for collision checking let exitGridPos = null; // track exit grid position for collision checking
let spawnGridPos = null; // track spawn grid position for validation
let spawnMarker = null; let spawnMarker = null;
const cellSize = 2; const cellSize = 2;
@@ -139,6 +140,7 @@ function clearLevelMeshes() {
if (exitBox) { try { exitBox.dispose(); } catch(e){}; exitBox = null; } if (exitBox) { try { exitBox.dispose(); } catch(e){}; exitBox = null; }
if (spawnMarker) { try { spawnMarker.dispose(); } catch(e){}; spawnMarker = null; } if (spawnMarker) { try { spawnMarker.dispose(); } catch(e){}; spawnMarker = null; }
exitGridPos = null; exitGridPos = null;
spawnGridPos = null;
} }
// Simple seeded RNG // Simple seeded RNG
@@ -209,6 +211,26 @@ function findDeadEnds(grid) {
return dead; return dead;
} }
function gridCellToWorld(grid, x, y) {
const halfW = (grid[0].length * cellSize) / 2;
const halfH = (grid.length * cellSize) / 2;
return new BABYLON.Vector3(
x * cellSize - halfW + cellSize / 2,
0,
y * cellSize - halfH + cellSize / 2,
);
}
function isWalkableCell(grid, x, y) {
return y >= 0 && y < grid.length && x >= 0 && x < grid[0].length && grid[y][x] === 0;
}
function isReservedCell(x, y) {
if (chestMap.has(`${x},${y}`)) return true;
if (exitGridPos && exitGridPos.x === x && exitGridPos.y === y) return true;
return false;
}
function buildLevelFromGrid(grid) { function buildLevelFromGrid(grid) {
clearLevelMeshes(); clearLevelMeshes();
const h = grid.length; const h = grid.length;
@@ -295,71 +317,96 @@ function placeExit(grid, seed) {
exitGridPos = { x, y }; exitGridPos = { x, y };
} }
const [x,y] = exitGridPos; const [x,y] = [exitGridPos.x, exitGridPos.y];
const halfW = (grid[0].length * cellSize) / 2; if (!isWalkableCell(grid, x, y)) {
const halfH = (grid.length * cellSize) / 2; console.warn("Exit selected on non-walkable cell", { x, y });
const ex = x*cellSize - halfW + cellSize/2; return;
const ez = y*cellSize - halfH + cellSize/2; }
const exitWorld = gridCellToWorld(grid, x, y);
const ex = exitWorld.x;
const ez = exitWorld.z;
const worldSpan = Math.max(grid[0].length, grid.length) * cellSize;
// Ground plane indicator // Ground plane indicator (larger + slightly raised to avoid z-fighting)
const plane = BABYLON.MeshBuilder.CreateGround('exitZone', { width: cellSize*0.9, height: cellSize*0.9 }, scene); const plane = BABYLON.MeshBuilder.CreateGround('exitZone', { width: cellSize*1.4, height: cellSize*1.4 }, scene);
plane.material = new BABYLON.StandardMaterial('exitMat', scene); const exitMat = new BABYLON.StandardMaterial('exitMat', scene);
plane.material.emissiveColor = new BABYLON.Color3(0.9, 0.8, 0.2); exitMat.diffuseColor = new BABYLON.Color3(0.25, 0.2, 0.03);
plane.position = new BABYLON.Vector3(ex, 0.01, ez); exitMat.emissiveColor = new BABYLON.Color3(1.0, 0.85, 0.15);
exitMat.disableLighting = true;
plane.material = exitMat;
plane.position = new BABYLON.Vector3(ex, 0.08, ez);
exitBox = plane; exitBox = plane;
levelMeshes.push(plane); levelMeshes.push(plane);
// Tall pillar for visibility // Very tall beacon for visibility over maze walls
const pillar = BABYLON.MeshBuilder.CreateCylinder('exitPillar', { diameter: cellSize*0.3, height: cellSize*3.0 }, scene); const beaconHeight = Math.max(18, worldSpan * 0.8);
const pillar = BABYLON.MeshBuilder.CreateCylinder('exitPillar', { diameter: cellSize*0.55, height: beaconHeight }, scene);
const pillarMat = new BABYLON.StandardMaterial('exitPillarMat', scene); const pillarMat = new BABYLON.StandardMaterial('exitPillarMat', scene);
pillarMat.diffuseColor = new BABYLON.Color3(0.95, 0.8, 0.1); pillarMat.diffuseColor = new BABYLON.Color3(0.95, 0.8, 0.1);
pillarMat.emissiveColor = new BABYLON.Color3(0.3, 0.25, 0.05); pillarMat.emissiveColor = new BABYLON.Color3(1.0, 0.7, 0.1);
pillarMat.disableLighting = true;
pillar.material = pillarMat; pillar.material = pillarMat;
pillar.position = new BABYLON.Vector3(ex, cellSize*0.75, ez); pillar.position = new BABYLON.Vector3(ex, beaconHeight * 0.5, ez);
levelMeshes.push(pillar); levelMeshes.push(pillar);
// Bright orb at the top of beacon
const orb = BABYLON.MeshBuilder.CreateSphere('exitOrb', { diameter: cellSize * 1.0 }, scene);
const orbMat = new BABYLON.StandardMaterial('exitOrbMat', scene);
orbMat.diffuseColor = new BABYLON.Color3(1.0, 0.9, 0.3);
orbMat.emissiveColor = new BABYLON.Color3(1.0, 0.9, 0.35);
orbMat.disableLighting = true;
orb.material = orbMat;
orb.position = new BABYLON.Vector3(ex, beaconHeight + cellSize * 0.7, ez);
levelMeshes.push(orb);
} }
function spawnCameraAt(grid) { function spawnCameraAt(grid) {
const h = grid.length, w = grid[0].length; const h = grid.length;
for (let y = 1; y < h-1; y++) { const w = grid[0].length;
for (let x = 1; x < w-1; x++) { let bestCell = null;
// Skip walls let bestDist = -1;
if (grid[y][x] !== 0) continue;
// Choose a valid spawn that is far from exit when possible.
// Skip chests for (let y = 1; y < h - 1; y++) {
if (chestMap.has(`${x},${y}`)) continue; for (let x = 1; x < w - 1; x++) {
if (!isWalkableCell(grid, x, y)) continue;
// Skip exit if (isReservedCell(x, y)) continue;
if (exitGridPos && exitGridPos.x === x && exitGridPos.y === y) continue;
const d = exitGridPos ? Math.hypot(x - exitGridPos.x, y - exitGridPos.y) : 0;
// Found valid spawn point if (d > bestDist) {
const halfW = (w * cellSize) / 2; bestDist = d;
const halfH = (h * cellSize) / 2; bestCell = { x, y };
const px = x*cellSize - halfW + cellSize/2;
const pz = y*cellSize - halfH + cellSize/2;
try {
if (camera && camera.position) {
camera.position = new BABYLON.Vector3(px, 1.6, pz);
}
} catch (e) {}
// Add spawn marker sphere
if (spawnMarker) {
try { spawnMarker.dispose(); } catch(e) {}
} }
const marker = BABYLON.MeshBuilder.CreateSphere('spawnMarker', { diameter: cellSize*0.4 }, scene);
const markerMat = new BABYLON.StandardMaterial('spawnMarkerMat', scene);
markerMat.diffuseColor = new BABYLON.Color3(0.2, 0.6, 0.95);
markerMat.emissiveColor = new BABYLON.Color3(0.1, 0.3, 0.5);
marker.material = markerMat;
marker.position = new BABYLON.Vector3(px, cellSize*0.2, pz);
spawnMarker = marker;
levelMeshes.push(marker);
return;
} }
} }
if (!bestCell) {
console.warn("No valid spawn cell found.");
return;
}
spawnGridPos = bestCell;
const spawnWorld = gridCellToWorld(grid, bestCell.x, bestCell.y);
const px = spawnWorld.x;
const pz = spawnWorld.z;
try {
if (camera && camera.position) {
camera.position = new BABYLON.Vector3(px, 1.6, pz);
}
} catch (e) {}
if (spawnMarker) {
try { spawnMarker.dispose(); } catch(e) {}
}
const marker = BABYLON.MeshBuilder.CreateSphere('spawnMarker', { diameter: cellSize*0.4 }, scene);
const markerMat = new BABYLON.StandardMaterial('spawnMarkerMat', scene);
markerMat.diffuseColor = new BABYLON.Color3(0.2, 0.6, 0.95);
markerMat.emissiveColor = new BABYLON.Color3(0.1, 0.3, 0.5);
marker.material = markerMat;
marker.position = new BABYLON.Vector3(px, cellSize*0.2, pz);
spawnMarker = marker;
levelMeshes.push(marker);
} }
function generateLevel() { function generateLevel() {
@@ -374,7 +421,20 @@ function generateLevel() {
placeChestsOnDeadEnds(grid, dead, cfg.minChestDeadEnds, seed + cfg.level); placeChestsOnDeadEnds(grid, dead, cfg.minChestDeadEnds, seed + cfg.level);
placeExit(grid, seed + cfg.level); placeExit(grid, seed + cfg.level);
spawnCameraAt(grid); spawnCameraAt(grid);
sharedState.runtime.message = `Level ${cfg.level} generated.`;
const placementValid =
!!exitGridPos &&
!!spawnGridPos &&
isWalkableCell(grid, exitGridPos.x, exitGridPos.y) &&
isWalkableCell(grid, spawnGridPos.x, spawnGridPos.y) &&
!(exitGridPos.x === spawnGridPos.x && exitGridPos.y === spawnGridPos.y);
if (!placementValid) {
sharedState.runtime.message = `Placement warning: spawn/exit invalid on level ${cfg.level}.`;
console.warn("Invalid spawn/exit placement", { exitGridPos, spawnGridPos });
} else {
sharedState.runtime.message = `Level ${cfg.level} generated (spawn ${spawnGridPos.x},${spawnGridPos.y} / exit ${exitGridPos.x},${exitGridPos.y}).`;
}
window.requestAnimationFrame(()=>{ /* let scene update */ }); window.requestAnimationFrame(()=>{ /* let scene update */ });
} }