🎮ArcadeLab

迷你三角洲战术射击升级版

by SuperFlare63
284 lines10.4 KB
▶ Play
<!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.