OutRun / index.html
Novaciano's picture
Update index.html
f014153 verified
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>OutRun con Rivales visibles</title>
<style>
body{margin:0;overflow:hidden;background:#6dd5fa;font-family:sans-serif;touch-action:none;}
canvas{display:block;width:100vw;height:80vh;background:skyblue;}
#controls{position:fixed;bottom:0;width:100%;display:flex;justify-content:space-around;padding:10px;background:rgba(0,0,0,0.3);}
.btn{width:70px;height:70px;border-radius:50%;border:none;background:rgba(255,255,255,0.7);font-size:24px;font-weight:bold;user-select:none;}
#hud{position:fixed;top:5px;left:0;right:0;display:flex;justify-content:space-around;font-size:20px;font-weight:bold;color:white;text-shadow:2px 2px 3px black;}
</style>
</head>
<body>
<canvas id="game"></canvas>
<div id="hud">
<div id="score">SCORE: 0</div>
<div id="time">TIME: 60</div>
<div id="speed">SPEED: 0 km/h</div>
</div>
<div id="controls">
<button class="btn" id="left"></button>
<button class="btn" id="right"></button>
<button class="btn" id="up"></button>
<button class="btn" id="down"></button>
</div>
<script>
// ===== SETUP =====
const canvas=document.getElementById("game"),ctx=canvas.getContext("2d");
function resize(){canvas.width=window.innerWidth;canvas.height=window.innerHeight*0.8;}
resize();window.addEventListener("resize",resize);
let roadWidth=2000,segL=200,camD=0.84;
let playerX=0,pos=0,speed=200;
let score=0,timeLeft=60;
let keys={left:false,right:false,up:false,down:false};
let segments=[];
// carretera
for(let i=0;i<500;i++){segments.push({i,curve:Math.sin(i/30)*2,y:i*segL});}
// sprites
let palmImg=new Image(); palmImg.src="https://i.ibb.co/6Hw6yHr/palm.png";
let carImg=new Image(); carImg.src="https://i.ibb.co/VVvTqYm/car.png";
// rivales
let rivals=[];
function spawnRival(){
let lane=(Math.random()*2-1)*0.8; // carriles
let z=pos+2000+Math.random()*3000;
let spd=400+Math.random()*400;
rivals.push({x:lane,z,spd});
}
setInterval(spawnRival,3000);
// ===== INPUT =====
window.addEventListener("keydown",e=>{
if(e.key==="ArrowLeft")keys.left=true;
if(e.key==="ArrowRight")keys.right=true;
if(e.key==="ArrowUp")keys.up=true;
if(e.key==="ArrowDown")keys.down=true;
});
window.addEventListener("keyup",e=>{
if(e.key==="ArrowLeft")keys.left=false;
if(e.key==="ArrowRight")keys.right=false;
if(e.key==="ArrowUp")keys.up=false;
if(e.key==="ArrowDown")keys.down=false;
});
function bindTouch(id,key){
let el=document.getElementById(id);
el.addEventListener("touchstart",()=>keys[key]=true);
el.addEventListener("touchend",()=>keys[key]=false);
}
bindTouch("left","left"); bindTouch("right","right");
bindTouch("up","up"); bindTouch("down","down");
// ===== CORE =====
function project(p,camX,camY,camZ){
let dz=p.z-camZ; if(dz<=0.1)dz=0.1; // evita división por cero
let dx=p.x-camX,dy=p.y-camY;
let scale=camD/dz;
return {x:(1+scale*dx)*canvas.width/2,y:(1-scale*dy)*canvas.height/2,w:scale*roadWidth/2};
}
function drawSegment(p1,p2,color){
ctx.fillStyle=color.grass; ctx.fillRect(0,p2.y,canvas.width,p1.y-p2.y);
ctx.fillStyle=color.road;
ctx.beginPath();
ctx.moveTo(p1.x-p1.w,p1.y); ctx.lineTo(p1.x+p1.w,p1.y);
ctx.lineTo(p2.x+p2.w,p2.y); ctx.lineTo(p2.x-p2.w,p2.y);
ctx.fill();
}
function drawSprite(img,scale,x,y,w,h){ctx.drawImage(img,x-w*scale/2,y-h*scale,w*scale,h*scale);}
// ===== UPDATE =====
function update(dt){
pos+=speed*dt; if(pos>=segments.length*segL)pos-=segments.length*segL;
if(keys.left)playerX-=0.02;
if(keys.right)playerX+=0.02;
if(keys.up)speed+=5;
if(keys.down)speed-=5;
if(speed<0)speed=0;if(speed>2000)speed=2000;
for(let r of rivals){r.z-= (speed-r.spd)*dt;}
rivals=rivals.filter(r=>r.z>pos&&r.z<pos+8000);
timeLeft-=dt;if(timeLeft<=0){timeLeft=0;speed=0;}
score+=Math.floor(speed*dt*0.1);
document.getElementById("score").textContent="SCORE: "+score;
document.getElementById("time").textContent="TIME: "+Math.floor(timeLeft);
document.getElementById("speed").textContent="SPEED: "+Math.floor(speed/10)+" km/h";
}
// ===== RENDER =====
function render(){
ctx.clearRect(0,0,canvas.width,canvas.height);
let base=Math.floor(pos/segL),camY=1000,camZ=pos+500;
let x=0,dx=0;
for(let n=0;n<300;n++){
let seg=segments[(base+n)%segments.length];
seg.z=seg.y-pos; x+=dx; dx+=seg.curve*0.001;
let p1=project({x:x,y:0,z:seg.z},playerX*roadWidth,camY,camZ);
let p2=project({x:x+dx,y:0,z:seg.z+segL},playerX*roadWidth,camY,camZ);
let color={road:n%2?"#707070":"#696969",grass:n%2?"#10aa10":"#009900",rumble:"#fff"};
if(p1.y>=p2.y&&p2.y<canvas.height)drawSegment(p1,p2,color);
if(n%20===0&&palmImg.complete){
let s=p1.w/250;
drawSprite(palmImg,s,p1.x-p1.w*1.5,p1.y,200,200);
drawSprite(palmImg,s,p1.x+p1.w*1.5,p1.y,200,200);
}
}
// rivales visibles
for(let r of rivals){
let dz=r.z-pos; if(dz<=0)continue;
let proj=project({x:r.x*roadWidth,y:0,z:dz},playerX*roadWidth,1000,pos+500);
if(carImg.complete){
let s=proj.w/350;
drawSprite(carImg,s,proj.x,proj.y,120,60);
}
}
// auto jugador
ctx.fillStyle="red";
ctx.fillRect(canvas.width/2-20+playerX*200,canvas.height-80,40,60);
}
// ===== LOOP =====
let last=0;
function loop(ts){let dt=(ts-last)/1000;if(dt>0.05)dt=0.05;last=ts;update(dt);render();requestAnimationFrame(loop);}
requestAnimationFrame(loop);
</script>
</body>
</html>