迷你三角洲战术射击升级版
by SuperFlare63284 lines10.4 KB
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0,maximum-scale=1,user-scalable=no">
<title>迷你三角洲战术射击升级版</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{background:#111;overflow:hidden;font-family:sans-serif}
canvas{display:block;width:100vw;height:100vh}
#ui{position:fixed;inset:0;pointer-events:none}
#joystick{position:absolute;left:30px;bottom:40px;width:120px;height:120px;border-radius:50%;background:rgba(255,255,255,0.15);border:2px solid rgba(255,255,255,0.3);pointer-events:auto}
#stick{width:50px;height:50px;border-radius:50%;background:rgba(80,180,255,0.6);position:absolute;left:35px;top:35px}
#fireBtn{position:absolute;right:30px;bottom:70px;width:90px;height:90px;border-radius:50%;background:rgba(220,40,40,0.5);border:2px solid #ff6666;color:white;font-size:18px;pointer-events:auto}
#switchGun{position:absolute;right:140px;bottom:90px;width:70px;height:70px;border-radius:12px;background:rgba(40,120,220,0.5);border:2px solid #77bbff;color:#fff;font-size:13px;pointer-events:auto}
#hp{position:absolute;top:15px;left:15px;color:#fff;font-size:19px;text-shadow:1px 1px #000}
#armor{position:absolute;top:40px;left:15px;color:#66ccff;font-size:18px;text-shadow:1px 1px #000}
#gunName{position:absolute;top:65px;left:15px;color:#ffdd55;font-size:17px;text-shadow:1px 1px #000}
#kill{position:absolute;top:90px;left:15px;color:#fff;font-size:17px;text-shadow:1px 1px #000}
#extractText{position:absolute;top:115px;left:15px;color:#77ff77;font-size:16px;text-shadow:1px 1px #000}
#minimap{position:absolute;top:15px;right:15px;width:130px;height:130px;border:2px solid rgba(255,255,255,0.4);background:rgba(0,0,0,0.4);pointer-events:none}
</style>
</head>
<body>
<canvas id="game"></canvas>
<canvas id="minimap"></canvas>
<div id="ui">
<div id="joystick"><div id="stick"></div></div>
<button id="fireBtn">开火</button>
<button id="switchGun">换枪</button>
<div id="hp">生命:100</div>
<div id="armor">护甲:0</div>
<div id="gunName">武器:步枪</div>
<div id="kill">击杀:0</div>
<div id="extractText">等待撤离开启</div>
</div>
<script>
const canvas=document.getElementById("game");
const ctx=canvas.getContext("2d");
const mm=document.getElementById("minimap");
const mmCtx=mm.getContext("2d");
let W,H;
function resize(){W=canvas.width=innerWidth;H=canvas.height=innerHeight;mm.width=130;mm.height=130}
resize();addEventListener("resize",resize);
//枪械定义
const guns=[
{name:"步枪",damage:12,bulletSpeed:9,fireRate:12},
{name:"狙击枪",damage:45,bulletSpeed:16,fireRate:45},
{name:"冲锋枪",damage:7,bulletSpeed:11,fireRate:5}
];
let currentGun=0;
let fireCooldown=0;
//玩家
const player={x:W/2,y:H/2,r:16,speed:3,hp:100,armor:0,kill:0,angle:0};
let vx=0,vy=0;
//摇杆
const joy=document.getElementById("joystick");
const stick=document.getElementById("stick");
let jx=0,jy=0;
joy.addEventListener("touchmove",e=>{
const t=e.touches[0];
const r=joy.getBoundingClientRect();
let dx=t.clientX-(r.left+r.width/2);
let dy=t.clientY-(r.top+r.height/2);
const dist=Math.hypot(dx,dy);
const max=40;
if(dist>max){dx=dx/dist*max;dy=dy/dist*max}
stick.style.left=(35+dx)+"px";stick.style.top=(35+dy)+"px";
jx=dx/max;jy=dy/max;
e.preventDefault();
});
joy.addEventListener("touchend",()=>{
jx=0;jy=0;stick.style.left="35px";stick.style.top="35px";
});
//开火
const fireBtn=document.getElementById("fireBtn");
let shooting=false;
fireBtn.addEventListener("touchstart",e=>{shooting=true;e.preventDefault()});
fireBtn.addEventListener("touchend",()=>shooting=false);
//切换武器
const switchGun=document.getElementById("switchGun");
switchGun.addEventListener("touchstart",e=>{
currentGun=(currentGun+1)%guns.length;
e.preventDefault();
});
let bullets=[];
let enemies=[];
let enemyBullets=[];
let airDrops=[];
let spawnTimer=0;
//撤离点
let extractActive=false;
let extractTimer=0;
const extractPoint={x:W*0.8,y:H*0.2,r:45};
function spawnEnemy(){
const side=Math.floor(Math.random()*4);
let ex,ey;
if(side===0){ex=Math.random()*W;ey=-20}
else if(side===1){ex=W+20;ey=Math.random()*H}
else if(side===2){ex=Math.random()*W;ey=H+20}
else {ex=-20;ey=Math.random()*H}
enemies.push({x:ex,y:ey,r:15,hp:30,speed:1.2,shootCd:Math.random()*60})
}
function spawnAirdrop(){
airDrops.push({
x:80+Math.random()*(W-160),
y:80+Math.random()*(H-160),
r:22,
lootType:Math.random()<0.5?"armor":"gun"
});
}
function loop(){
ctx.fillStyle="#3a4a32";
ctx.fillRect(0,0,W,H);
ctx.fillStyle="#2e3d27";
for(let i=0;i<80;i++) ctx.fillRect((i*97)%W,(i*53)%H,3,3);
//玩家移动
vx=jx*player.speed;vy=jy*player.speed;
player.x+=vx;player.y+=vy;
player.x=Math.max(player.r,Math.min(W-player.r,player.x));
player.y=Math.max(player.r,Math.min(H-player.r,player.y));
//最近敌人自动瞄准
let near=null,nd=9999;
for(let e of enemies){
const d=Math.hypot(e.x-player.x,e.y-player.y);
if(d<nd){nd=d;near=e}
}
if(near) player.angle=Math.atan2(near.y-player.y,near.x-player.x);
fireCooldown--;
//开火逻辑
if(shooting&&fireCooldown<=0){
const g=guns[currentGun];
bullets.push({
x:player.x+Math.cos(player.angle)*18,
y:player.y+Math.sin(player.angle)*18,
vx:Math.cos(player.angle)*g.bulletSpeed,
vy:Math.sin(player.angle)*g.bulletSpeed,
damage:g.damage,life:120
});
fireCooldown=g.fireRate;
}
//玩家子弹更新
for(let i=bullets.length-1;i>=0;i--){
const b=bullets[i];
b.x+=b.vx;b.y+=b.vy;b.life--;
if(b.life<=0||b.x<0||b.y<0||b.x>W||b.y>H){bullets.splice(i,1);continue}
ctx.fillStyle="#ffdd33";
ctx.beginPath();ctx.arc(b.x,b.y,4,0,Math.PI*2);ctx.fill();
}
//敌人逻辑
for(let i=enemies.length-1;i>=0;i--){
const e=enemies[i];
const dx=player.x-e.x;
const dy=player.y-e.y;
const d=Math.hypot(dx,dy);
if(d>130){
e.x+=(dx/d)*e.speed;
e.y+=(dy/d)*e.speed;
}
e.shootCd--;
if(e.shootCd<=0&&d<340){
enemyBullets.push({x:e.x,y:e.y,vx:(dx/d)*5,vy:(dy/d)*5,life:130,dmg:8});
e.shootCd=90;
}
//子弹击中敌人
for(let j=bullets.length-1;j>=0;j--){
const b=bullets[j];
if(Math.hypot(b.x-e.x,b.y-e.y)<e.r){
e.hp-=b.damage;
bullets.splice(j,1);
break;
}
}
if(e.hp<=0){enemies.splice(i,1);player.kill++;continue;}
ctx.fillStyle="#882222";
ctx.beginPath();ctx.arc(e.x,e.y,e.r,0,Math.PI*2);ctx.fill();
}
//敌方子弹
for(let i=enemyBullets.length-1;i>=0;i--){
const b=enemyBullets[i];
b.x+=b.vx;b.y+=b.vy;b.life--;
if(b.life<=0){enemyBullets.splice(i,1);continue}
if(Math.hypot(b.x-player.x,b.y-player.y)<player.r){
let dmg=b.dmg;
if(player.armor>0){
const absorb=Math.min(player.armor,dmg);
player.armor-=absorb;
dmg-=absorb;
}
player.hp-=dmg;
enemyBullets.splice(i,1);
}
ctx.fillStyle="#ff5555";
ctx.beginPath();ctx.arc(b.x,b.y,5,0,Math.PI*2);ctx.fill();
}
//空投物资绘制与拾取
for(let i=airDrops.length-1;i>=0;i--){
const a=airDrops[i];
ctx.fillStyle="#cc8822";
ctx.fillRect(a.x-a.r,a.y-a.r,a.r*2,a.r*2);
ctx.strokeStyle="#fff";ctx.lineWidth=2;
ctx.strokeRect(a.x-a.r,a.y-a.r,a.r*2,a.r*2);
if(Math.hypot(player.x-a.x,player.y-a.y)<a.r+player.r){
if(a.lootType==="armor") player.armor=Math.min(100,player.armor+50);
else currentGun=(currentGun+1)%guns.length;
airDrops.splice(i,1);
}
}
//撤离点逻辑
extractTimer++;
if(extractTimer>900&&!extractActive){
extractActive=true;
}
if(extractActive){
ctx.strokeStyle="#33ff55";
ctx.lineWidth=3;
ctx.beginPath();ctx.arc(extractPoint.x,extractPoint.y,extractPoint.r,0,Math.PI*2);ctx.stroke();
ctx.fillStyle="#33ff55";
ctx.font="15px sans-serif";ctx.textAlign="center";
ctx.fillText("撤离点",extractPoint.x,extractPoint.y);
//到达撤离点胜利
if(Math.hypot(player.x-extractPoint.x,player.y-extractPoint.y)<extractPoint.r){
ctx.fillStyle="rgba(0,0,0,0.7)";ctx.fillRect(0,0,W,H);
ctx.fillStyle="#55ff77";ctx.font="38px sans-serif";ctx.textAlign="center";
ctx.fillText("成功撤离!任务胜利",W/2,H/2-25);
ctx.fillStyle="#fff";ctx.font="24px sans-serif";
ctx.fillText("总击杀:"+player.kill,W/2,H/2+20);
return;
}
}
//绘制玩家
ctx.save();
ctx.translate(player.x,player.y);
ctx.rotate(player.angle);
ctx.fillStyle="#3377cc";
ctx.beginPath();ctx.arc(0,0,player.r,0,Math.PI*2);ctx.fill();
ctx.fillStyle="#222";
ctx.fillRect(8,-3,22,6);
ctx.restore();
//生成敌人和空投
spawnTimer++;
if(spawnTimer>120&&enemies.length<12){
spawnEnemy();
spawnTimer=0;
}
if(player.kill>0&&player.kill%8===0&&airDrops.length<2){
spawnAirdrop();
}
//小地图绘制
mmCtx.clearRect(0,0,130,130);
mmCtx.fillStyle="#2d3b26";mmCtx.fillRect(0,0,130,130);
const sx=130/W,sy=130/H;
//撤离点小地图标记
if(extractActive){
mmCtx.fillStyle="#33ff55";
mmCtx.beginPath();mmCtx.arc(extractPoint.x*sx,extractPoint.y*sy,6,0,Math.PI*2);mmCtx.fill();
}
//空投
mmCtx.fillStyle="#cc8822";
for(let a of airDrops) mmCtx.fillRect(a.x*sx-2,a.y*sy-2,4,4);
//敌人
mmCtx.fillStyle="#ff3333";
for(let e of enemies) mmCtx.fillRect(e.x*sx-2,e.y*sy-2,4,4);
//玩家
mmCtx.fillStyle="#44aaff";
mmCtx.beginPath();mmCtx.arc(player.x*sx,player.y*sy,4,0,Math.PI*2);mmCtx.fill();
//UI文字更新
document.getElementById("hp").innerText="生命:"+Math.max(0,player.hp);
document.getElementById("armor").innerText="护甲:"+player.armor;
document.getElementById("gunName").innerText="武器:"+guns[currentGun].name;
document.getElementById("kill").innerText="击杀:"+player.kill;
document.getElementById("extractText").innerText=extractActive?"撤离点已开启!前往绿色圆圈撤离":"等待撤离开启";
if(player.hp<=0){
ctx.fillStyle="rgba(0,0,0,0.65)";ctx.fillRect(0,0,W,H);
ctx.fillStyle="#fff";ctx.font="36px sans-serif";ctx.textAlign="center";
ctx.fillText("任务失败!击杀:"+player.kill,W/2,H/2);
return;
}
requestAnimationFrame(loop);
}
loop();
</script>
</body>
</html>Game Source: 迷你三角洲战术射击升级版
Creator: SuperFlare63
Libraries: none
Complexity: complex (284 lines, 10.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-superflare63-msqywkkz" to link back to the original. Then publish at arcadelab.ai/publish.