Download index.html from Novaciano/OutRun: direct link, hf CLI and curl.
- Browser
- Download file 5.4 kB
-
https://huggingface.co/spaces/Novaciano/OutRun/resolve/main/index.html
- Command line
-
hf download hf://spaces/Novaciano/OutRun/index.html
-
curl -L -o index.html https://huggingface.co/spaces/Novaciano/OutRun/resolve/main/index.html
5.4 kB
| <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> | |