🎮ArcadeLab

第一人称迷宫闯关|金币商城(触屏版)

by WildComet34
326 lines11.9 KB
▶ Play
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>第一人称迷宫闯关|金币商城(触屏版)</title>
<style>
*{box-sizing:border-box;margin:0;padding:0;font-family:"Microsoft Yahei",sans-serif}
body{background:#222;color:#fff;overflow:hidden;touch-action:none}
canvas{display:block}
.ui{position:absolute;top:10px;left:10px;z-index:10}
button{padding:8px 14px;margin:4px;border:none;border-radius:6px;background:#2185d0;color:#fff;font-size:16px;cursor:pointer}
button:hover{background:#1678c2}
#menu,#shop,#inv{position:absolute;width:100vw;height:100vh;background:rgba(20,20,20,0.95);display:none;z-index:20;padding:30px;overflow-y:auto}
h2{color:#ffd700;margin-bottom:20px;font-size:36px}
.item{padding:12px 0;border-bottom:#444 solid 1px}
.buyBtn{background:#28a745}
.useBtn{background:#9932cc;margin-left:8px}
.backBtn{background:#dc3545;margin-top:20px}
.goldText{font-size:22px;color:#ffd700;margin-bottom:20px}
#minimap{position:absolute;top:10px;right:10px;width:160px;height:160px;background:#000;border:2px solid #fff;display:none}
.tip{color:#87cefa;margin:8px 0}
/* 📱触屏按键样式 */
.touchPad{position:absolute;bottom:20px;left:20px;z-index:12}
.touchBtn{width:60px;height:60px;border-radius:50%;background:rgba(255,255,255,0.25);color:#fff;border:2px solid #fff;font-size:22px;margin:4px;user-select:none;-webkit-user-select:none}
.touchBtn:active{background:rgba(255,255,255,0.5)}
#touchControl{display:none}
</style>
</head>
<body>
<canvas id="gameCanvas"></canvas>
<div class="ui">
  <div id="gameUI" style="display:none">
    <div class="goldText" id="gameGold"></div>
    <div class="tip" id="activeBuff"></div>
    <div style="font-size:14px">W前进 S后退 ← →转向 ESC返回菜单</div>
  </div>
  <canvas id="minimap"></canvas>
</div>
<!-- 📱触屏虚拟按键 -->
<div id="touchControl" class="touchPad">
  <div><button class="touchBtn" id="btnUp">↑</button></div>
  <div><button class="touchBtn" id="btnLeft">←</button><button class="touchBtn" id="btnDown">↓</button><button class="touchBtn" id="btnRight">→</button></div>
</div>

<div id="menu">
  <h2>🏰 第一人称迷宫闯关</h2>
  <div class="goldText" id="menuGold"></div>
  <div><button onclick="startGame()">🎮 开始游戏</button></div>
  <div><button onclick="openShop()">🛒 商城</button></div>
  <div><button onclick="openInv()">📦 背包</button></div>
  <div><button onclick="closeAll()">❌ 退出</button></div>
</div>
<div id="shop">
  <h2>🛒 商城</h2>
  <div class="goldText" id="shopGold"></div>
  <div id="shopList"></div>
  <button class="backBtn" onclick="openMenu()">返回菜单</button>
</div>
<div id="inv">
  <h2>📦 背包(点击使用道具)</h2>
  <div id="invList"></div>
  <button class="backBtn" onclick="openMenu()">返回菜单</button>
</div>

<script>
const canvas = document.getElementById("gameCanvas");
const ctx = canvas.getContext("2d");
const miniCanvas = document.getElementById("minimap");
const miniCtx = miniCanvas.getContext("2d");
miniCanvas.width=160;miniCanvas.height=160;
function resizeCanvas(){canvas.width=window.innerWidth;canvas.height=window.innerHeight}
resizeCanvas();
window.addEventListener("resize",resizeCanvas);

// ========== 玩家数据 ==========
const player = {
  gold:0,
  inventory:[],
  buff:{
    speed:false,
    doubleGold:false,
    mapUnlock:false,
    farView:false,
    footPrint:false,
    bright:false
  }
};
const shopItems = [
  {id:1,name:"加速药水",price:20,desc:"移动速度+20%",type:"buffSpeed"},
  {id:2,name:"透视眼镜",price:35,desc:"视野看得更远",type:"buffFarView"},
  {id:3,name:"通关徽章",price:50,desc:"收藏装饰品",type:"collect"},
  {id:5,name:"小地图解锁卡",price:60,desc:"游戏内显示小地图",type:"buffMap"},
  {id:6,name:"迷宫提示箭",price:30,desc:"显示去往终点方向",type:"hintArrow"},
  {id:11,name:"双倍金币卡(一局)",price:70,desc:"本局通关拿20金币!",type:"buffDoubleGold"},
  {id:12,name:"重置骰子",price:25,desc:"重新生成当前迷宫",type:"resetMaze"},
  {id:13,name:"脚印追踪墨水",price:40,desc:"可以看见自己走过的路",type:"buffFoot"},
  {id:14,name:"夜光手电筒",price:55,desc:"亮度提升,看得更清楚",type:"bright"}
];

// ========== 迷宫 ==========
let maze,px,py,angle;
const cellSize = 40;
const mazeW = 21, mazeH =21;
let gameRunning = false;
const keys = {};
let walkedCells = new Set();

// 📱触屏按键监听
const btnUp = document.getElementById("btnUp");
const btnDown = document.getElementById("btnDown");
const btnLeft = document.getElementById("btnLeft");
const btnRight = document.getElementById("btnRight");
const touchControl = document.getElementById("touchControl");

function bindTouchButton(btn,keyName){
  btn.addEventListener("pointerdown",()=>{keys[keyName]=true})
  btn.addEventListener("pointerup",()=>{keys[keyName]=false})
  btn.addEventListener("pointerleave",()=>{keys[keyName]=false})
}
bindTouchButton(btnUp,"w");
bindTouchButton(btnDown,"s");
bindTouchButton(btnLeft,"ArrowLeft");
bindTouchButton(btnRight,"ArrowRight");

document.addEventListener("keydown",e=>{keys[e.key]=true;if(e.key==="Escape"&&gameRunning)openMenu()})
document.addEventListener("keyup",e=>keys[e.key]=false)

// 生成迷宫
function generateMaze(w,h){
  const m = Array.from({length:h},()=>Array(w).fill(1));
  function carve(x,y){
    m[y][x]=0;
    const dirs=[[0,-2],[0,2],[-2,0],[2,0]];
    dirs.sort(()=>Math.random()-0.5);
    for(let [dx,dy] of dirs){
      const nx=x+dx, ny=y+dy;
      if(nx>0&&nx<w-1&&ny>0&&ny<h-1&&m[ny][nx]===1){
        m[y+dy/2][x+dx/2]=0;
        carve(nx,ny);
      }
    }
  }
  carve(1,1);
  m[h-2][w-2]=0;
  return m;
}

// 射线渲染
function rayCast(){
  const w=canvas.width,h=canvas.height;
  const fov = Math.PI/3;
  const numRays=300;
  const maxDist = player.buff.farView?1200:900;
  for(let i=0;i<numRays;i++){
    const ra = angle - fov/2 + (i/numRays)*fov;
    let dist=0,hit=false;
    while(!hit&&dist<maxDist){
      dist++;
      const rx = px + Math.cos(ra)*dist;
      const ry = py + Math.sin(ra)*dist;
      const mx = Math.floor(rx/cellSize);
      const my = Math.floor(ry/cellSize);
      if(mx<0||mx>=maze[0].length||my<0||my>=maze.length||maze[my][mx]===1)hit=true;
    }
    const corrDist = dist*Math.cos(ra-angle);
    const wallH = h/(corrDist+1)*80;
    let baseShade = player.buff.bright?Math.max(80,255-corrDist*0.25):Math.max(30,255-corrDist*0.35);
    ctx.fillStyle=`rgb(${baseShade},${baseShade},${baseShade})`;
    const sx = i*(w/numRays);
    ctx.fillRect(sx,(h-wallH)/2,3,wallH);
  }
}
function drawMinimap(){
  if(!player.buff.mapUnlock) return;
  miniCtx.clearRect(0,0,160,160);
  const scale=160/Math.max(mazeW,mazeH);
  for(let y=0;y<mazeH;y++){
    for(let x=0;x<mazeW;x++){
      if(maze[y][x]===1) miniCtx.fillStyle="#777";
      else miniCtx.fillStyle="#222";
      miniCtx.fillRect(x*scale,y*scale,scale-1,scale-1);
    }
  }
  const pmx = px/cellSize, pmy = py/cellSize;
  miniCtx.fillStyle="#0f0";
  miniCtx.fillRect(pmx*scale-2,pmy*scale-2,5,5);
}
function gameLoop(){
  if(!gameRunning)return;
  const baseSpd = 2.5;
  const moveSpd = player.buff.speed ? baseSpd*1.2 : baseSpd;
  const rotSpd=0.045;
  if(keys["ArrowLeft"])angle-=rotSpd;
  if(keys["ArrowRight"])angle+=rotSpd;
  let dx=0,dy=0;
  if(keys["w"]||keys["W"]){
    dx += Math.cos(angle)*moveSpd;
    dy += Math.sin(angle)*moveSpd;
  }
  if(keys["s"]||keys["S"]){
    dx -= Math.cos(angle)*moveSpd;
    dy -= Math.sin(angle)*moveSpd;
  }
  const nx = px+dx, ny=py+dy;
  const mapX = Math.floor(nx/cellSize);
  const mapY = Math.floor(ny/cellSize);
  if(mapX>=0&&mapX<maze[0].length&&mapY>=0&&mapY<maze.length&&maze[mapY][mapX]===0){
    px=nx;py=ny;
  }
  // ✅到达终点 拿金币!普通通关+10金币
  const goalX = (mazeW-2)*cellSize;
  const goalY = (mazeH-2)*cellSize;
  if(Math.hypot(px-goalX,py-goalY) < cellSize){
    const reward = player.buff.doubleGold ? 20 :10;
    player.gold += reward;
    alert(`🎉通关!获得 ${reward} 金币!`)
    resetBuff();
    openMenu();
    return;
  }
  ctx.fillStyle="#000";ctx.fillRect(0,0,canvas.width,canvas.height);
  rayCast();
  drawMinimap();
  let buffText = "";
  if(player.buff.speed) buffText+="✅加速 ";
  if(player.buff.farView) buffText+="✅远视 ";
  if(player.buff.mapUnlock) buffText+="✅小地图 ";
  if(player.buff.doubleGold) buffText+="✅双倍金币 ";
  if(player.buff.bright) buffText+="✅手电筒 ";
  document.getElementById("activeBuff").innerText = buffText?"当前增益:"+buffText:"";
  document.getElementById("gameGold").innerText = "💰金币:"+player.gold;
  requestAnimationFrame(gameLoop);
}
function resetBuff(){
  player.buff.speed=false;
  player.buff.farView=false;
  player.buff.mapUnlock=false;
  player.buff.doubleGold=false;
  player.buff.footPrint=false;
  player.buff.bright=false;
}
function useItem(itemName){
  const idx = player.inventory.indexOf(itemName);
  if(idx===-1) return alert("没有这个道具!");
  const itemData = shopItems.find(x=>x.name===itemName);
  if(!itemData) return;
  switch(itemData.type){
    case "buffSpeed": player.buff.speed=true;alert("✅开启加速药水!本局生效");break;
    case "buffFarView": player.buff.farView=true;alert("✅开启透视眼镜!本局生效");break;
    case "buffMap": player.buff.mapUnlock=true;alert("✅解锁小地图!本局生效");break;
    case "buffDoubleGold": player.buff.doubleGold=true;alert("✅开启双倍金币!本局通关金币翻倍");break;
    case "bright": player.buff.bright=true;alert("✅夜光手电筒开启!");break;
    case "resetMaze": maze=generateMaze(mazeW,mazeH);px = cellSize*1.5; py=cellSize*1.5;alert("✅迷宫已重新生成!");break;
    case "collect": alert("🎁收藏品!仅观赏");return;
    default: alert("这个道具暂时还没做效果");return;
  }
  player.inventory.splice(idx,1);
  openInv();
}
// ========== UI ==========
function hideAll(){
  document.getElementById("menu").style.display="none";
  document.getElementById("shop").style.display="none";
  document.getElementById("inv").style.display="none";
  document.getElementById("gameUI").style.display="none";
  miniCanvas.style.display="none";
  touchControl.style.display="none";
}
function openMenu(){
  hideAll();
  gameRunning=false;
  document.getElementById("menu").style.display="block";
  document.getElementById("menuGold").innerText="💰金币:"+player.gold;
}
function openShop(){
  hideAll();
  document.getElementById("shop").style.display="block";
  document.getElementById("shopGold").innerText="💰金币:"+player.gold;
  const slist = document.getElementById("shopList");
  slist.innerHTML="";
  shopItems.forEach(it=>{
    const div = document.createElement("div");
    div.className="item";
    div.innerHTML = `<div><strong>${it.name}</strong></div><div>${it.desc}</div><div>价格:${it.price}金币 <button class="buyBtn">购买</button></div>`;
    div.querySelector(".buyBtn").onclick=()=>{
      if(player.gold >= it.price){
        player.gold -= it.price;
        player.inventory.push(it.name);
        openShop();
      }else alert("金币不足!")
    }
    slist.appendChild(div);
  })
}
function openInv(){
  hideAll();
  document.getElementById("inv").style.display="block";
  const ilist = document.getElementById("invList");
  ilist.innerHTML="";
  if(player.inventory.length===0)ilist.innerHTML="<p>背包空空的,去商城买点东西吧!</p>";
  else player.inventory.forEach(n=>{
    const d=document.createElement("div");
    d.className="item";
    d.innerHTML = `<span>- ${n}</span><button class="useBtn">使用</button>`;
    d.querySelector(".useBtn").onclick=()=>useItem(n);
    ilist.appendChild(d);
  })
}
function closeAll(){
  hideAll();
}
function startGame(){
  hideAll();
  maze = generateMaze(mazeW,mazeH);
  px = cellSize*1.5; py=cellSize*1.5; angle=0;
  walkedCells.clear();
  gameRunning=true;
  document.getElementById("gameUI").style.display="block";
  miniCanvas.style.display="block";
  touchControl.style.display="block"; // 📱显示触屏按键
  gameLoop();
}
openMenu();
</script>
</body>
</html>

Game Source: 第一人称迷宫闯关|金币商城(触屏版)

Creator: WildComet34

Libraries: none

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