🎮ArcadeLab

晶块探险家

by PixelCaptain55
204 lines5.4 KB
▶ Play
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0,user-scalable=no">
<title>晶块探险家</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{background:#222;overflow:hidden}
canvas{display:block;margin:0 auto;background:#67b6ff}
</style>
</head>
<body>
<canvas id="game"></canvas>
<script>
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
canvas.width = 800;
canvas.height = 450;

//玩家
const player = {
  x:50,y:300,w:32,h:40,
  vx:0,vy:0,speed:4,jumpPower:-12,gravity:0.5,
  grounded:false,big:false,hp:1,score:0
}
//按键
const keys = {left:false,right:false,jump:false}
//平台
const platforms = [
  {x:0,y:400,w:800,h:50},
  {x:180,y:320,w:120,h:20},
  {x:350,y:260,w:100,h:20},
  {x:520,y:200,w:130,h:20},
  {x:650,y:300,w:80,h:20}
]
//能量晶块(替代金币)
const crystals = [
  {x:220,y:280,collected:false},
  {x:380,y:220,collected:false},
  {x:560,y:160,collected:false},
  {x:680,y:260,collected:false}
]
//孢子(替代蘑菇)
const spores = [
  {x:200,y:300,type:"grow",alive:true},
  {x:540,y:180,type:"dash",alive:true}
]
//史莱姆敌人
const slimes = [
  {x:300,y:370,w:36,h:30,vx:-1,alive:true}
]
//终点传送门
const goal = {x:720,y:340,w:50,h:60}

//碰撞检测
function rectOverlap(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(){
  //移动
  if(keys.left) player.vx = -player.speed
  else if(keys.right) player.vx = player.speed
  else player.vx = 0
  if(keys.jump && player.grounded){
    player.vy = player.jumpPower; player.grounded=false;
  }
  player.vy += player.gravity;
  player.x += player.vx;
  player.y += player.vy;

  //边界
  if(player.x<0) player.x=0;
  if(player.x>canvas.width-player.w) player.x=canvas.width-player.w;

  //平台碰撞
  player.grounded = false;
  for(let p of platforms){
    if(rectOverlap(player,p)){
      if(player.vy>0 && player.y+player.h-player.vy <= p.y){
        player.y = p.y-player.h;
        player.vy=0;
        player.grounded=true;
      }
    }
  }

  //收集晶块
  for(let c of crystals){
    if(!c.collected && rectOverlap(player,{x:c.x,y:c.y,w:24,h:24})){
      c.collected=true; player.score+=100;
    }
  }

  //拾取孢子
  for(let s of spores){
    if(s.alive && rectOverlap(player,{x:s.x,y:s.y,w:28,h:28})){
      s.alive=false;
      if(s.type==="grow"){
        player.big=true; player.hp=2;
      }else{
        player.x += 80; //瞬移冲刺
      }
    }
  }

  //史莱姆
  for(let sl of slimes){
    if(!sl.alive) continue;
    sl.x += sl.vx;
    if(sl.x<50 || sl.x>700) sl.vx *= -1;
    //踩头
    if(rectOverlap(player,sl)){
      if(player.vy>0 && player.y+player.h < sl.y+sl.h/2){
        sl.alive=false; player.vy=-8; player.score+=200;
      }else{
        //被撞到
        if(player.big){
          player.big=false; player.hp=1;
        }else{
          alert("游戏结束!得分:"+player.score);
          location.reload();
        }
      }
    }
  }

  //到达终点
  if(rectOverlap(player,goal)){
    alert("🎉通关!得分:"+player.score);
    location.reload();
  }

  //渲染
  ctx.clearRect(0,0,canvas.width,canvas.height);
  //平台
  ctx.fillStyle="#7b5428";
  platforms.forEach(p=>ctx.fillRect(p.x,p.y,p.w,p.h));
  //晶块
  ctx.fillStyle="#33ccff";
  crystals.forEach(c=>{
    if(!c.collected){
      ctx.beginPath(); ctx.arc(c.x+12,c.y+12,12,0,Math.PI*2); ctx.fill();
    }
  })
  //孢子
  spores.forEach(s=>{
    if(s.alive){
      ctx.fillStyle = s.type=="grow"?"#38d050":"#c760ff";
      ctx.beginPath(); ctx.arc(s.x+14,s.y+14,14,0,Math.PI*2); ctx.fill();
    }
  })
  //史莱姆
  ctx.fillStyle="#44bb44";
  slimes.forEach(sl=>{
    if(sl.alive) ctx.fillRect(sl.x,sl.y,sl.w,sl.h);
  })
  //传送门终点
  ctx.fillStyle="#9944ff";
  ctx.fillRect(goal.x,goal.y,goal.w,goal.h);
  //玩家
  ctx.fillStyle="#ff4444";
  if(player.big) ctx.fillRect(player.x,player.y,player.w+8,player.h+8);
  else ctx.fillRect(player.x,player.y,player.w,player.h);
  //分数
  ctx.fillStyle="#fff";
  ctx.font="24px sans-serif";
  ctx.fillText("得分:"+player.score,20,30);

  requestAnimationFrame(loop);
}

//键盘控制
document.addEventListener('keydown',e=>{
  if(e.key==="ArrowLeft") keys.left=true;
  if(e.key==="ArrowRight") keys.right=true;
  if(e.key==="ArrowUp") keys.jump=true;
})
document.addEventListener('keyup',e=>{
  if(e.key==="ArrowLeft") keys.left=false;
  if(e.key==="ArrowRight") keys.right=false;
  if(e.key==="ArrowUp") keys.jump=false;
})

//手机触屏按钮(左右跳)
let touchDiv = document.createElement("div");
touchDiv.style="position:fixed;bottom:10px;left:0;right:0;display:flex;justify-content:space-between;padding:0 20px";
let btnL=document.createElement("button");btnL.innerText="←";btnL.style="width:70px;height:70px;font-size:30px";
let btnR=document.createElement("button");btnR.innerText="→";btnR.style="width:70px;height:70px;font-size:30px";
let btnJ=document.createElement("button");btnJ.innerText="跳";btnJ.style="width:70px;height:70px;font-size:30px";
touchDiv.appendChild(btnL);touchDiv.appendChild(btnR);touchDiv.appendChild(btnJ);
document.body.appendChild(touchDiv);

btnL.ontouchstart=()=>keys.left=true; btnL.ontouchend=()=>keys.left=false;
btnR.ontouchstart=()=>keys.right=true; btnR.ontouchend=()=>keys.right=false;
btnJ.ontouchstart=()=>keys.jump=true; btnJ.ontouchend=()=>keys.jump=false;

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

Game Source: 晶块探险家

Creator: PixelCaptain55

Libraries: none

Complexity: complex (204 lines, 5.4 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-pixelcaptain55" to link back to the original. Then publish at arcadelab.ai/publish.