Обновить doom.html

This commit is contained in:
2026-06-15 18:44:41 +03:00
parent 0bbb184759
commit 9847ac5fc9
+96 -47
View File
@@ -3,7 +3,7 @@
<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>DOOM ETERNAL: Web 3D (Real Textures)</title> <title>DOOM ETERNAL: Web 3D (Fixed)</title>
<style> <style>
body { margin: 0; overflow: hidden; background: #000; font-family: 'Impact', 'Arial Black', sans-serif; user-select: none; } body { margin: 0; overflow: hidden; background: #000; font-family: 'Impact', 'Arial Black', sans-serif; user-select: none; }
#game-container { position: relative; width: 100vw; height: 100vh; } #game-container { position: relative; width: 100vw; height: 100vh; }
@@ -19,12 +19,12 @@
#crosshair::after { top: 0; left: 11px; width: 2px; height: 24px; } #crosshair::after { top: 0; left: 11px; width: 2px; height: 24px; }
#blocker { #blocker {
position: absolute; width: 100%; height: 100%; background: rgba(0,0,0,0.9); position: absolute; width: 100%; height: 100%; background: rgba(0,0,0,0.85);
display: flex; flex-direction: column; align-items: center; justify-content: center; display: flex; flex-direction: column; align-items: center; justify-content: center;
z-index: 20; color: #fff; cursor: pointer; z-index: 20; color: #fff; cursor: pointer;
} }
#instructions { font-size: 42px; text-transform: uppercase; letter-spacing: 6px; color: #ff3333; text-shadow: 0 0 20px #ff0000; text-align: center; } #instructions { font-size: 42px; text-transform: uppercase; letter-spacing: 6px; color: #ff3333; text-shadow: 0 0 20px #ff0000; text-align: center; }
#sub-instructions { font-size: 16px; color: #888; margin-top: 20px; font-family: monospace; letter-spacing: 1px; } #sub-instructions { font-size: 16px; color: #ccc; margin-top: 20px; font-family: monospace; letter-spacing: 1px; }
#loading { font-size: 18px; color: #ffaa00; margin-top: 30px; display: none; } #loading { font-size: 18px; color: #ffaa00; margin-top: 30px; display: none; }
#hud { #hud {
@@ -34,7 +34,7 @@
align-items: center; padding: 0 50px; box-sizing: border-box; pointer-events: none; z-index: 10; align-items: center; padding: 0 50px; box-sizing: border-box; pointer-events: none; z-index: 10;
} }
.stat-box { text-align: center; color: #fff; position: relative; } .stat-box { text-align: center; color: #fff; position: relative; }
.stat-label { font-size: 14px; color: #666; letter-spacing: 3px; text-transform: uppercase; font-family: monospace; } .stat-label { font-size: 14px; color: #888; letter-spacing: 3px; text-transform: uppercase; font-family: monospace; }
.stat-value { font-size: 56px; line-height: 56px; text-shadow: 0 0 15px currentColor; font-family: 'Impact', sans-serif; } .stat-value { font-size: 56px; line-height: 56px; text-shadow: 0 0 15px currentColor; font-family: 'Impact', sans-serif; }
.health { color: #00ff00; } .health { color: #00ff00; }
.armor { color: #00bfff; } .armor { color: #00bfff; }
@@ -108,7 +108,7 @@
let pickups = []; let pickups = [];
let weaponGroup, pumpGroup; let weaponGroup, pumpGroup;
let isPumping = false; let isPumping = false;
let muzzleLight; let muzzleLight, playerLight;
let audioCtx; let audioCtx;
const mapLayout = [ const mapLayout = [
@@ -133,6 +133,7 @@
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
]; ];
const CELL_SIZE = 4; const CELL_SIZE = 4;
const PLAYER_RADIUS = 0.6; // Радиус игрока для коллизий
const uiHealth = document.getElementById('health-val'); const uiHealth = document.getElementById('health-val');
const uiArmor = document.getElementById('armor-val'); const uiArmor = document.getElementById('armor-val');
@@ -213,14 +214,31 @@
function init() { function init() {
scene = new THREE.Scene(); scene = new THREE.Scene();
scene.background = new THREE.Color(0x050000); scene.background = new THREE.Color(0x1a0a0a); // Светлее фон
scene.fog = new THREE.FogExp2(0x1a0505, 0.04);
// УМЕНЬШЕНА плотность тумана и сделан светлее для лучшей видимости
scene.fog = new THREE.FogExp2(0x2a0a0a, 0.02);
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 100); camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 100);
const ambientLight = new THREE.AmbientLight(0x220505, 0.6); // --- УЛУЧШЕННОЕ ОСВЕЩЕНИЕ ---
// 1. Яркий общий теплый свет
const ambientLight = new THREE.AmbientLight(0xffaaaa, 1.0);
scene.add(ambientLight); scene.add(ambientLight);
// 2. Направленный свет сверху (имитация адского светила)
const dirLight = new THREE.DirectionalLight(0xffccaa, 1.2);
dirLight.position.set(30, 50, 30);
dirLight.castShadow = true;
dirLight.shadow.mapSize.width = 1024;
dirLight.shadow.mapSize.height = 1024;
scene.add(dirLight);
// 3. Свет от игрока (всегда освещает пространство перед ним)
playerLight = new THREE.PointLight(0xffaa55, 1.5, 18);
playerLight.position.set(0, 0, -1);
camera.add(playerLight);
muzzleLight = new THREE.PointLight(0xffaa00, 0, 15); muzzleLight = new THREE.PointLight(0xffaa00, 0, 15);
muzzleLight.castShadow = true; muzzleLight.castShadow = true;
scene.add(muzzleLight); scene.add(muzzleLight);
@@ -229,7 +247,6 @@
loadingText.style.display = 'block'; loadingText.style.display = 'block';
const textureLoader = new THREE.TextureLoader(); const textureLoader = new THREE.TextureLoader();
// Используем надежные URL с официального репозитория Three.js (CORS-friendly)
const wallUrl = 'https://threejs.org/examples/textures/brick_diffuse.jpg'; const wallUrl = 'https://threejs.org/examples/textures/brick_diffuse.jpg';
const floorUrl = 'https://threejs.org/examples/textures/metal.jpg'; const floorUrl = 'https://threejs.org/examples/textures/metal.jpg';
@@ -241,23 +258,22 @@
floorTexture.wrapS = floorTexture.wrapT = THREE.RepeatWrapping; floorTexture.wrapS = floorTexture.wrapT = THREE.RepeatWrapping;
floorTexture.repeat.set(10, 10); floorTexture.repeat.set(10, 10);
// Генерация уровня // Материалы сделаны светлее (запасной цвет), чтобы даже без текстур было видно
const wallGeo = new THREE.BoxGeometry(CELL_SIZE, CELL_SIZE * 1.5, CELL_SIZE);
const wallMat = new THREE.MeshStandardMaterial({ const wallMat = new THREE.MeshStandardMaterial({
map: wallTexture, map: wallTexture,
color: 0x884444, // Запасной цвет, если текстура не загрузится color: 0x996666, // Светло-красный запасной цвет
roughness: 0.9,
metalness: 0.1
});
const floorMat = new THREE.MeshStandardMaterial({
map: floorTexture,
color: 0x777777, // Светло-серый запасной цвет
roughness: 0.8, roughness: 0.8,
metalness: 0.2 metalness: 0.2
}); });
const floorGeo = new THREE.PlaneGeometry(mapLayout[0].length * CELL_SIZE, mapLayout.length * CELL_SIZE); const floorGeo = new THREE.PlaneGeometry(mapLayout[0].length * CELL_SIZE, mapLayout.length * CELL_SIZE);
const floorMat = new THREE.MeshStandardMaterial({
map: floorTexture,
color: 0x555555, // Запасной цвет
roughness: 0.7,
metalness: 0.5
});
const floor = new THREE.Mesh(floorGeo, floorMat); const floor = new THREE.Mesh(floorGeo, floorMat);
floor.rotation.x = -Math.PI / 2; floor.rotation.x = -Math.PI / 2;
floor.position.set((mapLayout[0].length * CELL_SIZE)/2 - CELL_SIZE/2, 0, (mapLayout.length * CELL_SIZE)/2 - CELL_SIZE/2); floor.position.set((mapLayout[0].length * CELL_SIZE)/2 - CELL_SIZE/2, 0, (mapLayout.length * CELL_SIZE)/2 - CELL_SIZE/2);
@@ -265,7 +281,7 @@
scene.add(floor); scene.add(floor);
const ceilGeo = new THREE.PlaneGeometry(mapLayout[0].length * CELL_SIZE, mapLayout.length * CELL_SIZE); const ceilGeo = new THREE.PlaneGeometry(mapLayout[0].length * CELL_SIZE, mapLayout.length * CELL_SIZE);
const ceilMat = new THREE.MeshStandardMaterial({ color: 0x110505, roughness: 1 }); const ceilMat = new THREE.MeshStandardMaterial({ color: 0x221111, roughness: 1 });
const ceil = new THREE.Mesh(ceilGeo, ceilMat); const ceil = new THREE.Mesh(ceilGeo, ceilMat);
ceil.rotation.x = Math.PI / 2; ceil.rotation.x = Math.PI / 2;
ceil.position.set((mapLayout[0].length * CELL_SIZE)/2 - CELL_SIZE/2, CELL_SIZE * 1.5, (mapLayout.length * CELL_SIZE)/2 - CELL_SIZE/2); ceil.position.set((mapLayout[0].length * CELL_SIZE)/2 - CELL_SIZE/2, CELL_SIZE * 1.5, (mapLayout.length * CELL_SIZE)/2 - CELL_SIZE/2);
@@ -278,17 +294,19 @@
const posZ = z * CELL_SIZE; const posZ = z * CELL_SIZE;
if (type === 1) { if (type === 1) {
const wallGeo = new THREE.BoxGeometry(CELL_SIZE, CELL_SIZE * 1.5, CELL_SIZE);
const wall = new THREE.Mesh(wallGeo, wallMat); const wall = new THREE.Mesh(wallGeo, wallMat);
wall.position.set(posX, CELL_SIZE * 0.75, posZ); wall.position.set(posX, CELL_SIZE * 0.75, posZ);
wall.castShadow = true; wall.castShadow = true;
wall.receiveShadow = true; wall.receiveShadow = true;
scene.add(wall); scene.add(wall);
} else if (type === 9) { } else if (type === 9) {
camera.position.set(posX, 1.6, posZ); // Спавн игрока строго в центре клетки
camera.position.set(posX + CELL_SIZE/2, 1.6, posZ + CELL_SIZE/2);
} else if (type === 2) { } else if (type === 2) {
spawnEnemy(posX, posZ); spawnEnemy(posX + CELL_SIZE/2, posZ + CELL_SIZE/2);
} else if (type === 3) { } else if (type === 3) {
spawnPickup(posX, posZ); spawnPickup(posX + CELL_SIZE/2, posZ + CELL_SIZE/2);
} }
} }
} }
@@ -296,7 +314,7 @@
// Оружие // Оружие
weaponGroup = new THREE.Group(); weaponGroup = new THREE.Group();
const bodyGeo = new THREE.BoxGeometry(0.12, 0.15, 0.7); const bodyGeo = new THREE.BoxGeometry(0.12, 0.15, 0.7);
const bodyMat = new THREE.MeshStandardMaterial({ color: 0x222222, metalness: 0.8, roughness: 0.4 }); const bodyMat = new THREE.MeshStandardMaterial({ color: 0x333333, metalness: 0.8, roughness: 0.4 });
const body = new THREE.Mesh(bodyGeo, bodyMat); const body = new THREE.Mesh(bodyGeo, bodyMat);
const barrelGeo = new THREE.CylinderGeometry(0.035, 0.035, 0.6, 8); const barrelGeo = new THREE.CylinderGeometry(0.035, 0.035, 0.6, 8);
@@ -308,7 +326,7 @@
pumpGroup = new THREE.Group(); pumpGroup = new THREE.Group();
const pumpGeo = new THREE.BoxGeometry(0.14, 0.12, 0.25); const pumpGeo = new THREE.BoxGeometry(0.14, 0.12, 0.25);
const pumpMat = new THREE.MeshStandardMaterial({ color: 0x4a3c31, roughness: 0.9 }); const pumpMat = new THREE.MeshStandardMaterial({ color: 0x5c4a3d, roughness: 0.9 });
const pump = new THREE.Mesh(pumpGeo, pumpMat); const pump = new THREE.Mesh(pumpGeo, pumpMat);
pump.position.z = 0.15; pump.position.z = 0.15;
pumpGroup.add(pump); pumpGroup.add(pump);
@@ -322,7 +340,6 @@
// --- УПРАВЛЕНИЕ --- // --- УПРАВЛЕНИЕ ---
controls = new PointerLockControls(camera, document.body); controls = new PointerLockControls(camera, document.body);
// Надежный обработчик клика для запуска
blocker.addEventListener('click', function() { blocker.addEventListener('click', function() {
initAudio(); initAudio();
controls.lock(); controls.lock();
@@ -352,10 +369,35 @@
window.addEventListener('resize', onWindowResize); window.addEventListener('resize', onWindowResize);
} }
// --- ИСПРАВЛЕННАЯ КОЛЛИЗИЯ ---
function checkWallCollision(x, z) {
// Проверяем 4 угла вокруг игрока (квадрат коллизии)
const checks = [
{ x: x - PLAYER_RADIUS, z: z - PLAYER_RADIUS },
{ x: x + PLAYER_RADIUS, z: z - PLAYER_RADIUS },
{ x: x - PLAYER_RADIUS, z: z + PLAYER_RADIUS },
{ x: x + PLAYER_RADIUS, z: z + PLAYER_RADIUS }
];
for (let p of checks) {
const gridX = Math.floor(p.x / CELL_SIZE);
const gridZ = Math.floor(p.z / CELL_SIZE);
// Если вышли за пределы карты или попали в стену (1)
if (gridZ < 0 || gridZ >= mapLayout.length || gridX < 0 || gridX >= mapLayout[0].length) {
return true;
}
if (mapLayout[gridZ][gridX] === 1) {
return true;
}
}
return false;
}
function spawnEnemy(x, z) { function spawnEnemy(x, z) {
const group = new THREE.Group(); const group = new THREE.Group();
const bodyGeo = new THREE.IcosahedronGeometry(0.7, 1); const bodyGeo = new THREE.IcosahedronGeometry(0.7, 1);
const bodyMat = new THREE.MeshStandardMaterial({ color: 0x880000, roughness: 0.3, metalness: 0.6, emissive: 0x220000 }); const bodyMat = new THREE.MeshStandardMaterial({ color: 0xaa0000, roughness: 0.3, metalness: 0.6, emissive: 0x330000 });
const body = new THREE.Mesh(bodyGeo, bodyMat); const body = new THREE.Mesh(bodyGeo, bodyMat);
const eyeGeo = new THREE.SphereGeometry(0.25, 16, 16); const eyeGeo = new THREE.SphereGeometry(0.25, 16, 16);
@@ -363,7 +405,7 @@
const eye = new THREE.Mesh(eyeGeo, eyeMat); const eye = new THREE.Mesh(eyeGeo, eyeMat);
eye.position.set(0, 0.2, 0.6); eye.position.set(0, 0.2, 0.6);
const light = new THREE.PointLight(0xff0000, 1, 5); const light = new THREE.PointLight(0xff0000, 1.5, 6);
light.position.y = 0.5; light.position.y = 0.5;
group.add(body, eye, light); group.add(body, eye, light);
@@ -376,10 +418,10 @@
function spawnPickup(x, z, isDrop = false) { function spawnPickup(x, z, isDrop = false) {
const geo = new THREE.BoxGeometry(0.6, 0.4, 0.4); const geo = new THREE.BoxGeometry(0.6, 0.4, 0.4);
const mat = new THREE.MeshStandardMaterial({ color: 0xffcc00, emissive: 0xaa6600, emissiveIntensity: 0.5, metalness: 0.8 }); const mat = new THREE.MeshStandardMaterial({ color: 0xffcc00, emissive: 0xaa6600, emissiveIntensity: 0.8, metalness: 0.8 });
const mesh = new THREE.Mesh(geo, mat); const mesh = new THREE.Mesh(geo, mat);
mesh.position.set(x, isDrop ? 0.5 : 1, z); mesh.position.set(x, isDrop ? 0.5 : 1, z);
const light = new THREE.PointLight(0xffcc00, 0.5, 3); const light = new THREE.PointLight(0xffcc00, 1.0, 4);
mesh.add(light); mesh.add(light);
scene.add(mesh); scene.add(mesh);
pickups.push({ mesh, isDrop, bobOffset: Math.random() * Math.PI }); pickups.push({ mesh, isDrop, bobOffset: Math.random() * Math.PI });
@@ -445,7 +487,8 @@
setTimeout(() => { pumpGroup.position.z = 0; }, 300); setTimeout(() => { pumpGroup.position.z = 0; }, 300);
muzzleLight.intensity = 8; muzzleLight.position.copy(camera.position).add(camera.getWorldDirection(new THREE.Vector3()).multiplyScalar(1));
muzzleLight.intensity = 10;
setTimeout(() => { muzzleLight.intensity = 0; }, 60); setTimeout(() => { muzzleLight.intensity = 0; }, 60);
const raycaster = new THREE.Raycaster(); const raycaster = new THREE.Raycaster();
@@ -485,7 +528,7 @@
} }
if(emptySpots.length > 0) { if(emptySpots.length > 0) {
const spot = emptySpots[Math.floor(Math.random() * emptySpots.length)]; const spot = emptySpots[Math.floor(Math.random() * emptySpots.length)];
spawnEnemy(spot.x * CELL_SIZE, spot.z * CELL_SIZE); spawnEnemy(spot.x * CELL_SIZE + CELL_SIZE/2, spot.z * CELL_SIZE + CELL_SIZE/2);
} }
}, 2000); }, 2000);
} }
@@ -493,15 +536,6 @@
} }
} }
function checkWallCollision(newX, newZ) {
const gridX = Math.round(newX / CELL_SIZE);
const gridZ = Math.round(newZ / CELL_SIZE);
if (gridZ >= 0 && gridZ < mapLayout.length && gridX >= 0 && gridX < mapLayout[0].length) {
return mapLayout[gridZ][gridX] === 1;
}
return true;
}
function onWindowResize() { function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight; camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix(); camera.updateProjectionMatrix();
@@ -516,22 +550,34 @@
prevTime = time; prevTime = time;
if (controls.isLocked) { if (controls.isLocked) {
velocity.x -= velocity.x * 12.0 * delta; // Физика движения
velocity.z -= velocity.z * 12.0 * delta; velocity.x -= velocity.x * 10.0 * delta;
velocity.z -= velocity.z * 10.0 * delta;
direction.z = Number(moveForward) - Number(moveBackward); direction.z = Number(moveForward) - Number(moveBackward);
direction.x = Number(moveRight) - Number(moveLeft); direction.x = Number(moveRight) - Number(moveLeft);
direction.normalize(); direction.normalize();
if (moveForward || moveBackward) velocity.z -= direction.z * 80.0 * delta; if (moveForward || moveBackward) velocity.z -= direction.z * 100.0 * delta;
if (moveLeft || moveRight) velocity.x -= direction.x * 80.0 * delta; if (moveLeft || moveRight) velocity.x -= direction.x * 100.0 * delta;
const nextX = camera.position.x - velocity.x * delta; const nextX = camera.position.x - velocity.x * delta;
const nextZ = camera.position.z - velocity.z * delta; const nextZ = camera.position.z - velocity.z * delta;
if (!checkWallCollision(nextX, camera.position.z)) camera.position.x = nextX; // ИСПРАВЛЕННОЕ СКОЛЬЖЕНИЕ ВДОЛЬ СТЕН: проверяем оси независимо
if (!checkWallCollision(camera.position.x, nextZ)) camera.position.z = nextZ; if (!checkWallCollision(nextX, camera.position.z)) {
camera.position.x = nextX;
} else {
velocity.x = 0; // Гасим инерцию при ударе о стену
}
if (!checkWallCollision(camera.position.x, nextZ)) {
camera.position.z = nextZ;
} else {
velocity.z = 0;
}
// Анимация оружия
weaponGroup.position.z = THREE.MathUtils.lerp(weaponGroup.position.z, -0.6, delta * 12); weaponGroup.position.z = THREE.MathUtils.lerp(weaponGroup.position.z, -0.6, delta * 12);
weaponGroup.rotation.x = THREE.MathUtils.lerp(weaponGroup.rotation.x, 0, delta * 12); weaponGroup.rotation.x = THREE.MathUtils.lerp(weaponGroup.rotation.x, 0, delta * 12);
@@ -541,6 +587,7 @@
weaponGroup.position.x = 0.35 + Math.cos(bobSpeed * 0.5) * 0.01; weaponGroup.position.x = 0.35 + Math.cos(bobSpeed * 0.5) * 0.01;
} }
// Логика врагов
enemies.forEach(enemy => { enemies.forEach(enemy => {
const dist = enemy.position.distanceTo(camera.position); const dist = enemy.position.distanceTo(camera.position);
const dirToPlayer = new THREE.Vector3().subVectors(camera.position, enemy.position).normalize(); const dirToPlayer = new THREE.Vector3().subVectors(camera.position, enemy.position).normalize();
@@ -584,6 +631,7 @@
} }
}); });
// Подбор предметов
for (let i = pickups.length - 1; i >= 0; i--) { for (let i = pickups.length - 1; i >= 0; i--) {
const p = pickups[i]; const p = pickups[i];
p.mesh.rotation.y += delta * 2; p.mesh.rotation.y += delta * 2;
@@ -598,6 +646,7 @@
} }
} }
// Частицы
for (let i = particles.length - 1; i >= 0; i--) { for (let i = particles.length - 1; i >= 0; i--) {
const p = particles[i]; const p = particles[i];
p.life -= delta * 1.5; p.life -= delta * 1.5;