File size: 5,401 Bytes
1289656
 
 
 
f014153
1289656
f014153
 
 
 
 
1289656
 
 
 
046ea31
 
 
 
 
1289656
 
 
149c1bb
 
1289656
 
 
f014153
 
 
 
f1d61eb
f014153
 
 
 
 
627df7c
f014153
 
149c1bb
046ea31
f014153
 
046ea31
 
f014153
046ea31
f014153
 
 
046ea31
 
f014153
149c1bb
f014153
1289656
f014153
 
 
 
1289656
 
f014153
 
 
 
f1d61eb
149c1bb
 
 
 
 
f014153
 
7e9f5dd
f014153
 
 
 
 
 
149c1bb
 
046ea31
149c1bb
 
046ea31
 
149c1bb
 
f014153
149c1bb
f014153
149c1bb
f014153
 
 
 
 
 
 
 
 
 
046ea31
 
 
149c1bb
7e9f5dd
f014153
149c1bb
 
f014153
149c1bb
 
 
046ea31
149c1bb
 
f014153
 
 
 
 
 
 
149c1bb
f014153
046ea31
f014153
 
 
 
 
 
046ea31
 
7e9f5dd
f014153
7e9f5dd
 
f014153
149c1bb
f014153
149c1bb
1289656
 
aaac8e1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
<!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>