平面2D射击(机器人AI会射击+障碍物)
by SparkShark50289 lines8.4 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>平面机器人射击</title>
<style>
*{margin:0;padding:0;box-sizing:border-box;font-family:system-ui,sans-serif}
body{background:#111;overflow:hidden}
#game{width:100vw;height:100vh;background:#222;position:relative;overflow:hidden}
.player{width:44px;height:44px;background:#42a5f5;border-radius:6px;position:absolute}
.robot{width:44px;height:44px;background:#ef5350;border-radius:6px;position:absolute}
.wall{background:#668;border-radius:3px;position:absolute}
.bulletPlayer{width:6px;height:14px;background:#ffd600;border-radius:3px;position:absolute}
.bulletRobot{width:6px;height:14px;background:#ff2222;border-radius:3px;position:absolute}
#control{position:fixed;bottom:12px;left:0;right:0;display:flex;justify-content:space-between;padding:0 20px;z-index:6}
.btn{width:70px;height:70px;border-radius:50%;font-size:24px;background:#ffffffcc;border:none;margin:3px}
.shootBtn{background:#ff4444cc;color:white}
.scoreText{position:fixed;top:14px;left:14px;color:#fff;font-size:22px;font-weight:bold;z-index:6}
#gameOverBox{position:fixed;width:100vw;height:100vh;background:rgba(0,0,0,0.75);display:none;align-items:center;justify-content:center;z-index:10}
.panel{background:white;padding:28px;border-radius:12px;text-align:center}
</style>
</head>
<body>
<div id="game">
<div class="scoreText">分数:0</div>
<div class="player" id="player"></div>
<div id="gameOverBox">
<div class="panel">
<h2>游戏结束</h2>
<p id="finalScore"></p>
<br>
<button class="btn" onclick="restartGame()">重新开始</button>
</div>
</div>
</div>
<div id="control">
<div>
<button class="btn" id="leftBtn">←</button>
<button class="btn" id="rightBtn">→</button>
<button class="btn" id="upBtn">↑</button>
<button class="btn" id="downBtn">↓</button>
</div>
<button class="btn shootBtn" id="shootBtn">射击</button>
</div>
<script>
const game = document.getElementById("game");
const player = document.getElementById("player");
const gameOverBox = document.getElementById("gameOverBox");
const finalScoreText = document.getElementById("finalScore");
const scoreText = document.querySelector(".scoreText");
let px = 80, py = 200;
const pSpeed = 4.2;
let score = 0;
let gameRunning = true;
let leftHold=false,rightHold=false,upHold=false,downHold=false;
let playerBullets = [];
let robotBullets = [];
let robots = [];
let walls = [];
// 地图障碍物墙
const wallData = [
{x:220,y:120,w:240,h:22},
{x:120,y:260,w:22,h:180},
{x:420,y:180,w:220,h:22},
{x:520,y:300,w:22,h:160}
]
// 机器人位置
const robotData = [
{x:400,y:120,shootTimer:0},
{x:600,y:320,shootTimer:0}
]
// 生成墙
wallData.forEach(w=>{
const div=document.createElement("div");
div.className="wall";
div.style.left=w.x+"px";
div.style.top=w.y+"px";
div.style.width=w.w+"px";
div.style.height=w.h+"px";
game.appendChild(div);
walls.push({el:div,...w});
})
//生成机器人
robotData.forEach(r=>{
const div=document.createElement("div");
div.className="robot";
div.style.left=r.x+"px";
div.style.top=r.y+"px";
game.appendChild(div);
robots.push({el:div,x:r.x,y:r.y,shootTimer:r.shootTimer});
})
//触屏
document.getElementById("leftBtn").ontouchstart=()=>leftHold=true;
document.getElementById("leftBtn").ontouchend=()=>leftHold=false;
document.getElementById("rightBtn").ontouchstart=()=>rightHold=true;
document.getElementById("rightBtn").ontouchend=()=>rightHold=false;
document.getElementById("upBtn").ontouchstart=()=>upHold=true;
document.getElementById("upBtn").ontouchend=()=>upHold=false;
document.getElementById("downBtn").ontouchstart=()=>downHold=true;
document.getElementById("downBtn").ontouchend=()=>downHold=false;
document.getElementById("shootBtn").ontouchstart=shootPlayerBullet;
//电脑键盘WASD+空格射击
document.addEventListener("keydown",e=>{
if(e.code==="KeyA")leftHold=true;
if(e.code==="KeyD")rightHold=true;
if(e.code==="KeyW")upHold=true;
if(e.code==="KeyS")downHold=true;
if(e.code==="Space")shootPlayerBullet();
})
document.addEventListener("keyup",e=>{
if(e.code==="KeyA")leftHold=false;
if(e.code==="KeyD")rightHold=false;
if(e.code==="KeyW")upHold=false;
if(e.code==="KeyS")downHold=false;
})
//玩家发射子弹向右
function shootPlayerBullet(){
if(!gameRunning)return;
const b=document.createElement("div");
b.className="bulletPlayer";
const bx=px+44;
const by=py+18;
b.style.left=bx+"px";
b.style.top=by+"px";
game.appendChild(b);
playerBullets.push({el:b,x:bx,y:by,speed:8});
}
//机器人射子弹(射向玩家)
function robotShoot(robot){
const b=document.createElement("div");
b.className="bulletRobot";
const bx=robot.x;
const by=robot.y+18;
b.style.left=bx+"px";
b.style.top=by+"px";
game.appendChild(b);
robotBullets.push({el:b,x:bx,y:by,speedX:0,speedY:0,targetX:px,targetY:py});
}
//碰撞检测
function isHit(ax,aw,ay,ah,bx,bw,by,bh){
return ax < bx+bw && ax+aw > bx && ay < by+bh && ay+ah > by;
}
//游戏主循环
function gameLoop(){
if(!gameRunning)return;
//玩家移动+撞墙检测
let nx=px, ny=py;
if(leftHold)nx -= pSpeed;
if(rightHold)nx += pSpeed;
if(upHold)ny -= pSpeed;
if(downHold)ny += pSpeed;
let canMove=true;
for(let w of walls){
if(isHit(nx,44,ny,44,w.x,w.w,w.y,w.h)){canMove=false;break;}
}
if(canMove){px=nx;py=ny;}
//边界
if(px<0)px=0;
if(py<0)py=0;
if(px+44>window.innerWidth)px=window.innerWidth-44;
if(py+44>window.innerHeight)py=window.innerHeight-44;
player.style.left=px+"px";
player.style.top=py+"px";
//玩家子弹更新
for(let i=playerBullets.length-1;i>=0;i--){
let blt=playerBullets[i];
blt.x += blt.speed;
blt.el.style.left=blt.x+"px";
//撞墙移除
let hitWall=false;
for(let w of walls){
if(isHit(blt.x,6,blt.y,14,w.x,w.w,w.y,w.h)){hitWall=true;break;}
}
if(hitWall || blt.x>window.innerWidth){
blt.el.remove();
playerBullets.splice(i,1);
continue;
}
//打中机器人
for(let ri=robots.length-1;ri>=0;ri--){
let r=robots[ri];
if(isHit(blt.x,6,blt.y,14,r.x,44,r.y,44)){
blt.el.remove();
playerBullets.splice(i,1);
r.el.remove();
robots.splice(ri,1);
score +=10;
scoreText.innerText="分数:"+score;
break;
}
}
}
//机器人AI,每隔一段时间开枪
robots.forEach(r=>{
r.shootTimer++;
if(r.shootTimer>90){
robotShoot(r);
r.shootTimer=0;
}
})
//机器人子弹追踪玩家
for(let i=robotBullets.length-1;i>=0;i--){
let blt=robotBullets[i];
const dx = px - blt.x;
const dy = py - blt.y;
const dist = Math.sqrt(dx*dx+dy*dy);
if(dist>0){
blt.x += (dx/dist)*3;
blt.y += (dy/dist)*3;
}
blt.el.style.left=blt.x+"px";
blt.el.style.top=blt.y+"px";
//撞墙消失
let hitWall=false;
for(let w of walls){
if(isHit(blt.x,6,blt.y,14,w.x,w.w,w.y,w.h)){hitWall=true;break;}
}
if(hitWall || blt.x<0||blt.y<0||blt.x>window.innerWidth||blt.y>window.innerHeight){
blt.el.remove();
robotBullets.splice(i,1);
continue;
}
//打中玩家
if(isHit(blt.x,6,blt.y,14,px,44,py,44)){
gameOver();
return;
}
}
requestAnimationFrame(gameLoop);
}
function gameOver(){
gameRunning=false;
finalScoreText.innerText="得分:"+score;
gameOverBox.style.display="flex";
}
function restartGame(){
//清空子弹、机器人、墙
playerBullets.forEach(b=>b.el.remove());
robotBullets.forEach(b=>b.el.remove());
robots.forEach(r=>r.el.remove());
walls.forEach(w=>w.el.remove());
playerBullets=[];robotBullets=[];robots=[];walls=[];
px=80;py=200;score=0;
scoreText.innerText="分数:0";
gameRunning=true;
gameOverBox.style.display="none";
//重建地图
wallData.forEach(w=>{
const div=document.createElement("div");
div.className="wall";
div.style.left=w.x+"px";
div.style.top=w.y+"px";
div.style.width=w.w+"px";
div.style.height=w.h+"px";
game.appendChild(div);
walls.push({el:div,...w});
})
robotData.forEach(r=>{
const div=document.createElement("div");
div.className="robot";
div.style.left=r.x+"px";
div.style.top=r.y+"px";
game.appendChild(div);
robots.push({el:div,x:r.x,y:r.y,shootTimer:r.shootTimer});
})
gameLoop();
}
gameLoop();
</script>
</body>
</html>Game Source: 平面2D射击(机器人AI会射击+障碍物)
Creator: SparkShark50
Libraries: none
Complexity: complex (289 lines, 8.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: 2d-ai-sparkshark50" to link back to the original. Then publish at arcadelab.ai/publish.