rollercoaster / index.html
akhaliq's picture
akhaliq HF Staff
Update index.html
6a5770b verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Epic 3D Rollercoaster</title>
<style>
body{margin:0;overflow:hidden;font-family:'Arial',sans-serif;background:#000;color:#fff}
canvas{display:block;width:100vw;height:100vh}
#ui{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);text-align:center;
background:rgba(0,0,0,0.5);padding:15px 25px;border-radius:15px;font-size:18px;
pointer-events:none;backdrop-filter:blur(5px);border:1px solid rgba(255,255,255,0.1);
box-shadow:0 0 20px rgba(0,255,255,0.3);z-index:10}
#speed{font-size:28px;margin:10px 0;font-weight:bold;text-shadow:0 0 10px currentColor}
.speed-meter{width:250px;height:12px;background:rgba(255,255,255,0.1);border-radius:10px;
margin:15px auto;overflow:hidden;box-shadow:0 0 5px rgba(0,0,0,0.5) inset}
#speed-fill{height:100%;background:linear-gradient(90deg,#4CAF50,#FFEB3B,#F44336);transition:width 0.05s;
box-shadow:0 0 10px rgba(255,255,255,0.3)}
#controls{position:absolute;top:20px;left:20px;background:rgba(0,0,0,0.5);padding:15px;
border-radius:15px;font-size:16px;backdrop-filter:blur(5px);border:1px solid rgba(255,255,255,0.1);
max-width:220px;z-index:10}
#start-screen{position:absolute;top:0;left:0;width:100%;height:100%;background:radial-gradient(ellipse at center,
rgba(0,0,0,0.9) 0%,rgba(0,0,20,0.95) 100%);display:flex;flex-direction:column;justify-content:center;
align-items:center;z-index:100;text-align:center;opacity:1;transition:opacity 0.5s}
h1{font-size:4.5rem;margin:0 0 10px 0;background:linear-gradient(90deg,#ff8a00,#e52e71,#b145e9);
-webkit-background-clip:text;-webkit-text-fill-color:transparent;text-shadow:0 0 20px rgba(255,138,0,0.5)}
button{padding:15px 40px;font-size:1.3rem;margin-top:40px;background:linear-gradient(45deg,#00c6ff,#0072ff);
color:#fff;border:none;border-radius:50px;cursor:pointer;transition:all 0.3s;box-shadow:0 5px 25px rgba(0,198,255,0.5);
font-weight:bold;text-transform:uppercase}
button:hover{transform:scale(1.05);box-shadow:0 8px 35px rgba(0,198,255,0.7)}
#tutorial{background:rgba(255,255,255,0.1);padding:25px;border-radius:15px;margin:30px 0;max-width:600px;
border:1px solid rgba(255,255,255,0.1);backdrop-filter:blur(5px)}
#boost-container{margin-top:15px;font-size:14px}
#boost-meter{height:6px;background:rgba(255,255,255,0.1);border-radius:3px;margin-top:8px;overflow:hidden}
#boost-fill{height:100%;background:linear-gradient(90deg,#FF9800,#FF5722);box-shadow:0 0 10px rgba(255,152,0,0.5);
transition:width 0.1s}
#effects{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;opacity:0;transition:opacity 0.3s;
background:radial-gradient(ellipse at center,transparent 60%,rgba(255,255,255,0.3));z-index:5}
.stats{display:flex;justify-content:space-between;width:250px;margin:0 auto 10px auto}
.stat-value{font-weight:bold}
.blur-bg{position:absolute;top:0;left:0;width:100%;height:100%;background:radial-gradient(ellipse at center,
rgba(0,150,255,0.1) 0%,transparent 70%);opacity:0.5;z-index:-1}
</style>
</head>
<body>
<div id="effects"></div>
<div class="blur-bg"></div>
<div id="controls"><strong>CONTROLS:</strong><br>↑/↓: Speed<br>←/→: Lean<br>SPACE: Boost</div>
<div id="ui">
<div class="stats"><div>Score: <span class="stat-value" id="score">0</span></div><div>Boost: <span class="stat-value" id="boost-text">Ready</span></div></div>
<div id="speed">0 km/h</div><div class="speed-meter"><div id="speed-fill" style="width:0%"></div></div>
<div id="boost-container"><div id="boost-meter"><div id="boost-fill" style="width:100%"></div></div></div>
</div>
<div id="start-screen">
<h1>Epic Rollercoaster</h1>
<p style="font-size:1.3rem;max-width:700px;line-height:1.5">Experience breathtaking 3D visuals!</p>
<div id="tutorial"><strong>HOW TO PLAY:</strong><br><br>- ↑/↓ arrows for speed<br>- ←/→ arrows to lean<br>- SPACE for turbo boost</div>
<button id="start-btn">START RIDE</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/build/three.min.js"></script>
<script>
// Scene setup
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x000000);
scene.fog = new THREE.FogExp2(0x87CEEB, 0.003);
const camera = new THREE.PerspectiveCamera(85, window.innerWidth/window.innerHeight, 0.1, 3000);
camera.position.set(0,1.5,0);
const renderer = new THREE.WebGLRenderer({antialias:true,powerPreference:"high-performance"});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.2;
document.body.appendChild(renderer.domElement);
// Track creation
function createTrack() {
const points = [];
for(let i=0;i<=500;i++) {
const t = i/500*Math.PI*6;
points.push(new THREE.Vector3(
Math.sin(t*0.7)*150+Math.sin(t*0.3)*120*0.7,
Math.sin(t*0.5)*90+30+Math.abs(Math.sin(t))*40,
-t*30
));
}
const path = new THREE.CatmullRomCurve3(points,true);
const geom = new THREE.TubeGeometry(path,1000,1.8,24,false);
const mat = new THREE.MeshStandardMaterial({color:0x444444,metalness:0.8,roughness:0.3,side:THREE.DoubleSide});
const track = new THREE.Mesh(geom,mat);
track.castShadow = track.receiveShadow = true;
track.position.y = -1;
const edges = new THREE.EdgesGeometry(geom);
track.add(new THREE.LineSegments(edges,new THREE.LineBasicMaterial({color:0x00ffff,transparent:true,opacity:0.8})));
return {track,path};
}
const {track,path} = createTrack();
scene.add(track);
// Environment
function createEnv() {
const ground = new THREE.Mesh(new THREE.PlaneGeometry(5000,5000),
new THREE.MeshStandardMaterial({color:0x245d34,roughness:0.8}));
ground.rotation.x=-Math.PI/2; ground.position.y=-1.5; ground.receiveShadow=true; scene.add(ground);
const mtnMat = new THREE.MeshStandardMaterial({color:0x5a5a5a,roughness:0.9});
for(let i=0;i<30;i++) {
const size=100+Math.random()*150;
const mtn=new THREE.Mesh(new THREE.ConeGeometry(size,size*1.5,6),mtnMat);
const a=Math.random()*Math.PI*2,d=800+Math.random()*1200;
mtn.position.set(Math.cos(a)*d,-size*0.3,Math.sin(a)*d);
mtn.rotation.y=Math.random()*Math.PI; mtn.castShadow=true; scene.add(mtn);
}
const treeGroup=new THREE.Group();
for(let i=0;i<100;i++) {
const a=Math.random()*Math.PI*2,d=50+Math.random()*400,x=Math.cos(a)*d,z=Math.sin(a)*d;
if(Math.abs(x)<100&&Math.abs(z)<100) continue;
const th=3+Math.random()*7;
const trunk=new THREE.Mesh(new THREE.CylinderGeometry(0.3,0.5,th,8),new THREE.MeshStandardMaterial({color:0x5c4033}));
trunk.position.set(x,th/2,z); trunk.castShadow=true;
const leaves=new THREE.Mesh(new THREE.SphereGeometry(th*0.7,8),new THREE.MeshStandardMaterial({color:0x1a5e20}));
leaves.position.set(x,th+(th*0.4+Math.random()*3)*0.5,z);
leaves.scale.set(1+Math.random()*0.5,1,1+Math.random()*0.5); leaves.castShadow=true;
treeGroup.add(trunk); treeGroup.add(leaves);
}
scene.add(treeGroup);
scene.add(new THREE.Mesh(new THREE.SphereGeometry(2000,32),new THREE.MeshBasicMaterial({color:0x87CEEB,side:THREE.BackSide})));
}
createEnv();
// Lighting
function setupLights() {
const sun=new THREE.DirectionalLight(0xffffdd,1);
sun.position.set(10,100,10);
sun.castShadow=true;
sun.shadow.mapSize.width=sun.shadow.mapSize.height=2048;
scene.add(sun,new THREE.AmbientLight(0x404040,0.3),
new THREE.DirectionalLight(0x556688,0.3).position.set(-10,30,-30),
new THREE.DirectionalLight(0x445588,0.4).position.set(0,30,30));
}
setupLights();
// Game state
let speed=0,maxSpeed=80,pos=0,score=0,gameStarted=false,leanAngle=0,lastTime=0,boost=1,isBoosting=false;
const keys={ArrowUp:false,ArrowDown:false,ArrowLeft:false,ArrowRight:false,' ':false};
window.addEventListener('keydown',e=>keys[e.key]=true);
window.addEventListener('keyup',e=>keys[e.key]=false);
document.getElementById('start-btn').addEventListener('click',()=>{
document.getElementById('start-screen').style.opacity=0;
setTimeout(()=>{document.getElementById('start-screen').style.display='none';gameStarted=true},500);
});
window.addEventListener('resize',()=>{
camera.aspect=window.innerWidth/window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth,window.innerHeight);
});
// Animation loop
function animate(t) {
requestAnimationFrame(animate);
const dt=Math.min(0.1,(t-lastTime)/1000); lastTime=t;
if(gameStarted) {
if(keys.ArrowUp) speed=Math.min(maxSpeed,speed+dt*15);
if(keys.ArrowDown) speed=Math.max(0,speed-dt*15);
if(!keys.ArrowUp&&!keys.ArrowDown) speed=Math.max(0,speed*0.96);
isBoosting=false;
if(keys[' ']&&boost>0) {
isBoosting=true;
speed=Math.min(maxSpeed*1.5,speed+dt*30);
boost-=dt*0.5;
} else boost=Math.min(1,boost+dt*0.1);
if(keys.ArrowLeft) leanAngle=Math.max(-Math.PI/8,leanAngle-dt*2);
if(keys.ArrowRight) leanAngle=Math.min(Math.PI/8,leanAngle+dt*2);
if(!keys.ArrowLeft&&!keys.ArrowRight) leanAngle*=1-dt*5;
pos+=speed*dt*0.005;
updateCamera(dt);
score+=Math.floor(speed*dt*2);
updateUI();
}
renderer.render(scene,camera);
}
animate(0);
function updateCamera(dt) {
const pt=path.getPointAt(pos%1),tan=path.getTangentAt(pos%1).normalize();
const normal=new THREE.Vector3(0,1,0),binormal=new THREE.Vector3().crossVectors(tan,normal).normalize();
const leanOffset=binormal.multiplyScalar(leanAngle*3);
camera.position.copy(pt).add(leanOffset).add(new THREE.Vector3(0,1.7,0));
camera.lookAt(pt.clone().add(tan.multiplyScalar(10)));
camera.rotation.z=leanAngle;
if(speed>maxSpeed*0.9)camera.position.y+=Math.sin(t*0.03)*0.1;
}
function updateUI() {
document.getElementById('score').textContent=Math.floor(score);
document.getElementById('speed').textContent=Math.floor(speed)+' km/h';
document.getElementById('speed-fill').style.width=(speed/maxSpeed)*100+'%';
const speedColor=speed>maxSpeed*0.9?'#ff0000':speed>maxSpeed*0.7?'#ff9900':'#4CAF50';
document.getElementById('speed').style.color=speedColor;
document.getElementById('boost-fill').style.width=boost*100+'%';
document.getElementById('boost-text').textContent=boost<=0?"Recharging":isBoosting?"Active!":boost>=1?"Ready!":"Available";
document.getElementById('effects').style.opacity=Math.min(1,speed/maxSpeed)*0.8;
}
</script>
</body>
</html>