第一人称迷宫闯关|金币商城(触屏版)
by WildComet34326 lines11.9 KB
<!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.