未命名 sHdADnedit icon

创建者:
邓朝元
Fork(复制)
下载
嵌入
BUG反馈
index.html
index.html
            
            <!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>Dino Runner</title>
<style>
  body{margin:0;background:#f7f7f7;display:flex;align-items:center;justify-content:center;height:100vh;font-family:"Courier New",monospace}
  canvas{background:#f7f7f7;image-rendering:pixelated}
</style>
</head>
<body>
<canvas id="game" width="600" height="200"></canvas>
<script>
const cv=document.getElementById('game'),ctx=cv.getContext('2d');
const W=600,H=200,GROUND=170;
const GRAVITY=0.6,JUMP_V=10.25;

let state='idle',speed=6,score=0,hiScore=+localStorage.getItem('dinoHi')||0;
let obstacles=[],clouds=[],groundTicks=[],frame=0,night=false,nightAlpha=0;

const dino={x:40,y:GROUND,w:44,h:47,vy:0,ducking:false,dead:false};

// ---- 输入 ----
const keys={};
addEventListener('keydown',e=>{
  if(e.code==='Space'||e.code==='ArrowUp'||e.code==='ArrowDown')e.preventDefault();
  if(state==='idle'&&(e.code==='Space'||e.code==='ArrowUp'))start();
  if(state==='over'&&e.code==='Space')start();
  keys[e.code]=true;
});
addEventListener('keyup',e=>keys[e.code]=false);
cv.addEventListener('pointerdown',()=>{ if(state!=='run')start(); else jump(); });

function start(){
  state='run';speed=6;score=0;frame=0;night=false;nightAlpha=0;
  obstacles=[];clouds=[];groundTicks=[];
  for(let i=0;i<20;i++)groundTicks.push({x:Math.random()*W,y:GROUND+8+Math.random()*10,w:6+Math.random()*12});
  dino.y=GROUND;dino.vy=0;dino.ducking=false;dino.dead=false;
  dino.h=47;dino.w=44;
}
function jump(){
  if(dino.y>=GROUND&&!dino.dead){dino.vy=-JUMP_V;dino.ducking=false;dino.h=47;dino.w=44;}
}
function beep(){
  try{const a=new AudioContext(),o=a.createOscillator(),g=a.createGain();
  o.connect(g);g.connect(a.destination);o.frequency.value=880;
  g.gain.setValueAtTime(.15,a.currentTime);g.gain.exponentialRampToValueAtTime(.001,a.currentTime+.15);
  o.start();o.stop(a.currentTime+.15);}catch(e){}
}

// ---- 障碍生成 ----
let nextSpawn=60;
function spawn(){
  const r=Math.random();
  if(score>150&&r<0.25){ // 翼龙
    const heights=[GROUND-62,GROUND-32,GROUND-4];
    obstacles.push({type:'bird',x:W+30,y:heights[Math.floor(Math.random()*heights.length)],w:42,h:30,flap:0});
  }else{
    const big=Math.random()<0.5,n=1+Math.floor(Math.random()*3);
    const cw=big?25:17,ch=big?50:35;
    obstacles.push({type:'cactus',x:W+30,y:GROUND-ch,w:cw*n,h:ch,cw,n,big});
  }
  nextSpawn=40+Math.random()*60+(300/speed);
}

// ---- 更新 ----
function update(){
  frame++;
  speed=Math.min(13,6+score/1500*3);
  score+=speed*0.05;
  if(Math.floor(score)%100===0&&Math.floor(score)!==Math.floor(score-speed*0.05))beep();

  // 昼夜切换(每700分)
  const target=(Math.floor(score/700)%2===1)?1:0;
  nightAlpha+=(target-nightAlpha)*0.02;

  // 恐龙
  if(keys.Space||keys.ArrowUp)jump();
  dino.ducking=keys.ArrowDown&&dino.y>=GROUND;
  dino.h=dino.ducking?26:47; dino.w=dino.ducking?58:44;
  dino.vy+=keys.ArrowDown&&dino.y<GROUND?GRAVITY*2.5:GRAVITY;
  dino.y+=dino.vy;
  if(dino.y>GROUND){dino.y=GROUND;dino.vy=0;}

  // 地面
  groundTicks.forEach(g=>{g.x-=speed;if(g.x<-20){g.x=W;g.y=GROUND+8+Math.random()*10;}});
  // 云
  if(frame%150===0)clouds.push({x:W,y:20+Math.random()*60});
  clouds.forEach(c=>c.x-=speed*0.3);
  clouds=clouds.filter(c=>c.x>-60);

  // 障碍
  nextSpawn-=speed;
  if(nextSpawn<=0)spawn();
  obstacles.forEach(o=>{o.x-=speed;if(o.type==='bird')o.flap++;});
  obstacles=obstacles.filter(o=>o.x>-100);

  // 碰撞(缩小判定框)
  const pad=6;
  const dx=dino.x+pad,dy=dino.y,dw=dino.w-pad*2,dh=dino.h-pad;
  for(const o of obstacles){
    let ow=o.w;
    if(o.type==='bird')ow=36;
    if(dx<o.x+ow&&dx+dw>o.x&&dy<o.y+o.h&&dy+dh>o.y){
      state='over';dino.dead=true;
      hiScore=Math.max(hiScore,Math.floor(score));
      localStorage.setItem('dinoHi',hiScore);
    }
  }
}

// ---- 绘制 ----
function drawDino(){
  ctx.save();ctx.translate(dino.x,dino.y);
  const legFrame=Math.floor(frame/6)%2;
  if(dino.ducking){
    ctx.fillRect(0,8,58,18);           // 身体(趴下)
    ctx.fillRect(44,2,16,12);           // 头
    ctx.fillStyle='#fff';ctx.fillRect(52,4,3,3);ctx.fillStyle='#535353';
    ctx.fillRect(60,8,8,4);             // 嘴
    if(state==='run')ctx.fillRect(legFrame?8:20,26,6,6);else ctx.fillRect(8,26,6,6),ctx.fillRect(20,26,6,6);
  }else{
    ctx.fillRect(8,0,26,30);            // 身体
    ctx.fillRect(20,-18,22,18);         // 头
    ctx.fillStyle='#fff';ctx.fillRect(32,-12,3,3);ctx.fillStyle='#535353';
    ctx.fillRect(40,-12,8,5);           // 嘴
    ctx.fillRect(0,10,10,14);           // 尾巴
    ctx.fillRect(14,30,6,4);ctx.fillRect(24,30,6,4);
    if(state==='run'&&dino.y>=GROUND)
      ctx.fillRect(legFrame?14:24,34,6,13);
    else{ctx.fillRect(14,34,6,13);ctx.fillRect(24,34,6,13);}
  }
  if(dino.dead){ // X眼
    ctx.fillStyle='#f7f7f7';ctx.fillRect(30,-14,7,7);
    ctx.fillStyle='#535353';
    ctx.fillRect(30,-14,2,2);ctx.fillRect(35,-14,2,2);ctx.fillRect(32,-12,2,2);
    ctx.fillRect(30,-9,2,2);ctx.fillRect(35,-9,2,2);
  }
  ctx.restore();
}
function drawCactus(o){
  for(let i=0;i<o.n;i++){
    const x=o.x+i*o.cw;
    ctx.fillRect(x+o.cw/2-3,o.y,6,o.h);                 // 主干
    ctx.fillRect(x,o.y+o.h*0.3,4,o.h*0.3);ctx.fillRect(x,o.y+o.h*0.3,o.cw*0.4,4);
    ctx.fillRect(x+o.cw-4,o.y+o.h*0.15,4,o.h*0.3);ctx.fillRect(x+o.cw*0.6,o.y+o.h*0.15,o.cw*0.4,4);
  }
}
function drawBird(o){
  const up=Math.floor(o.flap/12)%2===0;
  ctx.save();ctx.translate(o.x,o.y);
  ctx.fillRect(6,12,30,8);                    // 身体
  ctx.fillRect(36,8,10,6);ctx.fillRect(44,10,6,3); // 头嘴
  if(up)ctx.fillRect(14,0,6,12);else ctx.fillRect(14,20,6,12); // 翅膀
  ctx.restore();
}

function draw(){
  // 昼夜
  ctx.fillStyle=`rgb(${247-nightAlpha*197},${247-nightAlpha*197},${247-nightAlpha*167})`;
  ctx.fillRect(0,0,W,H);
  ctx.fillStyle=`rgb(${83+nightAlpha*140},${83+nightAlpha*140},${83+nightAlpha*140})`;
  // 云
  clouds.forEach(c=>{ctx.fillRect(c.x,c.y,40,10);ctx.fillRect(c.x+8,c.y-6,24,8);});
  // 地面线
  ctx.fillRect(0,GROUND+47,W,2);
  groundTicks.forEach(g=>ctx.fillRect(g.x,g.y,g.w,2));
  // 障碍
  obstacles.forEach(o=>o.type==='cactus'?drawCactus(o):drawBird(o));
  drawDino();
  // 分数
  const s=String(Math.floor(score)).padStart(5,'0');
  ctx.font='bold 16px "Courier New"';ctx.textAlign='right';
  ctx.fillText('HI '+String(hiScore).padStart(5,'0')+'  '+s,W-15,28);
  if(state==='idle'){ctx.textAlign='center';ctx.fillText('按空格开始',W/2,90);}
  if(state==='over'){ctx.textAlign='center';ctx.font='bold 22px "Courier New"';
    ctx.fillText('G A M E  O V E R',W/2,80);
    ctx.font='14px "Courier New"';ctx.fillText('按空格重新开始',W/2,105);}
  ctx.textAlign='left';
}

(function loop(){
  if(state==='run')update();
  draw();
  requestAnimationFrame(loop);
})();
</script>
</body>
</html>
        
编辑器加载中
预览
控制台