Untitled
unknown
plain_text
4 days ago
8.4 kB
1
Indexable
<!doctype html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width,initial-scale=1" /> <title>Mini Browser Shooter — PWA</title> <link rel="manifest" href="manifest.json"> <meta name="theme-color" content="#0b1220" /> <style> html,body{height:100%;margin:0;font-family:Inter,system-ui,Segoe UI,Roboto,Arial} #game {display:flex;align-items:center;justify-content:center;height:100%;background:#0b1220} canvas{background:#0f1724;border-radius:8px;box-shadow:0 8px 30px rgba(2,6,23,.6)} .hud{position:fixed;left:16px;top:16px;color:#e6eef8;font-weight:600} .controls{position:fixed;right:16px;top:16px;color:#c8d7ee;font-size:13px} .footer{position:fixed;left:16px;bottom:16px;color:#94a3b8;font-size:13px} </style> </head> <body> <div id="game"> <canvas id="c" width="900" height="600"></canvas> </div> <div class="hud">Score: <span id="score">0</span> | HP: <span id="hp">100</span></div> <div class="controls">WASD/Arrows: move<br>Mouse: aim, Click: shoot</div> <div class="footer">Installable PWA shooter. Works offline after first load.</div> <script> if ('serviceWorker' in navigator) { navigator.serviceWorker.register('sw.js').then(() => { console.log('Service Worker registered'); }); } // ===== Shooter Game Code ===== const canvas = document.getElementById('c'); const ctx = canvas.getContext('2d'); const W = canvas.width, H = canvas.height; const scoreEl = document.getElementById('score'); const hpEl = document.getElementById('hp'); let keys = {}, mouse = {x: W/2, y: H/2, down: false}; const player = {x: W/2, y: H/2, r: 16, speed: 240, hp: 100, fireRate: 0.12, fireTimer: 0}; let bullets = [], enemies = [], particles = []; let score = 0, lastTime = performance.now(), spawnTimer = 0, spawnInterval = 1.2; function clamp(v,a,b){return Math.max(a,Math.min(b,v));} function rand(min,max){return Math.random()*(max-min)+min} function distance(a,b){return Math.hypot(a.x-b.x,a.y-b.y);} window.addEventListener('keydown', e => keys[e.key.toLowerCase()] = true); window.addEventListener('keyup', e => keys[e.key.toLowerCase()] = false); canvas.addEventListener('mousemove', e => { const rect = canvas.getBoundingClientRect(); mouse.x = (e.clientX - rect.left) * (canvas.width / rect.width); mouse.y = (e.clientY - rect.top) * (canvas.height / rect.height); }); canvas.addEventListener('mousedown', () => mouse.down = true); window.addEventListener('mouseup', () => mouse.down = false); function shoot() { const angle = Math.atan2(mouse.y - player.y, mouse.x - player.x); const speed = 600; bullets.push({x: player.x + Math.cos(angle)*(player.r+6), y: player.y + Math.sin(angle)*(player.r+6), vx: Math.cos(angle)*speed, vy: Math.sin(angle)*speed, r:4}); } function spawnEnemy() { const side = Math.floor(rand(0,4)); let x,y; if(side===0){ x = -20; y = rand(0,H); } else if(side===1){ x = W+20; y = rand(0,H); } else if(side===2){ x = rand(0,W); y = -20; } else { x = rand(0,W); y = H+20; } const spd = rand(40,110); enemies.push({x,y,r:14,spd, hp: 20}); } function spawnParticles(x,y,color,count=8) { for(let i=0;i<count;i++){ const angle = rand(0,Math.PI*2); const s = rand(40,240); particles.push({x,y,vx:Math.cos(angle)*s, vy:Math.sin(angle)*s, life:rand(0.4,0.9), r:rand(1,3), color}); } } function update(dt){ let dx = 0, dy = 0; if(keys['w']||keys['arrowup']) dy -= 1; if(keys['s']||keys['arrowdown']) dy += 1; if(keys['a']||keys['arrowleft']) dx -= 1; if(keys['d']||keys['arrowright']) dx += 1; if(dx!==0||dy!==0){ const len = Math.hypot(dx,dy); dx/=len; dy/=len; player.x += dx * player.speed * dt; player.y += dy * player.speed * dt; } player.x = clamp(player.x, player.r, W-player.r); player.y = clamp(player.y, player.r, H-player.r); player.fireTimer -= dt; if(mouse.down && player.fireTimer <= 0){ shoot(); player.fireTimer = player.fireRate; } for(let i=bullets.length-1;i>=0;i--){ const b = bullets[i]; b.x += b.vx * dt; b.y += b.vy * dt; if(b.x < -50 || b.x > W+50 || b.y < -50 || b.y > H+50) bullets.splice(i,1); } spawnTimer -= dt; if(spawnTimer<=0){ spawnEnemy(); spawnTimer = Math.max(0.4, spawnInterval * (0.92 - Math.min(0.6, score/40)*0.5)); } for(let i=enemies.length-1;i>=0;i--){ const e = enemies[i]; const angle = Math.atan2(player.y - e.y, player.x - e.x); e.x += Math.cos(angle) * e.spd * dt; e.y += Math.sin(angle) * e.spd * dt; if(distance(e, player) < e.r + player.r){ player.hp -= 8; spawnParticles(player.x, player.y, '255,120,90', 10); enemies.splice(i,1); if(player.hp<=0) player.hp=0; continue; } for(let j=bullets.length-1;j>=0;j--){ const b = bullets[j]; if(distance(e,b) < e.r + b.r){ e.hp -= 15; bullets.splice(j,1); spawnParticles(b.x,b.y,'255,230,120',6); if(e.hp<=0){ score += 5; scoreEl.textContent = score; spawnParticles(e.x,e.y,'180,255,160',14); enemies.splice(i,1); } break; } } } for(let i=particles.length-1;i>=0;i--){ const p = particles[i]; p.x += p.vx * dt; p.y += p.vy * dt; p.vx *= 0.98; p.vy *= 0.98; p.life -= dt; if(p.life <= 0) particles.splice(i,1); } hpEl.textContent = Math.max(0, Math.round(player.hp)); } function draw(){ ctx.clearRect(0,0,W,H); ctx.save(); ctx.globalAlpha = 0.06; ctx.strokeStyle = '#6b7280'; for(let gx=0; gx<W; gx+=40){ ctx.beginPath(); ctx.moveTo(gx,0); ctx.lineTo(gx,H); ctx.stroke(); } for(let gy=0; gy<H; gy+=40){ ctx.beginPath(); ctx.moveTo(0,gy); ctx.lineTo(W,gy); ctx.stroke(); } ctx.restore(); for(const p of particles){ ctx.beginPath(); ctx.arc(p.x,p.y,p.r,0,Math.PI*2); ctx.fillStyle = `rgba(${p.color}, ${Math.max(0, p.life/1)})`; ctx.fill(); } for(const e of enemies){ ctx.beginPath(); ctx.arc(e.x,e.y,e.r,0,Math.PI*2); ctx.fillStyle = '#ff6b6b'; ctx.fill(); ctx.beginPath(); ctx.arc(e.x,e.y, e.r-5,0,Math.PI*2); ctx.fillStyle = '#7b2b2b'; ctx.fill(); } for(const b of bullets){ ctx.beginPath(); ctx.arc(b.x,b.y,b.r,0,Math.PI*2); ctx.fillStyle = '#ffd166'; ctx.fill(); } ctx.save(); const ang = Math.atan2(mouse.y - player.y, mouse.x - player.x); ctx.translate(player.x, player.y); ctx.rotate(ang); ctx.beginPath(); ctx.arc(0,0,player.r,0,Math.PI*2); ctx.fillStyle='#60a5fa'; ctx.fill(); ctx.fillStyle = '#1e293b'; ctx.fillRect(0, -6, player.r+10, 12); ctx.beginPath(); ctx.arc(6,0,4,0,Math.PI*2); ctx.fillStyle='#c7ddff'; ctx.fill(); ctx.restore(); ctx.beginPath(); ctx.arc(mouse.x, mouse.y, 8,0,Math.PI*2); ctx.strokeStyle='rgba(200,220,255,0.6)'; ctx.stroke(); ctx.beginPath(); ctx.moveTo(mouse.x-14,mouse.y); ctx.lineTo(mouse.x+14,mouse.y); ctx.moveTo(mouse.x,mouse.y-14); ctx.lineTo(mouse.x,mouse.y+14); ctx.stroke(); } function loop(t){ const dt = Math.min(0.05, (t - lastTime) / 1000); lastTime = t; update(dt); draw(); if(player.hp <= 0){ ctx.fillStyle = 'rgba(2,6,23,0.6)'; ctx.fillRect(0,0,W,H); ctx.fillStyle = '#fff'; ctx.font = '40px sans-serif'; ctx.textAlign = 'center'; ctx.fillText('GAME OVER', W/2, H/2 - 20); ctx.font = '18px sans-serif'; ctx.fillText('Press R to restart', W/2, H/2 + 20); if(keys['r']) reset(); } else { requestAnimationFrame(loop); } } function reset(){ bullets = []; enemies = []; particles = []; score = 0; player.hp = 100; scoreEl.textContent = 0; hpEl.textContent = 100; lastTime = performance.now(); spawnTimer = 0; requestAnimationFrame(loop); } for(let i=0;i<3;i++) spawnEnemy(); requestAnimationFrame(loop); </script></body> </html>
Editor is loading...
Leave a Comment