迷宫逃生 | Maze Escape | Escape del Laberinto
by SuperPhoenix65439 lines14.9 KB
<!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>迷宫逃生 | Maze Escape | Escape del Laberinto</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{background:#111;display:flex;justify-content:center;align-items:center;min-height:100vh;color:#fff;font-family:Arial}
/* 左上角语言按钮 */
#langBtn{
position:fixed;top:12px;left:12px;z-index:9999;
padding:6px 10px;font-size:13px;background:#2266cc;color:#fff;
border:1px solid #fff;border-radius:6px;cursor:pointer;
}
/* 语言选择弹窗面板 */
#langPanel{
position:fixed;top:50px;left:12px;background:#222;
border:1px solid #fff;border-radius:6px;padding:10px;
z-index:9998;display:none;
}
.lang-item{
display:block;width:100%;margin:6px 0;padding:5px 8px;
background:#444;border:none;color:#fff;border-radius:4px;
cursor:pointer;text-align:left;
}
.lang-item:hover{background:#666;}
#startScreen{
position:fixed;z-index:999;
top:0;left:0;width:100%;height:100%;
background:#000;display:flex;flex-direction:column;
justify-content:center;align-items:center;
}
#startBtn{
padding:16px 40px;font-size:22px;background:#09f;color:#fff;
border:none;border-radius:10px;cursor:pointer;margin-top:20px;
}
#gameOverScreen{
position:fixed;z-index:990;
top:0;left:0;width:100%;height:100%;
background:rgba(0,0,0,0.9);display:none;
flex-direction:column;justify-content:center;align-items:center;
}
#winScreen{
position:fixed;z-index:990;
top:0;left:0;width:100%;height:100%;
background:rgba(0,0,0,0.9);display:none;
flex-direction:column;justify-content:center;align-items:center;
}
.restartBtn{
padding:14px 32px;font-size:18px;background:#f30;color:#fff;
border:none;border-radius:8px;margin-top:20px;cursor:pointer;
}
#viewBox{
width:640px;height:420px;
border:3px solid #fff;
overflow:hidden;
position:relative;
background:#3a3a3a;
display:none;
}
#mazeWorld{position:absolute}
.wall{background:#000;position:absolute}
.player{width:20px;height:20px;background:#0cf;position:absolute;border-radius:50%;z-index:2}
.monster{width:24px;height:24px;background:#f33;position:absolute;border-radius:50%;z-index:2}
.ball{width:14px;height:14px;background:#ffd700;border-radius:50%;position:absolute;box-shadow:0 0 8px gold}
.fog{position:absolute;top:0;left:0;right:0;bottom:0;pointer-events:none;z-index:10}
.info{position:absolute;top:10px;left:10px;font-size:20px;z-index:99}
/* 手机+电脑通用遥感 */
#joystick{
position:fixed;bottom:20px;left:20px;
width:120px;height:120px;background:rgba(255,255,255,0.2);
border-radius:50%;z-index:100;display:none;cursor:grab;
}
#stick{
position:absolute;width:50px;height:50px;background:#fff;
border-radius:50%;top:35px;left:35px;cursor:grabbing;
}
</style>
</head>
<body>
<!-- 左上角多语言按钮 -->
<button id="langBtn">语言 / Lang / Idioma</button>
<!-- 语言选择面板 -->
<div id="langPanel">
<button class="lang-item" data-lang="zh">中文 | Chinese | Chino</button>
<button class="lang-item" data-lang="en">英文 | English | Inglés</button>
<button class="lang-item" data-lang="es">西语 | Spanish | Español</button>
</div>
<!-- 开始界面 -->
<div id="startScreen">
<h1 id="startTitle">迷宫逃生</h1>
<p id="startTip">收集金球消灭怪物|电脑WASD/鼠标拖摇杆,手机触屏摇杆</p>
<button id="startBtn">开始游戏</button>
</div>
<!-- 失败界面 -->
<div id="gameOverScreen">
<h1 id="loseText">💀 被抓住了</h1>
<button class="restartBtn" id="restartLose">重新开始</button>
</div>
<!-- 胜利界面 -->
<div id="winScreen">
<h1 id="winText">🏆 胜利通关</h1>
<button class="restartBtn" id="restartWin">重新开始</button>
</div>
<!-- 游戏界面 -->
<div class="info"><span id="hpLabel">怪物血量:</span> <span id="hp">7</span></div>
<div id="viewBox">
<div id="mazeWorld"></div>
<div class="fog"></div>
</div>
<!-- 手机+电脑遥感 -->
<div id="joystick">
<div id="stick"></div>
</div>
<script>
// 多语言词典
const langData = {
zh:{
title:"迷宫逃生",
tip:"收集金球消灭怪物|电脑WASD/鼠标拖摇杆,手机触屏摇杆",
start:"开始游戏",
hp:"怪物血量:",
lose:"💀 被抓住了",
win:"🏆 胜利通关",
restart:"重新开始"
},
en:{
title:"Maze Escape",
tip:"Collect gold balls to defeat monster | WASD/Joystick for PC, Touch joystick for mobile",
start:"Start Game",
hp:"Monster HP:",
lose:"💀 Caught",
win:"🏆 Victory",
restart:"Restart"
},
es:{
title:"Escape del Laberinto",
tip:"Recoge oros para derrotar al monstruo | WASD/Mando PC, Mando táctil móvil",
start:"Empezar",
hp:"HP Monstruo:",
lose:"💀 Capturado",
win:"🏆 Victoria",
restart:"Reiniciar"
}
};
let nowLang = "zh";//默认中文
//DOM元素
const langBtn = document.getElementById("langBtn");
const langPanel = document.getElementById("langPanel");
const langItems = document.querySelectorAll(".lang-item");
const startTitle = document.getElementById("startTitle");
const startTip = document.getElementById("startTip");
const startBtn = document.getElementById("startBtn");
const hpLabel = document.getElementById("hpLabel");
const loseText = document.getElementById("loseText");
const winText = document.getElementById("winText");
const restartLose = document.getElementById("restartLose");
const restartWin = document.getElementById("restartWin");
//切换语言函数
function setLang(type){
nowLang = type;
const d = langData[type];
startTitle.textContent = d.title;
startTip.textContent = d.tip;
startBtn.textContent = d.start;
hpLabel.textContent = d.hp;
loseText.textContent = d.lose;
winText.textContent = d.win;
restartLose.textContent = d.restart;
restartWin.textContent = d.restart;
langPanel.style.display = "none";
}
//打开关闭语言面板
langBtn.onclick = ()=>{
langPanel.style.display = langPanel.style.display==="none"?"block":"none";
};
//点击空白关闭面板
document.body.onclick = (e)=>{
if(!langBtn.contains(e.target) && !langPanel.contains(e.target)){
langPanel.style.display = "none";
}
}
//选中语言
langItems.forEach(item=>{
item.onclick = ()=>setLang(item.dataset.lang);
})
//游戏原有逻辑不变
const gameOverScreen = document.getElementById('gameOverScreen');
const winScreen = document.getElementById('winScreen');
const viewBox = document.getElementById('viewBox');
const joystick = document.getElementById('joystick');
const stick = document.getElementById('stick');
const world = document.getElementById('mazeWorld');
const hpText = document.getElementById('hp');
const fog = document.querySelector('.fog');
const tile = 32;
const mapW = 32, mapH = 24;
let gameStarted = false;
let gameOver = false;
let isMouseDown = false;
const map = [
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,1],
[1,0,0,0,0,1,0,1,1,1,1,0,1,0,1,1,1,1,0,1,0,1,1,1,1,0,1,0,1,1,0,1],
[1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,1,0,0,1],
[1,0,1,0,1,1,1,1,0,0,1,1,1,1,1,0,0,1,1,1,1,0,0,1,1,1,1,0,1,0,0,1],
[1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1],
[1,1,1,0,1,0,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,0,0,1,0,1,1,1,1,1,1],
[1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1],
[1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,0,1],
[1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1],
[1,1,1,1,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,0,1],
[1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1],
[1,0,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1],
[1,1,1,1,1,0,1,1,1,1,1,1,0,1,0,1,1,1,0,1,1,1,1,1,1,0,1,1,1,1,1,1],
[1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,1],
[1,0,1,0,1,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0,1],
[1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1],
[1,0,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1],
[1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
];
const player = {x: tile*1.5, y: tile*1.5, size:20, speed:3};
const monster = {x:900, y:600, size:24, speed:1.2, hp:7};
const keys = {w:false,a:false,s:false,d:false};
let balls = [];
let p, m;
let stickMove = {x:0,y:0};
// 开始游戏
startBtn.onclick = () => {
startScreen.style.display = 'none';
viewBox.style.display = 'block';
joystick.style.display = 'block';
gameStarted = true;
gameOver = false;
};
// 重新开始
restartLose.onclick = ()=>location.reload();
restartWin.onclick = ()=>location.reload();
// 生成墙体
function initMap(){
world.style.width = mapW*tile+'px';
world.style.height = mapH*tile+'px';
for(let y=0;y<mapH;y++){
for(let x=0;x<mapW;x++){
if(map[y][x]===1){
const e = document.createElement('div');
e.className='wall';
e.style.left=x*tile+'px';
e.style.top=y*tile+'px';
e.style.width=tile+'px';
e.style.height=tile+'px';
world.appendChild(e);
}
}
}
}
// 生成角色
function initPlayer(){
p = document.createElement('div'); p.className='player'; world.appendChild(p);
m = document.createElement('div'); m.className='monster'; world.appendChild(m);
}
// 生成金球
function makeBalls(n=16){
const list = [];
for(let y=0;y<mapH;y++)for(let x=0;x<mapW;x++)if(map[y][x]===0)list.push({x,y});
for(let i=0;i<n;i++){
const o = list.splice(Math.random()*list.length|0,1)[0];
const e = document.createElement('div'); e.className='ball';
e.style.left = o.x*tile+9+'px';
e.style.top = o.y*tile+9+'px';
world.appendChild(e);
balls.push({e,x:o.x*tile+9,y:o.y*tile+9});
}
}
// 碰撞
function canGo(x,y){
const t = tile;
const pts = [
[x+2,y+2],[x+player.size-2,y+2],
[x+2,y+player.size-2],[x+player.size-2,y+player.size-2]
];
for(const [px,py] of pts){
const gx = (px/t)|0, gy = (py/t)|0;
if(gx<0||gy<0||gx>=mapW||gy>=mapH||map[gy][gx])return false;
}
return true;
}
// 镜头
function cam(){
const vw=viewBox.clientWidth,vh=viewBox.clientHeight;
world.style.transform = `translate(${vw/2-player.x}px,${vh/2-player.y}px)`;
fog.style.background = `radial-gradient(circle 110px at ${vw/2}px ${vh/2}px,transparent 0,#000 160px)`;
}
// ========== 【触屏+鼠标双适配摇杆代码】 ==========
const joyCenterX = 60;
const joyCenterY = 60;
const maxStickRange = 45;
joystick.ontouchstart = updateJoyPos;
joystick.ontouchmove = updateJoyPos;
joystick.ontouchend = resetJoy;
joystick.onmousedown = (e)=>{
e.preventDefault();
isMouseDown = true;
updateJoyPos({clientX:e.clientX,clientY:e.clientY});
}
document.onmousemove = (e)=>{
if(!isMouseDown)return;
updateJoyPos({clientX:e.clientX,clientY:e.clientY});
}
document.onmouseup = ()=>{
isMouseDown = false;
resetJoy();
}
function updateJoyPos(e){
const rect = joystick.getBoundingClientRect();
let dx = e.clientX - (rect.left+joyCenterX);
let dy = e.clientY - (rect.top+joyCenterY);
let dist = Math.hypot(dx,dy);
if(dist>maxStickRange){
dx = dx/dist*maxStickRange;
dy = dy/dist*maxStickRange;
}
stickMove.x = dx;
stickMove.y = dy;
stick.style.left = joyCenterX+dx+'px';
stick.style.top = joyCenterY+dy+'px';
}
function resetJoy(){
stickMove.x=0;stickMove.y=0;
stick.style.left='35px';stick.style.top='35px';
}
// 键盘
document.addEventListener('keydown',e=>{const k=e.key.toLowerCase();if(k in keys)keys[k]=true});
document.addEventListener('keyup',e=>{const k=e.key.toLowerCase();if(k in keys)keys[k]=false});
// 失败效果
function doGameOver(){
gameOver = true;
viewBox.style.filter = 'brightness(0.4) saturate(0)';
setTimeout(()=>{
gameOverScreen.style.display = 'flex';
},300);
}
// 胜利效果
function doWin(){
gameOver = true;
winScreen.style.display = 'flex';
}
// 主循环
function loop(){
if(!gameStarted || gameOver){requestAnimationFrame(loop);return;}
let nx=player.x, ny=player.y;
const sx = stickMove.x;
const sy = stickMove.y;
if(keys.a || sx < -18) nx-=player.speed;
if(keys.d || sx > 18) nx+=player.speed;
if(keys.w || sy < -18) ny-=player.speed;
if(keys.s || sy > 18) ny+=player.speed;
if(canGo(nx,player.y))player.x=nx;
if(canGo(player.x,ny))player.y=ny;
// 怪物穿墙追击
const dx=player.x-monster.x, dy=player.y-monster.y;
const dist=Math.hypot(dx,dy);
if(dist>5){
monster.x += dx/dist*monster.speed;
monster.y += dy/dist*monster.speed;
}
// 拾取金球
for(let i=balls.length-1;i>=0;i--){
const b=balls[i];
if(Math.hypot(player.x-b.x,player.y-b.y)<18){
monster.hp--; hpText.textContent=monster.hp;
b.e.remove(); balls.splice(i,1);
if(monster.hp<=0){doWin();return;}
}
}
// 被怪物抓到
if(Math.hypot(player.x-monster.x,player.y-monster.y)<18){
doGameOver();return;
}
p.style.left=player.x+'px';p.style.top=player.y+'px';
m.style.left=monster.x+'px';m.style.top=monster.y+'px';
cam();
requestAnimationFrame(loop);
}
initMap();
initPlayer();
makeBalls();
loop();
</script>
</body>
</html>Game Source: 迷宫逃生 | Maze Escape | Escape del Laberinto
Creator: SuperPhoenix65
Libraries: none
Complexity: complex (439 lines, 14.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: maze-escape-escape-del-laberinto-superphoenix65-mq2bfbjq" to link back to the original. Then publish at arcadelab.ai/publish.