3D-GAME / index.html
ginipick's picture
Update index.html
5ec2448 verified
<!DOCTYPE html>
<html>
<head>
<title>Forest Explorer</title>
<style>
body { margin: 0; background: black; overflow: hidden; }
canvas { width: 100vw; height: 100vh; }
.ui {
position: fixed;
color: white;
text-shadow: 2px 2px 2px rgba(0,0,0,0.5);
pointer-events: none;
font-family: Arial, sans-serif;
}
#stamina {
bottom: 20px;
left: 20px;
width: 200px;
height: 5px;
background: rgba(0,0,0,0.5);
border-radius: 3px;
}
#stamina-bar {
width: 100%;
height: 100%;
background: #4CAF50;
border-radius: 3px;
transition: width 0.2s;
}
#crosshair {
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 20px;
}
#flashlight-status {
top: 20px;
right: 20px;
}
.vignette {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: radial-gradient(circle, transparent 50%, rgba(0,0,0,0.4) 100%);
pointer-events: none;
}
</style>
</head>
<body>
<div id="stamina" class="ui"><div id="stamina-bar"></div></div>
<div id="crosshair" class="ui">+</div>
<div id="flashlight-status" class="ui">Flashlight [F]</div>
<div class="vignette"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
const SPAWN_POSITION = new THREE.Vector3(0, 1.7, 30); // Spawn point
const CAVE_ENTRANCE = new THREE.Vector3(0, 1.7, -15); // Cave entrance position
let camera, scene, renderer, clock;
let player = {
position: SPAWN_POSITION.clone(),
velocity: new THREE.Vector3(),
speed: {
walk: 3.5,
sprint: 7,
current: 3.5
},
stamina: 100,
headBob: {
value: 0,
intensity: 0.07,
speed: 8
},
inCave: false
};
let controls = {
forward: false,
backward: false,
left: false,
right: false,
sprint: false
};
let flashlight;
let isFlashlightOn = false;
// Initialize game
init();
animate();
async function init() {
// Setup basic scene
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
clock = new THREE.Clock();
// Setup renderer
renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);
// Setup scene elements
createForest();
createCave();
setupLighting();
setupParticles();
setupAudio();
// Initialize player
camera.position.copy(player.position);
document.addEventListener('keydown', onKeyDown);
document.addEventListener('keyup', onKeyUp);
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('click', () => document.body.requestPointerLock());
}
function createForest() {
// Ground
const ground = new THREE.Mesh(
new THREE.PlaneGeometry(200, 200),
new THREE.MeshStandardMaterial({
color: 0x33aa33,
roughness: 0.8
})
);
ground.rotation.x = -Math.PI/2;
ground.receiveShadow = true;
scene.add(ground);
// Trees
for(let i = 0; i < 500; i++) {
const distance = Math.random() * 80 + 20;
const angle = Math.random() * Math.PI * 2;
const x = Math.cos(angle) * distance;
const z = Math.sin(angle) * distance;
if(Math.abs(x) > 10 || z > 0) { // Keep path to cave clear
createTree(x, z);
}
}
// Flowers and grass
for(let i = 0; i < 1000; i++) {
const x = Math.random() * 180 - 90;
const z = Math.random() * 180 - 90;
if(Math.abs(x) > 5 || z > 0) {
createVegetation(x, z);
}
}
}
function createTree(x, z) {
const trunk = new THREE.Mesh(
new THREE.CylinderGeometry(0.5, 0.7, 5),
new THREE.MeshStandardMaterial({color: 0x885533})
);
const leaves = new THREE.Mesh(
new THREE.SphereGeometry(2),
new THREE.MeshStandardMaterial({color: 0x227722})
);
trunk.position.set(x, 2.5, z);
leaves.position.set(x, 6, z);
trunk.castShadow = true;
leaves.castShadow = true;
scene.add(trunk);
scene.add(leaves);
}
function createVegetation(x, z) {
const color = Math.random() > 0.5 ? 0xff99cc : 0xffff99;
const flower = new THREE.Mesh(
new THREE.SphereGeometry(0.2),
new THREE.MeshStandardMaterial({
color: color,
emissive: color,
emissiveIntensity: 0.2
})
);
flower.position.set(x, 0.2, z);
scene.add(flower);
}
function createCave() {
// Main tunnel
const points = [];
for(let i = 0; i < 10; i++) {
points.push(
new THREE.Vector3(
Math.sin(i/2) * 2,
Math.cos(i/3) * 1.5,
-15 - i * 5
)
);
}
const tunnelGeometry = new THREE.TubeGeometry(
new THREE.CatmullRomCurve3(points),
60,
3,
8,
false
);
const caveMaterial = new THREE.MeshStandardMaterial({
color: 0x333333,
roughness: 1,
side: THREE.BackSide
});
const tunnel = new THREE.Mesh(tunnelGeometry, caveMaterial);
scene.add(tunnel);
// Cave markings
const markingGeometry = new THREE.CircleGeometry(0.3, 16);
const markingMaterial = new THREE.MeshStandardMaterial({
color: 0xff0000,
emissive: 0xff0000,
emissiveIntensity: 0.5
});
for(let i = 0; i < 15; i++) {
const marking = new THREE.Mesh(markingGeometry, markingMaterial);
marking.position.set(
Math.random() * 4 - 2,
Math.random() * 2 + 1,
-20 - Math.random() * 30
);
marking.rotation.y = Math.random() * Math.PI;
scene.add(marking);
}
}
function setupLighting() {
// Forest lighting
const ambientLight = new THREE.AmbientLight(0x666666);
scene.add(ambientLight);
const sunLight = new THREE.DirectionalLight(0xffffbb, 1);
sunLight.position.set(50, 100, 50);
sunLight.castShadow = true;
scene.add(sunLight);
// Flashlight
flashlight = new THREE.SpotLight(0xffffff, 1);
flashlight.angle = Math.PI/6;
flashlight.penumbra = 0.1;
flashlight.decay = 2;
flashlight.distance = 30;
flashlight.visible = false;
camera.add(flashlight);
scene.add(camera);
}
function setupParticles() {
// Pollen/leaves particle system
const particleCount = 1000;
const positions = new Float32Array(particleCount * 3);
const colors = new Float32Array(particleCount * 3);
for(let i = 0; i < particleCount * 3; i += 3) {
positions[i] = Math.random() * 200 - 100;
positions[i+1] = Math.random() * 20;
positions[i+2] = Math.random() * 200 - 100;
colors[i] = 1;
colors[i+1] = 1;
colors[i+2] = 0.8;
}
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
const material = new THREE.PointsMaterial({
size: 0.1,
vertexColors: true,
transparent: true,
opacity: 0.6
});
const particles = new THREE.Points(geometry, material);
scene.add(particles);
}
function setupAudio() {
// Implement audio setup
}
function update(delta) {
updatePlayer(delta);
updateEnvironment();
}
function updatePlayer(delta) {
if(!document.pointerLockElement) return;
// Movement direction
const direction = new THREE.Vector3();
if(controls.forward) direction.z -= 1;
if(controls.backward) direction.z += 1;
if(controls.left) direction.x -= 1;
if(controls.right) direction.x += 1;
direction.normalize();
// Speed and stamina
if(controls.sprint && player.stamina > 0 && direction.length() > 0) {
player.speed.current = player.speed.sprint;
player.stamina = Math.max(0, player.stamina - delta * 30);
} else {
player.speed.current = player.speed.walk;
player.stamina = Math.min(100, player.stamina + delta * 10);
}
document.getElementById('stamina-bar').style.width =
player.stamina + '%';
// Move player
if(direction.length() > 0) {
const moveX = direction.x * player.speed.current * delta;
const moveZ = direction.z * player.speed.current * delta;
camera.position.x += moveX * Math.cos(camera.rotation.y) +
moveZ * Math.sin(camera.rotation.y);
camera.position.z += moveZ * Math.cos(camera.rotation.y) -
moveX * Math.sin(camera.rotation.y);
// Head bob
player.headBob.value += delta * player.headBob.speed;
camera.position.y = player.position.y +
Math.sin(player.headBob.value) * player.headBob.intensity;
}
// Update flashlight
if(isFlashlightOn) {
flashlight.position.copy(camera.position);
flashlight.rotation.copy(camera.rotation);
}
}
function updateEnvironment() {
// Check if entering/leaving cave
const inCaveNow = camera.position.z < -15;
if(inCaveNow !== player.inCave) {
player.inCave = inCaveNow;
transitionEnvironment();
}
}
function transitionEnvironment() {
if(player.inCave) {
scene.fog = new THREE.FogExp2(0x000000, 0.15);
scene.background = new THREE.Color(0x000000);
} else {
scene.fog = null;
scene.background = new THREE.Color(0x88ccff);
}
}
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta();
update(delta);
renderer.render(scene, camera);
}
// Event handlers
function onKeyDown(event) {
switch(event.code) {
case 'KeyW': controls.forward = true; break;
case 'KeyS': controls.backward = true; break;
case 'KeyA': controls.left = true; break;
case 'KeyD': controls.right = true; break;
case 'ShiftLeft': controls.sprint = true; break;
case 'KeyF': toggleFlashlight(); break;
}
}
function onKeyUp(event) {
switch(event.code) {
case 'KeyW': controls.forward = false; break;
case 'KeyS': controls.backward = false; break;
case 'KeyA': controls.left = false; break;
case 'KeyD': controls.right = false; break;
case 'ShiftLeft': controls.sprint = false; break;
}
}
function onMouseMove(event) {
if(document.pointerLockElement) {
camera.rotation.y -= event.movementX * 0.002;
camera.rotation.x = Math.max(
-Math.PI/2,
Math.min(Math.PI/2,
camera.rotation.x - event.movementY * 0.002)
);
}
}
function toggleFlashlight() {
isFlashlightOn = !isFlashlightOn;
flashlight.visible = isFlashlightOn;
document.getElementById('flashlight-status').textContent =
`Flashlight [F] ${isFlashlightOn ? 'ON' : 'OFF'}`;
}
</script>
</body>
</html>