feat: Introduce robust diagnostic setup with fixed-size renderer, enhanced logging, system checks, and improved error handling for initial game components.
This commit is contained in:
51
index.html
51
index.html
@@ -1,14 +1,62 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Horror Game</title>
|
<title>Horror Game</title>
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<script>
|
||||||
|
window.onerror = function (msg, url, lineNo, columnNo, error) {
|
||||||
|
const logDiv = document.getElementById('debug-log');
|
||||||
|
if (logDiv) {
|
||||||
|
const err = document.createElement('div');
|
||||||
|
err.style.color = 'red';
|
||||||
|
err.style.fontWeight = 'bold';
|
||||||
|
err.style.background = 'white';
|
||||||
|
err.style.padding = '5px';
|
||||||
|
err.style.margin = '5px 0';
|
||||||
|
err.textContent = `CRITICAL ERROR: ${msg} [Line: ${lineNo}]`;
|
||||||
|
logDiv.appendChild(err);
|
||||||
|
logDiv.scrollTop = logDiv.scrollHeight;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
function checkWebGL() {
|
||||||
|
try {
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
|
||||||
|
if (gl && gl instanceof WebGLRenderingContext) return "WebGL Supported";
|
||||||
|
else return "WebGL NOT Supported";
|
||||||
|
} catch (e) { return "WebGL Error: " + e.message; }
|
||||||
|
}
|
||||||
|
function test2D() {
|
||||||
|
try {
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = 100;
|
||||||
|
canvas.height = 100;
|
||||||
|
canvas.style.position = 'fixed';
|
||||||
|
canvas.style.bottom = '10px';
|
||||||
|
canvas.style.right = '10px';
|
||||||
|
canvas.style.zIndex = '10001';
|
||||||
|
canvas.style.border = '2px solid white';
|
||||||
|
document.body.appendChild(canvas);
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
ctx.fillStyle = 'red';
|
||||||
|
ctx.fillRect(0, 0, 100, 100);
|
||||||
|
window.log("2D Canvas Test: Rendered Red Square");
|
||||||
|
} catch (e) { window.log("2D Canvas Error: " + e.message); }
|
||||||
|
}
|
||||||
|
window.addEventListener('load', test2D);
|
||||||
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="game-container"></div>
|
<div id="game-container"></div>
|
||||||
<div id="debug-log" style="position: absolute; top: 10px; left: 10px; z-index: 999; color: lime; font-family: monospace; pointer-events: none; background: rgba(0,0,0,0.5);"></div>
|
<div id="debug-log"
|
||||||
|
style="position: absolute; top: 10px; left: 10px; z-index: 10000; color: lime; font-family: monospace; pointer-events: none; background: rgba(0,0,0,0.8); max-height: 80%; overflow-y: auto; font-size: 14px; padding: 10px; width: 300px;">
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="ui-layer">
|
<div id="ui-layer">
|
||||||
<div id="start-screen">
|
<div id="start-screen">
|
||||||
@@ -23,4 +71,5 @@
|
|||||||
|
|
||||||
<script type="module" src="/src/main.js"></script>
|
<script type="module" src="/src/main.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
49
src/Game.js
49
src/Game.js
@@ -1,52 +1,71 @@
|
|||||||
import { Graphics } from './Graphics.js';
|
import { Graphics } from './Graphics.js';
|
||||||
import { World } from './World.js';
|
import { World } from './World.js';
|
||||||
import { Player } from './Player.js';
|
import { Player } from './Player.js';
|
||||||
|
import * as THREE from 'three';
|
||||||
|
|
||||||
export class Game {
|
export class Game {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.graphics = new Graphics();
|
window.log('Game constructor start');
|
||||||
this.world = new World(this.graphics.scene);
|
try {
|
||||||
this.player = new Player(this.graphics.camera, this.world.colliders);
|
this.graphics = new Graphics();
|
||||||
|
this.world = new World(this.graphics.scene);
|
||||||
|
this.player = new Player(this.graphics.camera, this.world.colliders);
|
||||||
|
window.log('All components created successfully');
|
||||||
|
} catch (e) {
|
||||||
|
window.log('CRITICAL ERROR during setup: ' + e.message);
|
||||||
|
}
|
||||||
|
|
||||||
this.isRunning = false;
|
this.isRunning = false;
|
||||||
this.lastTime = 0;
|
this.lastTime = 0;
|
||||||
|
|
||||||
this.setupUI();
|
this.setupUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
setupUI() {
|
setupUI() {
|
||||||
const startScreen = document.getElementById('start-screen');
|
const startScreen = document.getElementById('start-screen');
|
||||||
const hud = document.getElementById('hud');
|
const hud = document.getElementById('hud');
|
||||||
|
if (!startScreen) {
|
||||||
|
window.log('ERROR: start-screen not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
startScreen.addEventListener('click', () => {
|
startScreen.addEventListener('click', () => {
|
||||||
this.player.lockControls();
|
window.log('Start screen clicked');
|
||||||
|
if (this.player) {
|
||||||
|
this.player.lockControls();
|
||||||
|
}
|
||||||
startScreen.style.display = 'none';
|
startScreen.style.display = 'none';
|
||||||
hud.style.display = 'block';
|
if (hud) hud.style.display = 'block';
|
||||||
this.isRunning = true;
|
this.isRunning = true;
|
||||||
|
window.log('Game isRunning = true');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
this.graphics.init();
|
window.log('Game.start() begin');
|
||||||
this.world.load();
|
try {
|
||||||
|
if (this.world) this.world.load();
|
||||||
// Add player to scene if needed, but usually player moves camera
|
if (this.player) {
|
||||||
this.graphics.scene.add(this.player.getObject());
|
const playerObj = this.player.getObject();
|
||||||
|
this.graphics.scene.add(playerObj);
|
||||||
|
}
|
||||||
|
window.log('World/Player loading complete');
|
||||||
|
} catch (e) {
|
||||||
|
window.log('ERROR in Game.start(): ' + e.message);
|
||||||
|
}
|
||||||
|
|
||||||
requestAnimationFrame(this.loop.bind(this));
|
requestAnimationFrame(this.loop.bind(this));
|
||||||
|
window.log('Animation loop requested');
|
||||||
}
|
}
|
||||||
|
|
||||||
loop(time) {
|
loop(time) {
|
||||||
requestAnimationFrame(this.loop.bind(this));
|
const dt = this.lastTime === 0 ? 0 : Math.min((time - this.lastTime) / 1000, 0.1);
|
||||||
|
|
||||||
const dt = this.lastTime === 0 ? 0 : Math.min((time - this.lastTime) / 1000, 0.1); // Cap dt
|
|
||||||
this.lastTime = time;
|
this.lastTime = time;
|
||||||
|
|
||||||
if (this.isRunning) {
|
if (this.isRunning) {
|
||||||
this.player.update(dt);
|
this.player.update(dt);
|
||||||
// this.world.update(dt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.graphics.render();
|
this.graphics.render();
|
||||||
|
requestAnimationFrame(this.loop.bind(this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,26 +9,35 @@ export class Graphics {
|
|||||||
|
|
||||||
this.camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 100);
|
this.camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 100);
|
||||||
|
|
||||||
// Real Screen Renderer
|
// Real Screen Renderer - Force 400x300 for diagnostic visibility
|
||||||
|
const w = 400;
|
||||||
|
const h = 300;
|
||||||
this.renderer = new THREE.WebGLRenderer({ antialias: false });
|
this.renderer = new THREE.WebGLRenderer({ antialias: false });
|
||||||
this.renderer.setSize(window.innerWidth, window.innerHeight);
|
this.renderer.setSize(w, h);
|
||||||
this.renderer.setClearColor(0xff00ff); // STRIKING MAGENTA for diagnostic
|
this.renderer.setClearColor(0xff00ff);
|
||||||
this.renderer.domElement.style.border = '5px solid yellow'; // VISIBLE BORDER
|
this.renderer.domElement.style.position = 'fixed';
|
||||||
this.renderer.domElement.style.zIndex = '100'; // FORCE TO FRONT
|
this.renderer.domElement.style.top = '50%';
|
||||||
|
this.renderer.domElement.style.left = '50%';
|
||||||
|
this.renderer.domElement.style.transform = 'translate(-50%, -50%)';
|
||||||
|
this.renderer.domElement.style.border = '5px solid yellow';
|
||||||
|
this.renderer.domElement.style.zIndex = '1000';
|
||||||
this.renderer.domElement.id = 'three-canvas';
|
this.renderer.domElement.id = 'three-canvas';
|
||||||
|
|
||||||
const container = document.getElementById('game-container');
|
document.body.appendChild(this.renderer.domElement);
|
||||||
if (container) {
|
window.log(`GL Vendor: ${this.renderer.getContext().getParameter(this.renderer.getContext().VENDOR)}`);
|
||||||
container.appendChild(this.renderer.domElement);
|
window.log(`Canvas forced to ${w}x${h} (centered)`);
|
||||||
window.log('Renderer attached to DOM');
|
|
||||||
} else {
|
|
||||||
window.log('ERROR: game-container not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
this.scene.add(new THREE.AxesHelper(10)); // Add axis lines (Red, Green, Blue)
|
this.camera.position.set(5, 5, 5);
|
||||||
this.camera.position.set(5, 5, 5); // Move camera out of the floor
|
|
||||||
this.camera.lookAt(0, 0, 0);
|
this.camera.lookAt(0, 0, 0);
|
||||||
|
|
||||||
|
// Add a guaranteed object
|
||||||
|
const testCube = new THREE.Mesh(
|
||||||
|
new THREE.BoxGeometry(2, 2, 2),
|
||||||
|
new THREE.MeshBasicMaterial({ color: 0x00ff00 })
|
||||||
|
);
|
||||||
|
this.scene.add(testCube);
|
||||||
|
window.log('Green Cube added to scene');
|
||||||
|
|
||||||
// --- Retro Pipeline Setup ---
|
// --- Retro Pipeline Setup ---
|
||||||
|
|
||||||
// 1. Off-screen Render Target (Small Resolution)
|
// 1. Off-screen Render Target (Small Resolution)
|
||||||
@@ -106,11 +115,15 @@ export class Graphics {
|
|||||||
render() {
|
render() {
|
||||||
if (!this.renderCount) this.renderCount = 0;
|
if (!this.renderCount) this.renderCount = 0;
|
||||||
this.renderCount++;
|
this.renderCount++;
|
||||||
if (this.renderCount % 100 === 0) window.log(`Rendering frame ${this.renderCount}`);
|
|
||||||
|
|
||||||
// --- Diagnostic: Render directly to screen ---
|
if (this.renderCount % 500 === 0) {
|
||||||
// 1. Render 3D Scene to Screen
|
window.log(`F:${this.renderCount} | Scene: ${this.scene.children.length} obj | Cam: ${this.camera.position.x.toFixed(1)},${this.camera.position.y.toFixed(1)},${this.camera.position.z.toFixed(1)}`);
|
||||||
this.renderer.setRenderTarget(null);
|
}
|
||||||
|
|
||||||
|
this.renderer.setClearColor(0xff00ff);
|
||||||
|
this.renderer.clear();
|
||||||
|
|
||||||
|
// 1. Render 3D Scene directly
|
||||||
this.renderer.render(this.scene, this.camera);
|
this.renderer.render(this.scene, this.camera);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
import { PointerLockControls } from 'three/examples/jsm/controls/PointerLockControls.js';
|
import { PointerLockControls } from 'three/examples/jsm/controls/PointerLockControls.js';
|
||||||
|
// Note: In some environments this might need to be 'three/addons/controls/PointerLockControls.js'
|
||||||
|
// but we'll stick to this for now as it's standard examples path.
|
||||||
|
|
||||||
export class Player {
|
export class Player {
|
||||||
constructor(camera, colliders) {
|
constructor(camera, colliders) {
|
||||||
@@ -11,7 +13,12 @@ export class Player {
|
|||||||
this.height = 1.7; // Eyes height
|
this.height = 1.7; // Eyes height
|
||||||
|
|
||||||
// Init controls
|
// Init controls
|
||||||
this.controls = new PointerLockControls(camera, document.body);
|
try {
|
||||||
|
this.controls = new PointerLockControls(camera, document.body);
|
||||||
|
window.log('PointerLockControls initialized');
|
||||||
|
} catch (e) {
|
||||||
|
window.log(`ERROR initializing controls: ${e.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
// Movement state
|
// Movement state
|
||||||
this.moveForward = false;
|
this.moveForward = false;
|
||||||
@@ -64,7 +71,7 @@ export class Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
toggleFlashlight() {
|
toggleFlashlight() {
|
||||||
if (!this.controls.isLocked) return; // Only toggle when game is active
|
if (!this.controls || !this.controls.isLocked) return; // Only toggle when game is active
|
||||||
this.flashlightOn = !this.flashlightOn;
|
this.flashlightOn = !this.flashlightOn;
|
||||||
if (this.flashlight) {
|
if (this.flashlight) {
|
||||||
this.flashlight.visible = this.flashlightOn;
|
this.flashlight.visible = this.flashlightOn;
|
||||||
@@ -72,15 +79,16 @@ export class Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
lockControls() {
|
lockControls() {
|
||||||
this.controls.lock();
|
if (this.controls) this.controls.lock();
|
||||||
|
else window.log('WARNING: Controls not initialized for locking');
|
||||||
}
|
}
|
||||||
|
|
||||||
getObject() {
|
getObject() {
|
||||||
return this.controls.getObject();
|
return this.controls ? this.controls.getObject() : new THREE.Group();
|
||||||
}
|
}
|
||||||
|
|
||||||
update(dt) {
|
update(dt) {
|
||||||
if (!this.controls.isLocked) return;
|
if (!this.controls || !this.controls.isLocked) return;
|
||||||
|
|
||||||
// Friction-like dampening
|
// Friction-like dampening
|
||||||
// Simple direct velocity for now
|
// Simple direct velocity for now
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export class World {
|
|||||||
}
|
}
|
||||||
|
|
||||||
load() {
|
load() {
|
||||||
|
window.log('World.load() start');
|
||||||
// Grid helper for depth
|
// Grid helper for depth
|
||||||
this.scene.add(new THREE.GridHelper(20, 20));
|
this.scene.add(new THREE.GridHelper(20, 20));
|
||||||
|
|
||||||
@@ -37,6 +38,7 @@ export class World {
|
|||||||
}
|
}
|
||||||
|
|
||||||
createWall(x, y, z, width, height, rotate = false) {
|
createWall(x, y, z, width, height, rotate = false) {
|
||||||
|
window.log(`Creating wall at ${x},${z}`);
|
||||||
const geo = new THREE.BoxGeometry(width, height, 0.5);
|
const geo = new THREE.BoxGeometry(width, height, 0.5);
|
||||||
const mat = new THREE.MeshBasicMaterial({ color: 0x555555 });
|
const mat = new THREE.MeshBasicMaterial({ color: 0x555555 });
|
||||||
const wall = new THREE.Mesh(geo, mat);
|
const wall = new THREE.Mesh(geo, mat);
|
||||||
|
|||||||
21
src/main.js
21
src/main.js
@@ -1,9 +1,28 @@
|
|||||||
window.log = (msg) => {
|
window.log = (msg) => {
|
||||||
const logDiv = document.getElementById('debug-log');
|
const logDiv = document.getElementById('debug-log');
|
||||||
if (logDiv) logDiv.innerHTML += `> ${msg}<br>`;
|
if (logDiv) {
|
||||||
|
const span = document.createElement('div');
|
||||||
|
span.textContent = `> ${msg}`;
|
||||||
|
logDiv.appendChild(span);
|
||||||
|
logDiv.scrollTop = logDiv.scrollHeight;
|
||||||
|
}
|
||||||
console.log(msg);
|
console.log(msg);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Toggle debug log with 'P'
|
||||||
|
window.addEventListener('keydown', (e) => {
|
||||||
|
if (e.code === 'KeyP') {
|
||||||
|
const logDiv = document.getElementById('debug-log');
|
||||||
|
if (logDiv) {
|
||||||
|
logDiv.style.display = logDiv.style.display === 'none' ? 'block' : 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
import * as THREE from 'three';
|
||||||
|
window.log(`THREE Revision: ${THREE.REVISION}`);
|
||||||
|
if (window.checkWebGL) window.log(`System Check: ${window.checkWebGL()}`);
|
||||||
|
|
||||||
import { Game } from './Game.js';
|
import { Game } from './Game.js';
|
||||||
|
|
||||||
window.addEventListener('DOMContentLoaded', () => {
|
window.addEventListener('DOMContentLoaded', () => {
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ import { defineConfig } from 'vite';
|
|||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
server: {
|
server: {
|
||||||
host: '0.0.0.0',
|
|
||||||
port: 5173,
|
port: 5173,
|
||||||
},
|
strictPort: true,
|
||||||
|
host: true
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user