Обновить doom.html
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<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>
|
||||
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; }
|
||||
@@ -19,12 +19,12 @@
|
||||
#crosshair::after { top: 0; left: 11px; width: 2px; height: 24px; }
|
||||
|
||||
#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;
|
||||
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; }
|
||||
#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; }
|
||||
|
||||
#hud {
|
||||
@@ -34,7 +34,7 @@
|
||||
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-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; }
|
||||
.health { color: #00ff00; }
|
||||
.armor { color: #00bfff; }
|
||||
@@ -108,7 +108,7 @@
|
||||
let pickups = [];
|
||||
let weaponGroup, pumpGroup;
|
||||
let isPumping = false;
|
||||
let muzzleLight;
|
||||
let muzzleLight, playerLight;
|
||||
let audioCtx;
|
||||
|
||||
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]
|
||||
];
|
||||
const CELL_SIZE = 4;
|
||||
const PLAYER_RADIUS = 0.6; // Радиус игрока для коллизий
|
||||
|
||||
const uiHealth = document.getElementById('health-val');
|
||||
const uiArmor = document.getElementById('armor-val');
|
||||
@@ -213,14 +214,31 @@
|
||||
|
||||
function init() {
|
||||
scene = new THREE.Scene();
|
||||
scene.background = new THREE.Color(0x050000);
|
||||
scene.fog = new THREE.FogExp2(0x1a0505, 0.04);
|
||||
scene.background = new THREE.Color(0x1a0a0a); // Светлее фон
|
||||
|
||||
// УМЕНЬШЕНА плотность тумана и сделан светлее для лучшей видимости
|
||||
scene.fog = new THREE.FogExp2(0x2a0a0a, 0.02);
|
||||
|
||||
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);
|
||||
|
||||
// 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.castShadow = true;
|
||||
scene.add(muzzleLight);
|
||||
@@ -229,7 +247,6 @@
|
||||
loadingText.style.display = 'block';
|
||||
const textureLoader = new THREE.TextureLoader();
|
||||
|
||||
// Используем надежные URL с официального репозитория Three.js (CORS-friendly)
|
||||
const wallUrl = 'https://threejs.org/examples/textures/brick_diffuse.jpg';
|
||||
const floorUrl = 'https://threejs.org/examples/textures/metal.jpg';
|
||||
|
||||
@@ -241,23 +258,22 @@
|
||||
floorTexture.wrapS = floorTexture.wrapT = THREE.RepeatWrapping;
|
||||
floorTexture.repeat.set(10, 10);
|
||||
|
||||
// Генерация уровня
|
||||
const wallGeo = new THREE.BoxGeometry(CELL_SIZE, CELL_SIZE * 1.5, CELL_SIZE);
|
||||
// Материалы сделаны светлее (запасной цвет), чтобы даже без текстур было видно
|
||||
const wallMat = new THREE.MeshStandardMaterial({
|
||||
map: wallTexture,
|
||||
color: 0x884444, // Запасной цвет, если текстура не загрузится
|
||||
color: 0x996666, // Светло-красный запасной цвет
|
||||
roughness: 0.9,
|
||||
metalness: 0.1
|
||||
});
|
||||
|
||||
const floorMat = new THREE.MeshStandardMaterial({
|
||||
map: floorTexture,
|
||||
color: 0x777777, // Светло-серый запасной цвет
|
||||
roughness: 0.8,
|
||||
metalness: 0.2
|
||||
});
|
||||
|
||||
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 floorGeo = new THREE.PlaneGeometry(mapLayout[0].length * CELL_SIZE, mapLayout.length * CELL_SIZE);
|
||||
const floor = new THREE.Mesh(floorGeo, floorMat);
|
||||
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);
|
||||
@@ -265,7 +281,7 @@
|
||||
scene.add(floor);
|
||||
|
||||
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);
|
||||
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);
|
||||
@@ -278,17 +294,19 @@
|
||||
const posZ = z * CELL_SIZE;
|
||||
|
||||
if (type === 1) {
|
||||
const wallGeo = new THREE.BoxGeometry(CELL_SIZE, CELL_SIZE * 1.5, CELL_SIZE);
|
||||
const wall = new THREE.Mesh(wallGeo, wallMat);
|
||||
wall.position.set(posX, CELL_SIZE * 0.75, posZ);
|
||||
wall.castShadow = true;
|
||||
wall.receiveShadow = true;
|
||||
scene.add(wall);
|
||||
} 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) {
|
||||
spawnEnemy(posX, posZ);
|
||||
spawnEnemy(posX + CELL_SIZE/2, posZ + CELL_SIZE/2);
|
||||
} else if (type === 3) {
|
||||
spawnPickup(posX, posZ);
|
||||
spawnPickup(posX + CELL_SIZE/2, posZ + CELL_SIZE/2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -296,7 +314,7 @@
|
||||
// Оружие
|
||||
weaponGroup = new THREE.Group();
|
||||
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 barrelGeo = new THREE.CylinderGeometry(0.035, 0.035, 0.6, 8);
|
||||
@@ -308,7 +326,7 @@
|
||||
|
||||
pumpGroup = new THREE.Group();
|
||||
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);
|
||||
pump.position.z = 0.15;
|
||||
pumpGroup.add(pump);
|
||||
@@ -322,7 +340,6 @@
|
||||
// --- УПРАВЛЕНИЕ ---
|
||||
controls = new PointerLockControls(camera, document.body);
|
||||
|
||||
// Надежный обработчик клика для запуска
|
||||
blocker.addEventListener('click', function() {
|
||||
initAudio();
|
||||
controls.lock();
|
||||
@@ -352,10 +369,35 @@
|
||||
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) {
|
||||
const group = new THREE.Group();
|
||||
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 eyeGeo = new THREE.SphereGeometry(0.25, 16, 16);
|
||||
@@ -363,7 +405,7 @@
|
||||
const eye = new THREE.Mesh(eyeGeo, eyeMat);
|
||||
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;
|
||||
|
||||
group.add(body, eye, light);
|
||||
@@ -376,10 +418,10 @@
|
||||
|
||||
function spawnPickup(x, z, isDrop = false) {
|
||||
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);
|
||||
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);
|
||||
scene.add(mesh);
|
||||
pickups.push({ mesh, isDrop, bobOffset: Math.random() * Math.PI });
|
||||
@@ -445,7 +487,8 @@
|
||||
|
||||
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);
|
||||
|
||||
const raycaster = new THREE.Raycaster();
|
||||
@@ -485,7 +528,7 @@
|
||||
}
|
||||
if(emptySpots.length > 0) {
|
||||
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);
|
||||
}
|
||||
@@ -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() {
|
||||
camera.aspect = window.innerWidth / window.innerHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
@@ -516,22 +550,34 @@
|
||||
prevTime = time;
|
||||
|
||||
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.x = Number(moveRight) - Number(moveLeft);
|
||||
direction.normalize();
|
||||
|
||||
if (moveForward || moveBackward) velocity.z -= direction.z * 80.0 * delta;
|
||||
if (moveLeft || moveRight) velocity.x -= direction.x * 80.0 * delta;
|
||||
if (moveForward || moveBackward) velocity.z -= direction.z * 100.0 * delta;
|
||||
if (moveLeft || moveRight) velocity.x -= direction.x * 100.0 * delta;
|
||||
|
||||
const nextX = camera.position.x - velocity.x * 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.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;
|
||||
}
|
||||
|
||||
// Логика врагов
|
||||
enemies.forEach(enemy => {
|
||||
const dist = enemy.position.distanceTo(camera.position);
|
||||
const dirToPlayer = new THREE.Vector3().subVectors(camera.position, enemy.position).normalize();
|
||||
@@ -584,6 +631,7 @@
|
||||
}
|
||||
});
|
||||
|
||||
// Подбор предметов
|
||||
for (let i = pickups.length - 1; i >= 0; i--) {
|
||||
const p = pickups[i];
|
||||
p.mesh.rotation.y += delta * 2;
|
||||
@@ -598,6 +646,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Частицы
|
||||
for (let i = particles.length - 1; i >= 0; i--) {
|
||||
const p = particles[i];
|
||||
p.life -= delta * 1.5;
|
||||
|
||||
Reference in New Issue
Block a user