<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
>
<channel>
<title><![CDATA[编程学习笔记]]></title> 
<atom:link href="https://lgw233.top/rss.php" rel="self" type="application/rss+xml" />
<description><![CDATA[记录美好生活]]></description>
<link>https://lgw233.top/</link>
<language>zh-cn</language>

<item>
    <title>网页版魂斗罗源码</title>
    <link>https://lgw233.top/?post=4</link>
    <description><![CDATA[<p><img src="http://8.156.81.135/content/uploadfile/202610/bfc81792653752.png" alt="" /><br />
<img src="http://8.156.81.135/content/uploadfile/202610/23af1792653760.png" alt="" /></p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang="zh-CN"&gt;
&lt;head&gt;
&lt;meta charset="UTF-8"&gt;
&lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
&lt;title&gt;魂斗罗 三关完整版｜二段跳无限生命&lt;/title&gt;
&lt;style&gt;
*{margin:0;padding:0;box-sizing:border-box}
html,body{width:100%;height:100%;overflow:hidden;background:#000;display:flex;align-items:center;justify-content:center}
canvas{display:block;image-rendering:pixelated;image-rendering:crisp-edges;background:#000}
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;canvas id="game"&gt;&lt;/canvas&gt;
&lt;script&gt;
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
const GAME_W = 800, GAME_H = 480;
canvas.width = GAME_W;
canvas.height = GAME_H;

// 窗口自适应缩放
function resize(){
  let scale = Math.min(window.innerWidth / GAME_W, window.innerHeight / GAME_H);
  canvas.style.width = GAME_W * scale + 'px';
  canvas.style.height = GAME_H * scale + 'px';
}
window.addEventListener('resize', resize);
resize();

// 音频系统
const AudioCtx = window.AudioContext || window.webkitAudioContext;
let audioCtx;
function ensureAudio(){
  if(!audioCtx) audioCtx = new AudioCtx();
}
function playSound(type){
  ensureAudio();
  const osc = audioCtx.createOscillator();
  const gain = audioCtx.createGain();
  osc.connect(gain);
  gain.connect(audioCtx.destination);
  let t = audioCtx.currentTime;
  switch(type){
    case 'shoot':
      osc.type='square';osc.frequency.setValueAtTime(800,t);osc.frequency.exponentialRampToValueAtTime(200,t+0.08);
      gain.gain.setValueAtTime(0.15,t);gain.gain.exponentialRampToValueAtTime(0.001,t+0.08);
      osc.start(t);osc.stop(t+0.08);break;
    case 'spread':
      osc.type='sawtooth';osc.frequency.setValueAtTime(600,t);osc.frequency.exponentialRampToValueAtTime(100,t+0.12);
      gain.gain.setValueAtTime(0.12,t);gain.gain.exponentialRampToValueAtTime(0.001,t+0.12);
      osc.start(t);osc.stop(t+0.12);break;
    case 'explosion':
      osc.type='sawtooth';osc.frequency.setValueAtTime(200,t);osc.frequency.exponentialRampToValueAtTime(30,t+0.3);
      gain.gain.setValueAtTime(0.2,t);gain.gain.exponentialRampToValueAtTime(0.001,t+0.3);
      osc.start(t);osc.stop(t+0.3);break;
    case 'powerup':
      osc.type='sine';osc.frequency.setValueAtTime(400,t);osc.frequency.exponentialRampToValueAtTime(1200,t+0.2);
      gain.gain.setValueAtTime(0.15,t);gain.gain.exponentialRampToValueAtTime(0.001,t+0.25);
      osc.start(t);osc.stop(t+0.25);break;
    case 'jump':
      osc.type='sine';osc.frequency.setValueAtTime(300,t);osc.frequency.exponentialRampToValueAtTime(600,t+0.1);
      gain.gain.setValueAtTime(0.1,t);gain.gain.exponentialRampToValueAtTime(0.001,t+0.1);
      osc.start(t);osc.stop(t+0.1);break;
    case 'hit':
      osc.type='square';osc.frequency.setValueAtTime(150,t);osc.frequency.exponentialRampToValueAtTime(50,t+0.2);
      gain.gain.setValueAtTime(0.2,t);gain.gain.exponentialRampToValueAtTime(0.001,t+0.2);
      osc.start(t);osc.stop(t+0.2);break;
    case 'boss':
      osc.type='sawtooth';osc.frequency.setValueAtTime(100,t);osc.frequency.exponentialRampToValueAtTime(40,t+0.5);
      gain.gain.setValueAtTime(0.25,t);gain.gain.exponentialRampToValueAtTime(0.001,t+0.5);
      osc.start(t);osc.stop(t+0.5);break;
  }
}

// 键盘输入
const keys = {};
window.addEventListener('keydown', e=&gt;{keys[e.code]=true;e.preventDefault()});
window.addEventListener('keyup', e=&gt;{keys[e.code]=false;e.preventDefault()});

// 关卡配置
const MAX_STAGE = 3;
let currentStage = 1;
const stageConfig = [
  {
    bgTop: '#0d1b2a', bgMid: '#1b2838', bgLow: '#2d4a3e', bgGround: '#1a3a2a',
    wallType: 'wall', enemyHpMult: 1, levelWidth: 6400, wallX: [800,1600,2400,3600,4800]
  },
  {
    bgTop: '#121212', bgMid: '#212121', bgLow: '#37474f', bgGround: '#263238',
    wallType: 'steelWall', enemyHpMult: 2, levelWidth: 6800, wallX: [600,1400,2200,3000,4000,5000]
  },
  {
    bgTop: '#0a192f', bgMid: '#112240', bgLow: '#233554', bgGround: '#1d3557',
    wallType: 'iceWall', enemyHpMult: 1, levelWidth: 7200, wallX: [750,1550,2350,3150,4150,5150]
  }
];

// 全局游戏变量
let gameState = 'title';
let score = 0, lives = 99, hiScore = parseInt(localStorage.getItem('contraHi') || '0');
let cameraX = 0;
let LEVEL_W = stageConfig[currentStage - 1].levelWidth;
const GRAVITY = 0.5;
let frameCount = 0;
let screenShake = 0;
let bossDefeated = false;
const platforms = [];

// 生成地图
function generateLevel(stageNum){
  platforms.length = 0;
  const cfg = stageConfig[stageNum - 1];
  LEVEL_W = cfg.levelWidth;
  // 地面
  for(let x = 0; x &lt; LEVEL_W; x += 200){
    if((x&gt;400&amp;&amp;x&lt;600) || (x&gt;1800&amp;&amp;x&lt;2000) || (x&gt;3200&amp;&amp;x&lt;3400)) continue;
    platforms.push({x, y: GAME_H - 40, w:200, h:40, type:'ground'});
  }
  // 浮空平台
  let floatList;
  if(stageNum === 1){
    floatList = [
      {x:300,y:320,w:120},{x:500,y:280,w:100},{x:700,y:240,w:140},
      {x:950,y:300,w:100},{x:1100,y:220,w:120},{x:1300,y:280,w:100},
      {x:1500,y:200,w:140},{x:1700,y:320,w:100},{x:1900,y:260,w:120},
      {x:2100,y:180,w:100},{x:2300,y:300,w:140},{x:2500,y:240,w:100},
      {x:2700,y:320,w:120},{x:2900,y:200,w:100},{x:3100,y:280,w:140},
      {x:3300,y:160,w:100},{x:3500,y:300,w:120},{x:3700,y:240,w:100},
      {x:3900,y:320,w:140},{x:4100,y:200,w:100},{x:4300,y:280,w:120},
      {x:4500,y:160,w:100},{x:4700,y:300,w:140},{x:4900,y:240,w:100},
      {x:5100,y:320,w:120},{x:5300,y:200,w:100},{x:5500,y:280,w:140},
      {x:5700,y:160,w:120},{x:5900,y:300,w:100}
    ];
  }else if(stageNum === 2){
    floatList = [
      {x:200,y:340,w:100},{x:450,y:260,w:130},{x:680,y:200,w:90},
      {x:900,y:360,w:110},{x:1200,y:240,w:120},{x:1500,y:180,w:100},
      {x:1800,y:320,w:140},{x:2100,y:220,w:90},{x:2400,y:380,w:120},
      {x:2700,y:260,w:110},{x:3000,y:190,w:130},{x:3300,y:340,w:100},
      {x:3600,y:210,w:120},{x:3900,y:300,w:90},{x:4200,y:170,w:140},
      {x:4500,y:350,w:110},{x:4800,y:230,w:100},{x:5100,y:310,w:130},
      {x:5400,y:180,w:90},{x:5700,y:290,w:120}
    ];
  }else{
    floatList = [
      {x:250,y:360,w:80},{x:420,y:290,w:110},{x:630,y:210,w:90},{x:840,y:140,w:120},
      {x:1050,y:330,w:100},{x:1260,y:250,w:80},{x:1470,y:170,w:110},{x:1680,y:100,w:90},
      {x:1900,y:350,w:120},{x:2100,y:270,w:100},{x:2320,y:190,w:80},{x:2530,y:120,w:110},
      {x:2740,y:320,w:90},{x:2950,y:240,w:120},{x:3160,y:160,w:100},{x:3370,y:90,w:80},
      {x:3600,y:340,w:110},{x:3810,y:260,w:90},{x:4020,y:180,w:120},{x:4230,y:110,w:100},
      {x:4440,y:310,w:80},{x:4650,y:230,w:110},{x:4860,y:150,w:90},{x:5070,y:80,w:120},
      {x:5300,y:300,w:100},{x:5510,y:220,w:80},{x:5720,y:140,w:110},{x:5930,y:70,w:90}
    ];
  }
  floatList.forEach(f=&gt;platforms.push({x:f.x,y:f.y,w:f.w,h:16,type:'platform'}));
  // 墙体
  cfg.wallX.forEach(x=&gt;platforms.push({x, y:GAME_H-120, w:30, h:80, type:cfg.wallType}));
}

// 玩家对象
const player = {
  x:100,y:300,w:28,h:40,vx:0,vy:0,
  dir:1,onGround:false,jumping:false,
  shooting:false,shootTimer:0,shootCooldown:8,
  weapon:'normal',weaponTimer:0,invincible:0,
  prone:false,animFrame:0,alive:true,respawnTimer:0,_bombUsed:false,
  jumpCount:0
};
let playerBullets = [], enemyBullets = [];
let enemies = [], boss = null;
let powerups = [], explosions = [], particles = [];

// 生成怪物道具
function spawnEnemies(stageNum){
  enemies.length = 0; powerups.length = 0;
  const cfg = stageConfig[stageNum - 1];
  const hpMult = cfg.enemyHpMult;
  let soldierCount, runnerCount;
  if(stageNum === 1){soldierCount=40;runnerCount=15;}
  else if(stageNum === 2){soldierCount=45;runnerCount=20;}
  else{soldierCount=35;runnerCount=0;}
  // 步兵
  for(let i=0;i&lt;soldierCount;i++){
    let ex = 600 + i*140 + Math.random()*60;
    enemies.push({
      type:'soldier',x:ex,y:GAME_H-80,w:24,h:36,hp:1*hpMult,dir:-1,vx:0,vy:0,onGround:false,
      shootTimer:Math.random()*120,alive:true,animFrame:0,patrol:ex-60,patrolMax:ex+60,state:'patrol'
    });
  }
  // 炮台
  let turretX;
  if(stageNum ===1) turretX = [1000,1800,2600,3400,4200,5000,5600];
  else if(stageNum===2) turretX = [900,1700,2500,3300,4100,4900,5500];
  else turretX = [1100,1900,2700,3500,4300,5100];
  turretX.forEach(tx=&gt;enemies.push({
    type:'turret',x:tx,y:GAME_H-72,w:32,h:32,hp:3*hpMult,dir:-1,shootTimer:60,alive:true,angle:0
  }));
  // 冲刺兵
  for(let i=0;i&lt;runnerCount;i++){
    let ex = 1200 + i*350 + Math.random()*100;
    enemies.push({
      type:'runner',x:ex,y:GAME_H-76,w:22,h:34,hp:1*hpMult,dir:-1,vx:-3,vy:0,onGround:false,alive:true,animFrame:0
    });
  }
  // 第三关飞行怪
  if(stageNum === 3){
    for(let i=0;i&lt;22;i++){
      let ex = 800 + i*280 + Math.random()*90;
      let baseY = 120 + Math.random()*180;
      enemies.push({
        type:'flyer',x:ex,y:baseY,w:26,h:22,hp:1,dir:-1,vx:-2,vy:0,baseY,alive:true,animFrame:0
      });
    }
  }
  // 道具
  [700,1400,2200,3000,3800,4600,5400].forEach((px,i)=&gt;{
    const types = ['spread','rapid','laser','life','shield'];
    powerups.push({x:px,y:180+Math.random()*80,w:24,h:24,type:types[i%types.length],alive:true,bobTimer:Math.random()*100});
  });
}
// 生成BOSS
function spawnBoss(){
  boss = {
    x:LEVEL_W-300,y:GAME_H-200,w:120,h:160,hp:50,maxHp:50,phase:0,
    shootTimer:0,moveTimer:0,alive:true,dir:-1,vy:0
  };
}
// 碰撞检测
function rectCollide(a,b){
  return a.x &lt; b.x + b.w &amp;&amp; a.x + a.w &gt; b.x &amp;&amp; a.y &lt; b.y + b.h &amp;&amp; a.y + a.h &gt; b.y;
}
// 地面碰撞修正
function resolveCollision(entity){
  entity.onGround = false;
  for(let p of platforms){
    if(rectCollide(entity,p)){
      let ox = Math.min(entity.x + entity.w - p.x, p.x + p.w - entity.x);
      let oy = Math.min(entity.y + entity.h - p.y, p.y + p.h - entity.y);
      if(oy &lt; ox){
        if(entity.y + entity.h / 2 &lt; p.y + p.h / 2){
          entity.y = p.y - entity.h;
          entity.vy = 0;
          entity.onGround = true;
          if(entity === player) player.jumpCount = 0;
        }else{
          entity.y = p.y + p.h;
          entity.vy = 0;
        }
      }else{
        if(entity.x + entity.w / 2 &lt; p.x + p.w / 2) entity.x = p.x - entity.w;
        else entity.x = p.x + p.w;
        entity.vx = 0;
      }
    }
  }
}

// ========== 绘制函数 ==========
function drawPixelChar(x,y,dir,frame,prone,shooting){
  const sx = x - cameraX;
  if(sx &lt; -50 || sx &gt; GAME_W+50) return;
  ctx.save();
  ctx.translate(sx+14,y);
  ctx.scale(dir,1);
  if(prone){
    ctx.fillStyle='#1e88e5'; ctx.fillRect(-14,24,28,12);
    ctx.fillStyle='#ffcc80'; ctx.fillRect(10,22,10,10);
    ctx.fillStyle='#1565c0'; ctx.fillRect(-14,28,28,8);
    ctx.fillStyle='#757575'; ctx.fillRect(18,26,16,4);
    if(shooting) ctx.fillStyle='#ffeb3b',ctx.fillRect(34,24,6,8);
  }else{
    ctx.fillStyle='#ffcc80'; ctx.fillRect(-6,0,12,12);
    ctx.fillStyle='#e53935'; ctx.fillRect(-7,0,14,4); ctx.fillRect(-9,2,4,3);
    ctx.fillStyle='#000'; ctx.fillRect(2,5,3,3);
    ctx.fillStyle='#1e88e5'; ctx.fillRect(-8,12,16,14);
    ctx.fillStyle='#795548'; ctx.fillRect(-8,24,16,3);
    let legOff = Math.sin(frame*0.3)*4;
    ctx.fillStyle='#1565c0'; ctx.fillRect(-7,27,6,13+legOff); ctx.fillRect(1,27,6,13-legOff);
    ctx.fillStyle='#4e342e'; ctx.fillRect(-8,37,7,3); ctx.fillRect(1,37,7,3);
    ctx.fillStyle='#757575';
    if(shooting){ ctx.fillRect(8,14,18,4); ctx.fillStyle='#ffeb3b'; ctx.fillRect(26,12,6,8); }
    else ctx.fillRect(8,16,14,3);
    ctx.fillStyle='#ffcc80'; ctx.fillRect(6,14,4,6);
  }
  ctx.restore();
}
function drawSoldier(e){
  const sx = e.x - cameraX;
  if(sx &lt; -50 || sx &gt; GAME_W+50) return;
  ctx.save(); ctx.translate(sx+12,e.y); ctx.scale(e.dir,1);
  ctx.fillStyle='#4caf50'; ctx.fillRect(-5,0,10,10);
  ctx.fillStyle='#2e7d32'; ctx.fillRect(-6,-2,12,6);
  ctx.fillStyle='#f00'; ctx.fillRect(1,4,3,2);
  ctx.fillStyle='#388e3c'; ctx.fillRect(-7,10,14,14);
  let lo = Math.sin(e.animFrame*0.2)*3;
  ctx.fillStyle='#1b5e20'; ctx.fillRect(-6,24,5,12+lo); ctx.fillRect(1,24,5,12-lo);
  ctx.fillStyle='#616161'; ctx.fillRect(7,14,12,3);
  ctx.restore();
}
function drawTurret(e){
  const sx = e.x - cameraX;
  if(sx &lt; -50 || sx &gt; GAME_W+50) return;
  ctx.fillStyle='#616161'; ctx.fillRect(sx,e.y+16,32,16);
  ctx.fillStyle='#757575'; ctx.beginPath(); ctx.arc(sx+16,e.y+16,14,Math.PI,0); ctx.fill();
  ctx.save(); ctx.translate(sx+16,e.y+14); ctx.rotate(e.angle);
  ctx.fillStyle='#424242'; ctx.fillRect(0,-3,24,6); ctx.restore();
  ctx.fillStyle = frameCount%30&lt;15 ? '#f44336' : '#ff8a80';
  ctx.fillRect(sx+13,e.y+6,6,4);
}
function drawRunner(e){
  const sx = e.x - cameraX;
  if(sx &lt; -50 || sx &gt; GAME_W+50) return;
  ctx.save(); ctx.translate(sx+11,e.y); ctx.scale(e.dir,1);
  ctx.fillStyle='#ff6f00'; ctx.fillRect(-5,0,10,10);
  ctx.fillStyle='#fff'; ctx.fillRect(1,3,4,4); ctx.fillStyle='#f00'; ctx.fillRect(2,4,2,2);
  ctx.fillStyle='#e65100'; ctx.fillRect(-6,10,12,12);
  let lo = Math.sin(e.animFrame*0.5)*6;
  ctx.fillStyle='#bf360c'; ctx.fillRect(-5,22,4,12+lo); ctx.fillRect(1,22,4,12-lo);
  ctx.fillStyle='#ff3d00'; ctx.fillRect(6,12,8,3);
  ctx.restore();
}
function drawFlyer(e){
  const sx = e.x - cameraX;
  if(sx &lt; -50 || sx &gt; GAME_W+50) return;
  ctx.save(); ctx.translate(sx+13,e.y); ctx.scale(e.dir,1);
  let wing = Math.sin(e.animFrame*0.4)*5;
  ctx.fillStyle='#78909c'; ctx.fillRect(-13+wing,8,10,6);
  ctx.fillRect(3-wing,8,10,6);
  ctx.fillStyle='#455a64'; ctx.fillRect(-8,2,16,14);
  ctx.fillStyle='#f44336'; ctx.fillRect(0,4,4,4);
  ctx.fillStyle='#263238'; ctx.fillRect(-6,14,12,6);
  ctx.restore();
}
function drawBoss(b){
  if(!b||!b.alive) return;
  const sx = b.x - cameraX;
  ctx.fillStyle='#455a64'; ctx.fillRect(sx,b.y,b.w,b.h);
  ctx.fillStyle='#37474f'; ctx.fillRect(sx+10,b.y+10,b.w-20,30);
  ctx.fillRect(sx+10,b.y+60,b.w-20,30); ctx.fillRect(sx+10,b.y+110,b.w-20,30);
  let coreGlow = Math.sin(frameCount*0.1)*0.3+0.7;
  ctx.fillStyle=`rgba(244,67,54,${coreGlow})`; ctx.beginPath(); ctx.arc(sx+b.w/2,b.y+b.h/2,20,0,Math.PI*2); ctx.fill();
  ctx.fillStyle='#ff8a80'; ctx.beginPath(); ctx.arc(sx+b.w/2,b.y+b.h/2,10,0,Math.PI*2); ctx.fill();
  ctx.fillStyle='#263238'; ctx.fillRect(sx-20,b.y+20,30,12); ctx.fillRect(sx-20,b.y+b.h-40,30,12);
  ctx.fillRect(sx+b.w-10,b.y+40,30,12); ctx.fillRect(sx+b.w-10,b.y+b.h-60,30,12);
  ctx.fillStyle='#f44336'; ctx.fillRect(sx+25,b.y+15,12,8); ctx.fillRect(sx+b.w-37,b.y+15,12,8);
  ctx.fillStyle='#333'; ctx.fillRect(sx,b.y-20,b.w,10);
  ctx.fillStyle = b.hp &gt; b.maxHp*0.3 ? '#f44336' : '#ff1744';
  ctx.fillRect(sx+1,b.y-19,(b.w-2)*(b.hp/b.maxHp),8);
  ctx.fillStyle='#fff'; ctx.font='10px monospace'; ctx.fillText(`STAGE${currentStage} BOSS`,sx+10,b.y-24);
}
function drawBackground(){
  const cfg = stageConfig[currentStage-1];
  const grad = ctx.createLinearGradient(0,0,0,GAME_H);
  grad.addColorStop(0,cfg.bgTop); grad.addColorStop(0.4,cfg.bgMid); grad.addColorStop(0.7,cfg.bgLow); grad.addColorStop(1,cfg.bgGround);
  ctx.fillStyle=grad; ctx.fillRect(0,0,GAME_W,GAME_H);
  ctx.fillStyle='#fff';
  for(let i=0;i&lt;60;i++){
    let sx = ((i*137+50)%GAME_W + GAME_W - cameraX*0.05%GAME_W)%GAME_W;
    let sy = (i*89+20)%(GAME_H*0.5);
    let s = i%3===0 ? 2 : 1;
    ctx.globalAlpha = 0.3 + Math.sin(frameCount*0.02+i)*0.3;
    ctx.fillRect(sx,sy,s,s);
  }
  ctx.globalAlpha=1;
  if(currentStage ===1){
    ctx.fillStyle='#1a3328';
    for(let i=0;i&lt;12;i++){
      let mx = i*200 - (cameraX*0.15)%2400;
      let mh = 80 + (i*47)%60;
      ctx.beginPath(); ctx.moveTo(mx,GAME_H-40); ctx.lineTo(mx+100,GAME_H-40-mh); ctx.lineTo(mx+200,GAME_H-40); ctx.fill();
    }
    ctx.fillStyle='#0d2818';
    for(let i=0;i&lt;20;i++){
      let tx = i*150 - (cameraX*0.3)%3000;
      let th = 40 + (i*31)%30;
      ctx.fillRect(tx+15,GAME_H-40-th,10,th);
      ctx.beginPath(); ctx.moveTo(tx,GAME_H-40-th+10); ctx.lineTo(tx+20,GAME_H-40-th-25); ctx.lineTo(tx+40,GAME_H-40-th+10); ctx.fill();
    }
  }else if(currentStage ===2){
    ctx.fillStyle='#263238';
    for(let i=0;i&lt;15;i++){
      let mx = i*180 - (cameraX*0.15)%2700;
      let mh = 70 + (i*41)%50;
      ctx.fillRect(mx,GAME_H-40-mh,90,mh);
    }
  }else{
    ctx.fillStyle='#b3e5fc';
    for(let i=0;i&lt;14;i++){
      let mx = i*190 - (cameraX*0.15)%2660;
      let mh = 90 + (i*53)%60;
      ctx.beginPath(); ctx.moveTo(mx,GAME_H-40); ctx.lineTo(mx+95,GAME_H-40-mh); ctx.lineTo(mx+190,GAME_H-40); ctx.fill();
    }
  }
}
function drawPlatforms(){
  for(let p of platforms){
    let sx = p.x - cameraX;
    if(sx &gt; GAME_W+10 || sx+p.w &lt; -10) continue;
    if(p.type === 'ground'){
      ctx.fillStyle='#3e2723'; ctx.fillRect(sx,p.y,p.w,p.h);
      ctx.fillStyle='#4caf50'; ctx.fillRect(sx,p.y,p.w,6); ctx.fillStyle='#2e7d32'; ctx.fillRect(sx,p.y+6,p.w,4);
      ctx.fillStyle='#5d4037'; for(let dx=0;dx&lt;p.w;dx+=20){ ctx.fillRect(sx+dx+5,p.y+15,8,4); ctx.fillRect(sx+dx+2,p.y+25,6,3); }
    }else if(p.type === 'platform'){
      ctx.fillStyle='#78909c'; ctx.fillRect(sx,p.y,p.w,p.h);
      ctx.fillStyle='#90a4ae'; ctx.fillRect(sx,p.y,p.w,4); ctx.fillStyle='#546e7a'; ctx.fillRect(sx+2,p.y+6,p.w-4,4);
      ctx.fillStyle='#b0bec5'; ctx.fillRect(sx+4,p.y+2,3,3); ctx.fillRect(sx+p.w-7,p.y+2,3,3);
    }else if(p.type === 'wall'){
      ctx.fillStyle='#5d4037'; ctx.fillRect(sx,p.y,p.w,p.h);
      ctx.fillStyle='#6d4c41'; for(let dy=0;dy&lt;p.h;dy+=12){ ctx.fillRect(sx,p.y+dy,p.w,1); ctx.fillRect(sx+p.w/2,p.y+dy,1,12); }
    }else if(p.type === 'steelWall'){
      ctx.fillStyle='#263238'; ctx.fillRect(sx,p.y,p.w,p.h);
      ctx.fillStyle='#455a64'; for(let dy=0;dy&lt;p.h;dy+=15){ ctx.fillRect(sx,p.y+dy,p.w,2); ctx.fillRect(sx+p.w/2,p.y+dy,1,15); }
    }else if(p.type === 'iceWall'){
      ctx.fillStyle='#1565c0'; ctx.fillRect(sx,p.y,p.w,p.h);
      ctx.fillStyle='#64b5f6'; for(let dy=0;dy&lt;p.h;dy+=14){ ctx.fillRect(sx,p.y+dy,p.w,2); ctx.fillRect(sx+p.w/2,p.y+dy,1,14); }
    }
  }
}
function drawPowerups(){
  for(let p of powerups){
    if(!p.alive) continue;
    let sx = p.x - cameraX;
    if(sx &lt; -30 || sx &gt; GAME_W+30) continue;
    let bob = Math.sin((frameCount+p.bobTimer)*0.05)*4;
    let py = p.y + bob;
    ctx.fillStyle='#e53935'; ctx.fillRect(sx-2,py-2,28,28);
    ctx.fillStyle='#c62828'; ctx.fillRect(sx,py,24,24);
    ctx.fillStyle='#fff'; ctx.font='bold 14px monospace';
    const letterMap = {spread:'S',rapid:'R',laser:'L',life:'1UP',shield:'SH'};
    ctx.fillText(letterMap[p.type]||'?',sx+4,py+17);
    ctx.globalAlpha = 0.3 + Math.sin(frameCount*0.1)*0.2;
    ctx.fillStyle='#ffeb3b'; ctx.fillRect(sx-4,py-4,32,32);
    ctx.globalAlpha=1;
  }
}
function drawBullets(){
  for(let b of playerBullets){
    let sx = b.x - cameraX;
    if(sx &lt; -10 || sx &gt; GAME_W+10) continue;
    if(b.type === 'laser'){
      ctx.fillStyle='#00e5ff'; ctx.fillRect(sx,b.y-1,b.w,3); ctx.fillStyle='#b2ebf2'; ctx.fillRect(sx,b.y,b.w,1);
    }else if(b.type === 'spread'){
      ctx.fillStyle='#ff9800'; ctx.beginPath(); ctx.arc(sx+b.w/2,b.y+b.h/2,3,0,Math.PI*2); ctx.fill();
    }else{
      ctx.fillStyle='#ffeb3b'; ctx.fillRect(sx,b.y,b.w,b.h); ctx.fillStyle='#fff'; ctx.fillRect(sx+1,b.y+1,b.w-2,b.h-2);
    }
  }
  for(let b of enemyBullets){
    let sx = b.x - cameraX;
    if(sx &lt; -10 || sx &gt; GAME_W+10) continue;
    ctx.fillStyle='#f44336'; ctx.beginPath(); ctx.arc(sx+3,b.y+3,4,0,Math.PI*2); ctx.fill();
    ctx.fillStyle='#ff8a80'; ctx.beginPath(); ctx.arc(sx+3,b.y+3,2,0,Math.PI*2); ctx.fill();
  }
}
function drawExplosions(){
  for(let i=explosions.length-1;i&gt;=0;i--){
    let e = explosions[i];
    let sx = e.x - cameraX;
    let prog = e.timer / e.maxTimer;
    let r = e.radius * (1-prog*0.5);
    ctx.globalAlpha = prog;
    ctx.fillStyle='#ff6f00'; ctx.beginPath(); ctx.arc(sx,e.y,r,0,Math.PI*2); ctx.fill();
    ctx.fillStyle='#ffeb3b'; ctx.beginPath(); ctx.arc(sx,e.y,r*0.6,0,Math.PI*2); ctx.fill();
    ctx.fillStyle='#fff'; ctx.beginPath(); ctx.arc(sx,e.y,r*0.2,0,Math.PI*2); ctx.fill();
    ctx.globalAlpha=1;
    e.timer--;
    if(e.timer &lt;= 0) explosions.splice(i,1);
  }
}
function drawParticles(){
  for(let i=particles.length-1;i&gt;=0;i--){
    let p = particles[i];
    let sx = p.x - cameraX;
    ctx.globalAlpha = p.life / p.maxLife;
    ctx.fillStyle = p.color; ctx.fillRect(sx,p.y,p.size,p.size);
    p.x += p.vx; p.y += p.vy; p.vy += 0.1; p.life--;
    if(p.life &lt;= 0) particles.splice(i,1);
  }
  ctx.globalAlpha=1;
}
function drawHUD(){
  ctx.fillStyle='rgba(0,0,0,0.5)'; ctx.fillRect(0,0,GAME_W,36);
  ctx.fillStyle='#fff'; ctx.font='bold 16px monospace';
  ctx.fillText(`SCORE: ${score.toString().padStart(8,'0')}`,10,24);
  ctx.fillText(`HI: ${hiScore.toString().padStart(8,'0')}`,220,24);
  ctx.fillStyle='#4fc3f7'; ctx.fillText(`STAGE ${currentStage}/${MAX_STAGE}`,340,24);
  ctx.fillStyle='#e53935';
  for(let i=0;i&lt;Math.min(lives,20);i++){
    ctx.fillRect(460+i*22,10,14,16); ctx.fillStyle='#ffcc80'; ctx.fillRect(463+i*22,6,8,8); ctx.fillStyle='#e53935';
  }
  ctx.fillStyle='#ffeb3b'; ctx.font='12px monospace';
  const weaponName = {normal:'NORMAL',spread:'SPREAD',rapid:'RAPID',laser:'LASER'};
  ctx.fillText(`WEAPON: ${weaponName[player.weapon]}`,580,24);
  if(player.weaponTimer&gt;0 &amp;&amp; player.weapon!=='normal'){
    ctx.fillStyle='#333'; ctx.fillRect(580,28,80,4);
    ctx.fillStyle='#ffeb3b'; ctx.fillRect(580,28,80*(player.weaponTimer/600),4);
  }
  ctx.fillStyle='#333'; ctx.fillRect(GAME_W-160,14,150,8);
  ctx.fillStyle='#4caf50'; ctx.fillRect(GAME_W-160,14,150*Math.min(1,cameraX/(LEVEL_W-GAME_W)),8);
  ctx.fillStyle='#fff'; ctx.font='8px monospace'; ctx.fillText('PROGRESS',GAME_W-145,12);
}
function drawTitle(){
  ctx.fillStyle='#0a0a0a'; ctx.fillRect(0,0,GAME_W,GAME_H);
  ctx.strokeStyle='rgba(229,57,53,0.1)'; ctx.lineWidth=1;
  for(let i=0;i&lt;20;i++){ let y=(i*30+frameCount*0.5)%GAME_H; ctx.beginPath(); ctx.moveTo(0,y); ctx.lineTo(GAME_W,y); ctx.stroke(); }
  ctx.save(); ctx.textAlign='center';
  let titleY = 100 + Math.sin(frameCount*0.03)*8;
  ctx.fillStyle='#e53935'; ctx.font='bold 72px monospace'; ctx.fillText('CONTRA',GAME_W/2,titleY);
  ctx.fillStyle='#b71c1c'; ctx.fillText('CONTRA',GAME_W/2+3,titleY+3);
  ctx.fillStyle='#ffeb3b'; ctx.font='bold 20px monospace'; ctx.fillText('魂 斗 罗 三关完整版',GAME_W/2,titleY+40);
  if(frameCount%60 &lt; 40){ ctx.fillStyle='#fff'; ctx.font='18px monospace'; ctx.fillText('PRESS ENTER TO START',GAME_W/2,280); }
  ctx.fillStyle='#90a4ae'; ctx.font='13px monospace';
  ctx.fillText('← → / A D : MOVE    ↑ / W / SPACE : 二段跳    ↓ / S : PRONE',GAME_W/2,340);
  ctx.fillText('Z : SHOOT    X : BOMB（清屏炸弹）',GAME_W/2,365);
  ctx.fillStyle='#ffeb3b'; ctx.font='14px monospace'; ctx.fillText(`HI-SCORE: ${hiScore.toString().padStart(8,'0')}`,GAME_W/2,420);
  ctx.restore();
}
function drawGameOver(){
  ctx.fillStyle='rgba(0,0,0,0.7)'; ctx.fillRect(0,0,GAME_W,GAME_H);
  ctx.save(); ctx.textAlign='center';
  ctx.fillStyle='#e53935'; ctx.font='bold 56px monospace'; ctx.fillText('GAME OVER',GAME_W/2,GAME_H/2-30);
  ctx.fillStyle='#fff'; ctx.font='20px monospace'; ctx.fillText(`FINAL SCORE: ${score.toString().padStart(8,'0')}`,GAME_W/2,GAME_H/2+20);
  if(frameCount%60&lt;40){ ctx.fillStyle='#ffeb3b'; ctx.font='16px monospace'; ctx.fillText('PRESS ENTER TO RETURN TITLE',GAME_W/2,GAME_H/2+70); }
  ctx.restore();
}
function drawWin(){
  ctx.fillStyle='rgba(0,0,0,0.7)'; ctx.fillRect(0,0,GAME_W,GAME_H);
  ctx.save(); ctx.textAlign='center';
  ctx.fillStyle='#4caf50'; ctx.font='bold 48px monospace'; ctx.fillText('ALL MISSIONS COMPLETE!',GAME_W/2,GAME_H/2-40);
  ctx.fillStyle='#ffeb3b'; ctx.font='bold 24px monospace'; ctx.fillText(`TOTAL SCORE: ${score.toString().padStart(8,'0')}`,GAME_W/2,GAME_H/2+10);
  ctx.fillStyle='#fff'; ctx.font='16px monospace'; ctx.fillText('ALL 3 STAGES CLEARED, ALIEN FORCES DEFEATED!',GAME_W/2,GAME_H/2+50);
  if(frameCount%60&lt;40){ ctx.fillStyle='#90a4ae'; ctx.font='14px monospace'; ctx.fillText('PRESS ENTER TO PLAY AGAIN',GAME_W/2,GAME_H/2+90); }
  ctx.restore();
}

// 玩家死亡
function playerDie(){
  if(player.invincible &gt; 0) return;
  player.alive = false;
  player.respawnTimer = 90;
  screenShake = 20;
  playSound('hit');
  explosions.push({x:player.x,y:player.y+20,radius:25,timer:25,maxTimer:25});
  for(let i=0;i&lt;15;i++){
    particles.push({
      x:player.x+14,y:player.y+20,
      vx:(Math.random()-0.5)*6,vy:(Math.random()-0.5)*6-3,
      size:2+Math.random()*3,color:['#e53935','#ffeb3b','#ff9800'][Math.floor(Math.random()*3)],
      life:30+Math.random()*20,maxLife:50
    });
  }
}
// 玩家射击
function shoot(){
  const bx = player.x + (player.dir===1 ? player.w : -8);
  const by = player.y + (player.prone ? 26 : 14);
  let bvx = player.dir * 8;
  let bvy = 0;
  if(keys['ArrowUp'] || keys['KeyW']){ bvy = -6; bvx *= 0.6; }
  if(player.weapon === 'spread'){
    playSound('spread');
    for(let a=-0.3;a&lt;=0.3;a+=0.15){
      playerBullets.push({
        x:bx,y:by,w:6,h:6,
        vx:bvx*Math.cos(a) - bvy*Math.sin(a),
        vy:bvx*Math.sin(a) + bvy*Math.cos(a),
        type:'spread',life:60
      });
    }
  }else if(player.weapon === 'laser'){
    playSound('spread');
    playerBullets.push({x:bx,y:by,w:30,h:3,vx:bvx*1.5,vy:bvy*1.5,type:'laser',life:40});
  }else{
    playSound('shoot');
    playerBullets.push({x:bx,y:by,w:8,h:3,vx:bvx,vy:bvy,type:'normal',life:80});
  }
  player.shooting = true;
  setTimeout(()=&gt;player.shooting=false,80);
}

// 更新玩家逻辑
function updatePlayer(){
  if(!player.alive){
    player.respawnTimer--;
    if(player.respawnTimer &lt;= 0){
      player.alive = true;
      player.x = cameraX + 100;
      player.y = 200;
      player.vx = 0; player.vy = 0;
      player.invincible = 120;
      player.weapon = 'normal';
    }
    return;
  }
  const spd = 3;
  if(keys['ArrowLeft']||keys['KeyA']){ player.vx=-spd; player.dir=-1; }
  else if(keys['ArrowRight']||keys['KeyD']){ player.vx=spd; player.dir=1; }
  else player.vx=0;
  player.prone = (keys['ArrowDown']||keys['KeyS']) &amp;&amp; player.onGround;
  if(player.prone){ player.h=20; player.vx*=0.5; }else player.h=40;

  if(keys['Space'] || keys['ArrowUp'] || keys['KeyW']){
    if(player.onGround){
      player.vy = -10;
      player.jumpCount = 1;
      player.onGround = false;
      playSound('jump');
    }else if(player.jumpCount === 1){
      player.vy = -9;
      player.jumpCount = 2;
      playSound('jump');
    }
    keys['Space'] = false;
    keys['ArrowUp'] = false;
    keys['KeyW'] = false;
  }

  player.vy += GRAVITY;
  player.x += player.vx;
  player.y += player.vy;
  resolveCollision(player);
  if(player.x &lt; cameraX) player.x = cameraX;
  if(player.y &gt; GAME_H+50){ playerDie(); return; }

  if(player.shootTimer&gt;0) player.shootTimer--;
  let cd = player.weapon === 'rapid' ? 4 : player.shootCooldown;
  if(keys['KeyZ'] &amp;&amp; player.shootTimer &lt;= 0){ shoot(); player.shootTimer = cd; }

  if(keys['KeyX'] &amp;&amp; !player._bombUsed){
    player._bombUsed = true;
    for(let e of enemies){
      if(e.alive &amp;&amp; Math.abs(e.x-player.x)&lt;400){
        e.alive = false; score += 100;
        explosions.push({x:e.x+e.w/2,y:e.y+e.h/2,radius:30,timer:20,maxTimer:20});
      }
    }
    if(boss &amp;&amp; boss.alive){
      boss.hp -= 10;
      if(boss.hp &lt;= 0){
        boss.alive = false; bossDefeated = true; score += 5000; screenShake = 30; playSound('explosion');
        for(let k=0;k&lt;30;k++){
          explosions.push({x:boss.x+Math.random()*boss.w,y:boss.y+Math.random()*boss.h,radius:15+Math.random()*20,timer:20+Math.random()*20,maxTimer:40});
        }
        setTimeout(()=&gt;{
          if(currentStage &lt; MAX_STAGE){
            currentStage++;
            bossDefeated = false;
            cameraX = 0;
            player.x = 100; player.y = 300;
            generateLevel(currentStage);
            spawnEnemies(currentStage);
            boss = null;
          }else{
            gameState='win';
            if(score&gt;hiScore){ hiScore=score; localStorage.setItem('contraHi',hiScore); }
          }
        },2000);
      }
    }
    screenShake=15; playSound('explosion');
  }
  if(!keys['KeyX']) player._bombUsed = false;
  if(player.invincible&gt;0) player.invincible--;
  if(player.weaponTimer&gt;0){ player.weaponTimer--; if(player.weaponTimer&lt;=0) player.weapon='normal'; }

  // 相机跟随与边界
  let targetCam = player.x - GAME_W/3;
  cameraX += (targetCam - cameraX)*0.08;
  if(cameraX &lt; 0) cameraX = 0;
  if(cameraX &gt; LEVEL_W - GAME_W) cameraX = LEVEL_W - GAME_W;

  // 到达关卡末尾生成BOSS
  if(cameraX &gt; LEVEL_W - GAME_W - 200 &amp;&amp; !boss &amp;&amp; !bossDefeated){
    spawnBoss();
  }

  if(Math.abs(player.vx) &gt; 0.1 &amp;&amp; player.onGround) player.animFrame++;
}

// 更新子弹
function updateBullets(){
  for(let i=playerBullets.length-1;i&gt;=0;i--){
    let b = playerBullets[i];
    b.x += b.vx; b.y += b.vy; b.life--;
    if(b.life &lt;= 0 || b.x &lt; cameraX-50 || b.x &gt; cameraX+GAME_W+50){
      playerBullets.splice(i,1); continue;
    }
    // 击中敌人
    for(let e of enemies){
      if(e.alive &amp;&amp; rectCollide(b,e)){
        e.hp--;
        playerBullets.splice(i,1);
        if(e.hp &lt;= 0){
          e.alive = false; score += e.type==='turret'?200:100;
          explosions.push({x:e.x+e.w/2,y:e.y+e.h/2,radius:20,timer:15,maxTimer:15});
          playSound('explosion');
        }
        break;
      }
    }
    // 击中BOSS
    if(boss &amp;&amp; boss.alive &amp;&amp; rectCollide(b,boss)){
      boss.hp--;
      playerBullets.splice(i,1);
      if(boss.hp &lt;= 0){
        boss.alive = false; bossDefeated = true; score += 5000; screenShake = 30; playSound('explosion');
        for(let k=0;k&lt;30;k++){
          explosions.push({x:boss.x+Math.random()*boss.w,y:boss.y+Math.random()*boss.h,radius:15+Math.random()*20,timer:20+Math.random()*20,maxTimer:40});
        }
        setTimeout(()=&gt;{
          if(currentStage &lt; MAX_STAGE){
            currentStage++;
            bossDefeated = false;
            cameraX = 0;
            player.x = 100; player.y = 300;
            generateLevel(currentStage);
            spawnEnemies(currentStage);
            boss = null;
          }else{
            gameState='win';
            if(score&gt;hiScore){ hiScore=score; localStorage.setItem('contraHi',hiScore); }
          }
        },2000);
      }
    }
  }
  for(let i=enemyBullets.length-1;i&gt;=0;i--){
    let b = enemyBullets[i];
    b.x += b.vx; b.y += b.vy; b.life--;
    if(b.life &lt;= 0 || b.x &lt; cameraX-50 || b.x &gt; cameraX+GAME_W+50){
      enemyBullets.splice(i,1); continue;
    }
    if(player.alive &amp;&amp; rectCollide(b,player)){
      enemyBullets.splice(i,1);
      playerDie();
    }
  }
}

// 更新敌人
function updateEnemies(){
  for(let e of enemies){
    if(!e.alive) continue;
    if(e.x &lt; cameraX - 200 || e.x &gt; cameraX + GAME_W + 200) continue;
    e.animFrame++;

    if(e.type === 'soldier'){
      e.vy += GRAVITY;
      e.y += e.vy;
      resolveCollision(e);
      // 巡逻
      if(e.x &lt;= e.patrol){ e.dir = 1; e.vx = 1; }
      else if(e.x &gt;= e.patrolMax){ e.dir = -1; e.vx = -1; }
      e.x += e.vx;
      // 看到玩家射击
      if(Math.abs(e.x - player.x) &lt; 400 &amp;&amp; Math.abs(e.y - player.y) &lt; 100){
        e.dir = player.x &gt; e.x ? 1 : -1;
        e.shootTimer--;
        if(e.shootTimer &lt;= 0){
          e.shootTimer = 90;
          enemyBullets.push({x:e.x+e.w/2,y:e.y+12,w:6,h:6,vx:e.dir*4,vy:0,life:120});
        }
      }
    }else if(e.type === 'turret'){
      // 炮台瞄准
      let dx = player.x - e.x;
      let dy = player.y - e.y;
      e.angle = Math.atan2(dy, dx);
      e.shootTimer--;
      if(e.shootTimer &lt;= 0 &amp;&amp; Math.abs(e.x - player.x) &lt; 500){
        e.shootTimer = 80;
        let spd = 4;
        enemyBullets.push({
          x:e.x+16,y:e.y+14,w:6,h:6,
          vx:Math.cos(e.angle)*spd, vy:Math.sin(e.angle)*spd, life:150
        });
      }
    }else if(e.type === 'runner'){
      e.vy += GRAVITY;
      e.y += e.vy;
      resolveCollision(e);
      e.x += e.vx;
    }else if(e.type === 'flyer'){
      e.y = e.baseY + Math.sin(e.animFrame*0.05)*30;
      e.x += e.vx;
      // 追踪玩家
      if(Math.abs(e.x - player.x) &lt; 350){
        e.vy = (player.y - e.y) * 0.02;
        e.y += e.vy;
      }
      e.shootTimer = (e.shootTimer||100) - 1;
      if(e.shootTimer &lt;= 0 &amp;&amp; Math.abs(e.x - player.x) &lt; 300){
        e.shootTimer = 110;
        let dx = player.x - e.x, dy = player.y - e.y;
        let dist = Math.sqrt(dx*dx+dy*dy);
        enemyBullets.push({x:e.x+13,y:e.y+11,w:6,h:6,vx:(dx/dist)*3.5,vy:(dy/dist)*3.5,life:150});
      }
    }
  }
}

// 更新BOSS
function updateBoss(){
  if(!boss || !boss.alive) return;
  boss.moveTimer++;
  boss.shootTimer++;
  // 上下移动
  boss.y = GAME_H - 200 + Math.sin(boss.moveTimer*0.02)*40;
  // 射击
  if(boss.shootTimer &gt; 60){
    boss.shootTimer = 0;
    // 扇形弹幕
    for(let a=-0.4;a&lt;=0.4;a+=0.2){
      let dx = player.x - boss.x, dy = player.y - boss.y;
      let baseAngle = Math.atan2(dy, dx);
      let angle = baseAngle + a;
      enemyBullets.push({
        x:boss.x+boss.w/2, y:boss.y+boss.h/2, w:8,h:8,
        vx:Math.cos(angle)*3.5, vy:Math.sin(angle)*3.5, life:200
      });
    }
    playSound('boss');
  }
}

// 更新道具
function updatePowerups(){
  for(let p of powerups){
    if(!p.alive) continue;
    if(rectCollide(player,p)){
      p.alive = false;
      playSound('powerup');
      switch(p.type){
        case 'spread': player.weapon='spread'; player.weaponTimer=600; break;
        case 'rapid': player.weapon='rapid'; player.weaponTimer=600; break;
        case 'laser': player.weapon='laser'; player.weaponTimer=600; break;
        case 'life': lives = Math.min(lives+1, 99); break;
        case 'shield': player.invincible = 300; break;
      }
    }
  }
}

// 游戏初始化
function initGame(){
  score = 0; lives = 99;
  currentStage = 1;
  cameraX = 0;
  bossDefeated = false;
  player.x = 100; player.y = 300;
  player.vx = 0; player.vy = 0;
  player.weapon = 'normal'; player.weaponTimer = 0;
  player.alive = true; player.invincible = 0;
  player.jumpCount = 0;
  playerBullets.length = 0; enemyBullets.length = 0;
  explosions.length = 0; particles.length = 0;
  generateLevel(1);
  spawnEnemies(1);
  boss = null;
  gameState = 'playing';
}

// 主循环
function gameLoop(){
  frameCount++;
  if(screenShake &gt; 0) screenShake--;

  ctx.save();
  if(screenShake &gt; 0){
    ctx.translate((Math.random()-0.5)*screenShake, (Math.random()-0.5)*screenShake);
  }

  if(gameState === 'title'){
    drawTitle();
    if(keys['Enter']){
      keys['Enter'] = false;
      initGame();
    }
  }else if(gameState === 'playing'){
    updatePlayer();
    updateBullets();
    updateEnemies();
    updateBoss();
    updatePowerups();

    drawBackground();
    drawPlatforms();
    drawPowerups();
    drawBullets();
    for(let e of enemies){
      if(!e.alive) continue;
      if(e.type==='soldier') drawSoldier(e);
      else if(e.type==='turret') drawTurret(e);
      else if(e.type==='runner') drawRunner(e);
      else if(e.type==='flyer') drawFlyer(e);
    }
    drawBoss(boss);
    if(player.alive &amp;&amp; player.invincible%4&lt;2){
      drawPixelChar(player.x,player.y,player.dir,player.animFrame,player.prone,player.shooting);
    }
    drawExplosions();
    drawParticles();
    drawHUD();
  }else if(gameState === 'gameover'){
    drawBackground();
    drawPlatforms();
    drawGameOver();
    if(keys['Enter']){
      keys['Enter'] = false;
      gameState = 'title';
    }
  }else if(gameState === 'win'){
    drawBackground();
    drawWin();
    if(keys['Enter']){
      keys['Enter'] = false;
      gameState = 'title';
    }
  }

  ctx.restore();
  requestAnimationFrame(gameLoop);
}

// 启动游戏
gameLoop();
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>]]></description>
    <pubDate>Thu, 22 Oct 2026 15:22:19 +0800</pubDate>
    <dc:creator>梁苟王</dc:creator>
    <guid>https://lgw233.top/?post=4</guid>
</item>
<item>
    <title>响应式公司内部文件编号规则生成器单页源码_原生 JS+Tailwind CSS</title>
    <link>https://lgw233.top/?post=3</link>
    <description><![CDATA[<p>企业在日常运营中，常面临内部文档编号混乱、跨部门检索效率低、新员工上手难等问题 —— 同一类文件可能出现 “行政 - 2025-001”“HR25001” 等多种格式，导致重要文档查找耗时，甚至因编号不统一引发工作失误。而这款公司内部文件编号规则生成器单页源码，正是为解决此类痛点而生，基于 HTML5、Tailwind CSS 与原生 JavaScript 开发，无需复杂部署，即可快速搭建企业专属的文档编号管理工具。</p>
<p>这款单页源码的核心能力围绕 “企业文件编号全流程管理” 设计，覆盖从规则定义到导出使用的完整环节，具体包括：<br />
自定义规则配置：支持企业按 “部门 + 年份 + 文件类型 + 序列号” 等维度，自由设置编号结构（如 “技术部 - 2025 - 研发文档 - 005”），满足行政、HR、研发、财务等不同部门的个性化需求；<br />
实时预览效果：配置过程中实时显示编号生成结果，避免规则设置错误，减少后续调整成本；<br />
一键导出规则：生成的编号规则可导出为 TXT 或 JSON 格式，方便企业存档、分发，或嵌入内部 OA 系统；<br />
响应式适配：采用 Tailwind CSS 实现响应式布局，在 PC 端（办公场景）、移动端（临时查阅）均能流畅使用，无需额外开发多端版本。<br />
<img src="http://8.156.81.135/content/uploadfile/202610/940c1792653685.png" alt="" /><br />
下载地址:<br />
<a href="https://wwgl.lanzout.com/ir5rK363fxne">https://wwgl.lanzout.com/ir5rK363fxne</a><br />
密码:6dko</p>]]></description>
    <pubDate>Thu, 22 Oct 2026 15:21:07 +0800</pubDate>
    <dc:creator>梁苟王</dc:creator>
    <guid>https://lgw233.top/?post=3</guid>
</item>
<item>
    <title>通过 JavaScript 实现简单高效的平台检测</title>
    <link>https://lgw233.top/?post=2</link>
    <description><![CDATA[<p>在前端开发中，我们经常需要根据用户使用的设备或操作系统来优化体验。本文将介绍如何用一段简洁的 JavaScript 代码实现前端平台检测功能，识别用户是在 Android、iOS、Windows、macOS 还是 Linux 系统上访问。<br />
核心原理是分析浏览器提供的navigator.userAgent属性，这个属性包含了浏览器类型、操作系统等信息。我们可以通过正则表达式匹配这些信息中的特征字符串，从而判断设备平台。<br />
实现代码：</p>
<pre><code class="language-javascript">function getPlatform() {
    // 定义各平台的识别规则
    const PLATFORM_PATTERNS = {
        android: /Android/i,
        ios: /(iPhone|iPad|iPod)/i,
        windows: /Win(?:dows )?(NT|10|11)/i,
        macos: /(Mac OS|Macintosh|MacIntel)/i,
        linux: /(Linux|X11)/i
    };

    // 获取用户代理字符串
    const ua = navigator.userAgent;

    // 优先检查iOS（避免被MacOS误匹配）
    if (PLATFORM_PATTERNS.ios.test(ua)) {
        return 'ios';
    }

    // 顺序匹配其他平台
    return (
        Object.keys(PLATFORM_PATTERNS).find(platform =&gt; 
            platform !== 'ios' &amp;&amp; PLATFORM_PATTERNS[platform].test(ua)
        ) || 'unknown'
    );
}
console.log(getPlatform());</code></pre>]]></description>
    <pubDate>Thu, 22 Oct 2026 15:20:01 +0800</pubDate>
    <dc:creator>梁苟王</dc:creator>
    <guid>https://lgw233.top/?post=2</guid>
</item>
<item>
    <title>演示文章</title>
    <link>https://lgw233.top/?post=1</link>
    <description><![CDATA[<p>这是一篇演示文章</p>]]></description>
    <pubDate>Thu, 22 Oct 2026 15:17:39 +0800</pubDate>
    <dc:creator>梁苟王</dc:creator>
    <guid>https://lgw233.top/?post=1</guid>
</item>
</channel>
</rss>