🎮ArcadeLab

碰碰赛车竞速|网页版

by SparkHero40
179 lines5.2 KB
▶ Play
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>碰碰赛车竞速|网页版</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{background:#222;color:#fff;font-family:system-ui,Arial;overflow:hidden;touch-action:none}
#game{display:block;margin:0 auto;background:#444}
.info{text-align:center;padding:8px;font-size:15px}
.ctrl{display:flex;width:100%;position:fixed;bottom:12px;gap:10px;padding:0 12px;display:none}
.btn{flex:1;height:70px;background:#36f;border:none;border-radius:12px;color:#fff;font-size:22px;font-weight:bold;user-select:none;-webkit-user-select:none}
.btn:active{background:#24d}
@media (max-width:768px){
  .ctrl{display:flex;}
}
</style>
</head>
<body>
<div class="info">电脑:WASD / 方向键|手机:底部触屏按钮,撞开对手拿分</div>
<canvas id="game"></canvas>
<div class="ctrl">
<button class="btn" id="btnLeft">←</button>
<button class="btn" id="btnGas">加速</button>
<button class="btn" id="btnBrake">减速</button>
<button class="btn" id="btnRight">→</button>
</div>

<script>
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
function resize(){
  if(window.innerWidth > 768){
    canvas.width = 600;
    canvas.height = 800;
  }else{
    canvas.width = Math.min(window.innerWidth, 420);
    canvas.height = window.innerHeight - 110;
  }
}
resize();
window.addEventListener('resize',resize);

const roadW = 240;
const roadX = ()=>(canvas.width - roadW)/2;

const player = {
  x: roadX() + roadW/2 - 20,
  y: canvas.height - 120,
  w: 40,
  h: 70,
  speed: 0,
  maxSpeed:7,
  turn:0
};

const keys = {left:false,right:false,gas:false,brake:false};
const cars = [];
for(let i=0;i<5;i++){
  cars.push({
    x: roadX()+30 + (i*45)%(roadW-60),
    y: -120*i - 80,
    w:40,h:70,
    speed:2.5+Math.random()*2,
    color:['#ff4444','#4488ff','#44dd44','#dddd44','#dd44dd'][i]
  })
}
let roadOffset = 0;
let score = 0;

//触屏按钮
const btnLeft = document.getElementById('btnLeft');
const btnRight = document.getElementById('btnRight');
const btnGas = document.getElementById('btnGas');
const btnBrake = document.getElementById('btnBrake');

function bindTouch(btn,key){
  btn.addEventListener('touchstart',e=>{e.preventDefault();keys[key]=true})
  btn.addEventListener('touchend',e=>{e.preventDefault();keys[key]=false})
}
bindTouch(btnLeft,'left');
bindTouch(btnRight,'right');
bindTouch(btnGas,'gas');
bindTouch(btnBrake,'brake');

//键盘
document.addEventListener('keydown',e=>{
  if(e.code==='KeyA'||e.code==='ArrowLeft') keys.left=true;
  if(e.code==='KeyD'||e.code==='ArrowRight') keys.right=true;
  if(e.code==='KeyW'||e.code==='ArrowUp') keys.gas=true;
  if(e.code==='KeyS'||e.code==='ArrowDown') keys.brake=true;
})
document.addEventListener('keyup',e=>{
  if(e.code==='KeyA'||e.code==='ArrowLeft') keys.left=false;
  if(e.code==='KeyD'||e.code==='ArrowRight') keys.right=false;
  if(e.code==='KeyW'||e.code==='ArrowUp') keys.gas=false;
  if(e.code==='KeyS'||e.code==='ArrowDown') keys.brake=false;
})

function rectCollision(a,b){
  return a.x < b.x+b.w && a.x+a.w > b.x && a.y < b.y+b.h && a.y+a.h > b.y;
}

function loop(){
  const rx = roadX();
  if(keys.gas) player.speed = Math.min(player.speed+0.15, player.maxSpeed);
  else if(keys.brake) player.speed = Math.max(player.speed-0.15, -2);
  else player.speed *= 0.96;

  if(keys.left) player.turn = -0.04;
  else if(keys.right) player.turn = 0.04;
  else player.turn *= 0.85;

  player.x += player.turn * 32;
  player.x = Math.max(rx, Math.min(rx+roadW-player.w, player.x));

  roadOffset += player.speed * 4;
  if(roadOffset>100) roadOffset=0;
  score += player.speed * 0.08;

  for(let c of cars){
    c.y += c.speed*4 - player.speed*4;
    if(Math.random()<0.007){
      c.x += (Math.random()>0.5?1:-1)*40;
      c.x = Math.max(rx, Math.min(rx+roadW - c.w, c.x));
    }
    if(c.y>canvas.height){
      c.y = -100;
      c.x = rx + 20 + Math.random()*(roadW-60);
      c.speed = 2.5 + Math.random()*2.2;
    }
    if(rectCollision(player,c)){
      player.speed *= 0.7;
      c.speed *= 0.6;
      const dx = player.x - c.x;
      if(dx>0){
        player.x +=5;c.x -=5;
      }else{
        player.x -=5;c.x +=5;
      }
    }
  }

  ctx.fillStyle='#222';
  ctx.fillRect(0,0,canvas.width,canvas.height);
  ctx.fillStyle='#444';
  ctx.fillRect(rx,0,roadW,canvas.height);
  ctx.strokeStyle='#fff';
  ctx.lineWidth=4;
  ctx.setLineDash([40,30]);
  ctx.beginPath();
  ctx.moveTo(rx+roadW/2, roadOffset);
  ctx.lineTo(rx+roadW/2, canvas.height);
  ctx.stroke();
  ctx.setLineDash([]);

  for(let c of cars){
    ctx.fillStyle=c.color;
    ctx.fillRect(c.x,c.y,c.w,c.h);
    ctx.fillStyle='#222';
    ctx.fillRect(c.x+8,c.y+10,c.w-16,18);
  }
  ctx.fillStyle='#2288ff';
  ctx.fillRect(player.x,player.y,player.w,player.h);
  ctx.fillStyle='#88ddff';
  ctx.fillRect(player.x+7,player.y+12,player.w-14,20);

  ctx.fillStyle='white';
  ctx.font='20px Arial';
  ctx.fillText('分数:'+Math.floor(score),12,30);

  requestAnimationFrame(loop);
}
loop();
</script>
</body>
</html>

Game Source: 碰碰赛车竞速|网页版

Creator: SparkHero40

Libraries: none

Complexity: moderate (179 lines, 5.2 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: game-sparkhero40" to link back to the original. Then publish at arcadelab.ai/publish.