飞机大战 - BOSS激光优化版
by MegaFlare60503 lines16.0 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>飞机大战 - BOSS激光优化版</title>
<style>
*{margin:0;padding:0;box-sizing:border-box;}
body{overflow:hidden;background:#000;}
#gameCanvas{display:block;}
#touchArea{position:fixed;top:0;left:0;width:100%;height:100%;z-index:3;}
.btn-group{position:fixed;bottom:20;left:0;width:100%;display:flex;justify-content:center;gap:20px;z-index:10;}
button{padding:10px 20px;font-size:16px;border:none;border-radius:8px;background:#444;color:#ccc;}
button.unlocked{background:#28a;color:#fff;}
#bossWarning{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);font-size:32px;color:red;display:none;z-index:20;}
#talentPopup,#gameOverPopup{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.7);display:none;z-index:30;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:20px;}
.talent{padding:12px 24px;background:#2288dd;color:#fff;border-radius:8px;}
.info{position:fixed;top:10;left:10;color:#fff;font-size:18px;z-index:15;}
</style>
</head>
<body>
<div class="info">分数:<span id="score">0</span>|生命:<span id="lives">3</span></div>
<div id="bossWarning">⚠️ BOSS 来袭 ⚠️</div>
<div id="talentPopup">
<h2 style="color:#fff">选择天赋</h2>
<div id="talentContainer" style="display:flex;gap:15px;"></div>
</div>
<div id="gameOverPopup">
<h2 style="color:red">游戏结束</h2>
<p style="color:#fff">最终分数:<span id="finalScore">0</span></p>
<button onclick="restartGame()">重新开始</button>
</div>
<div class="btn-group">
<button id="dodgeBtn">闪避 (未解锁)</button>
<button id="nukeBtn">核弹 (未解锁)</button>
</div>
<canvas id="gameCanvas"></canvas>
<div id="touchArea"></div>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const touchArea = document.getElementById('touchArea');
const dodgeBtn = document.getElementById('dodgeBtn');
const nukeBtn = document.getElementById('nukeBtn');
let width, height;
let isTouching = false;
let lasers = [];
let laserWarnings = [];
function resizeCanvas() {
width = window.innerWidth;
height = window.innerHeight;
canvas.width = width;
canvas.height = height;
}
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
let game = {
player: {
x: 0, y: 0, w: 50, h: 50,
speed: 5,
bulletSpeed: 10,
fireRate: 300,
isDodging: false
},
bullets: [],
enemyBullets: [],
enemies: [],
boss: null,
score: 0,
lives: 3,
lastScore: 0,
skills: {
dodge: { unlocked: false, cd: 0, cooldown: 5000 },
nuke: { unlocked: false, cd: 0, cooldown: 15000 }
},
isGameOver: false,
lastEnemySpawn: 0,
spawnRate: 1000,
bossSpawned: false,
lastShot: 0,
laserDamageActive: false,
laserHitCount: 0,
maxLaserHits: 1
};
const talents = [
{ type: 'speed', desc: '移动速度+2', value: 2 },
{ type: 'bulletSpeed', desc: '子弹速度+3', value: 3 },
{ type: 'fireRate', desc: '射速提升10%', value: 0.1 },
{ type: 'unlockDodge', desc: '解锁闪避技能', value: true },
{ type: 'unlockNuke', desc: '解锁核弹技能', value: true },
{ type: 'dodgeCD', desc: '闪避冷却-1秒', value: -1000 },
{ type: 'nukeCD', desc: '核弹冷却-3秒', value: -3000 },
{ type: 'lives', desc: '生命+1', value: 1 }
];
function checkOverlap(a, b) {
return a.x < b.x + b.w &&
a.x + a.w > b.x &&
a.y < b.y + b.h &&
a.y + a.h > b.y;
}
function initPlayer() {
game.player.x = width / 2 - game.player.w / 2;
game.player.y = height - game.player.h - 50;
game.lastShot = Date.now();
}
function drawPlayer() {
ctx.save();
if (game.player.isDodging) ctx.globalAlpha = 0.5;
ctx.fillStyle = '#4af';
ctx.beginPath();
ctx.moveTo(game.player.x + game.player.w / 2, game.player.y);
ctx.lineTo(game.player.x, game.player.y + game.player.h);
ctx.lineTo(game.player.x + game.player.w, game.player.y + game.player.h);
ctx.closePath();
ctx.fill();
ctx.restore();
}
touchArea.addEventListener('touchstart', e=>{
e.preventDefault();
isTouching = true;
updatePlayerPosition(e.touches[0]);
});
touchArea.addEventListener('touchmove', e=>{
e.preventDefault();
if(isTouching) updatePlayerPosition(e.touches[0]);
});
touchArea.addEventListener('touchend', ()=>isTouching = false);
function updatePlayerPosition(touch) {
const rect = canvas.getBoundingClientRect();
let tx = touch.clientX - rect.left;
let ty = touch.clientY - rect.top;
tx = Math.max(game.player.w/2, Math.min(width - game.player.w/2, tx));
ty = Math.max(game.player.h/2, Math.min(height - game.player.h/2, ty));
game.player.x = tx - game.player.w / 2;
game.player.y = ty - game.player.h / 2;
}
function shoot() {
if(Date.now() - game.lastShot > game.player.fireRate){
game.bullets.push({
x: game.player.x + game.player.w/2 - 2,
y: game.player.y,
w:4,h:15,
speed:game.player.bulletSpeed
});
game.lastShot = Date.now();
}
}
function drawBullets() {
ctx.fillStyle = '#fff';
game.bullets.forEach((b,i)=>{
b.y -= b.speed;
ctx.fillRect(b.x,b.y,b.w,b.h);
if(b.y<0) game.bullets.splice(i,1);
});
}
function spawnEnemy() {
if(Date.now()-game.lastEnemySpawn < game.spawnRate || game.boss) return;
game.lastEnemySpawn = Date.now();
const canShoot = Math.random() < 0.2;
game.enemies.push({
x:Math.random()*(width-40),
y:-50,w:40,h:40,
speed:2+Math.random()*2,
hp:1,canShoot,lastShot:Date.now(),
shotInterval:3000+Math.random()*2000
});
if(game.score>=1500 && !game.bossSpawned){
game.bossSpawned = true;
document.getElementById('bossWarning').style.display='flex';
setTimeout(()=>{
document.getElementById('bossWarning').style.display='none';
spawnBoss();
},1500);
}
}
function spawnBoss() {
game.boss = {
x:width/2-80,y:50,w:160,h:120,
speed:2,hp:60,lastShot:0,
bulletInterval:800,laserInterval:5000,
lastLaser:0,direction:1
};
}
function enemyShoot(enemy) {
if(Date.now()-enemy.lastShot < enemy.shotInterval) return;
enemy.lastShot = Date.now();
const angle = (Math.random()-0.5)*0.8;
game.enemyBullets.push({
x:enemy.x+enemy.w/2-3,
y:enemy.y+enemy.h,
w:6,h:12,speed:4,
vx:Math.sin(angle)*3,
vy:Math.cos(angle)*4
});
}
function createLaserWarning(x,y,w,h){
let d = document.createElement('div');
d.style.cssText = `position:absolute;left:${x}px;top:${y}px;width:${w}px;height:${h}px;background:rgba(255,0,0,0.3);z-index:4;`;
document.body.appendChild(d);
laserWarnings.push(d);
return d;
}
function clearLaserWarnings(){
laserWarnings.forEach(d=>d.parentNode&&d.remove());
laserWarnings = [];
}
function bossAttack() {
if(!game.boss) return;
if(Date.now()-game.boss.lastShot > game.boss.bulletInterval){
game.boss.lastShot = Date.now();
for(let i=0;i<7;i++){
let ang = (i-3)*0.15;
game.enemyBullets.push({
x:game.boss.x+game.boss.w/2-3,
y:game.boss.y+game.boss.h,
w:6,h:12,speed:5,
vx:Math.sin(ang)*4,
vy:Math.cos(ang)*5
});
}
}
if(Date.now()-game.boss.lastLaser > game.boss.laserInterval){
game.boss.lastLaser = Date.now();
game.laserHitCount = 0;
let lc = 3,lw=4,lh=height-game.boss.y;
for(let i=0;i<lc;i++){
let lx = game.boss.x + (i*game.boss.w)/(lc-1);
createLaserWarning(lx - lw/2, game.boss.y, lw, lh);
}
setTimeout(()=>{
clearLaserWarnings();
for(let i=0;i<lc;i++){
let lx = game.boss.x + (i*game.boss.w)/(lc-1);
let ld = document.createElement('div');
ld.style.cssText = `position:absolute;left:${lx-lw/2}px;top:${game.boss.y}px;width:${lw}px;height:${lh}px;background:rgba(255,0,0,0.7);z-index:5;`;
document.body.appendChild(ld);
lasers.push(ld);
}
game.laserDamageActive = true;
setTimeout(()=>{
lasers.forEach(d=>d.parentNode&&d.remove());
lasers = [];
game.laserDamageActive = false;
game.laserHitCount = 0;
},1000);
},1000);
}
}
function drawEnemies() {
game.enemies.forEach((e,i)=>{
e.y += e.speed;
ctx.fillStyle='#f33';
ctx.fillRect(e.x,e.y,e.w,e.h);
if(e.canShoot) enemyShoot(e);
if(e.y>height){
game.enemies.splice(i,1);
game.lives--;
updateStats();
}
});
ctx.fillStyle='#ff9900';
game.enemyBullets.forEach((b,i)=>{
b.x += b.vx;
b.y += b.vy;
ctx.fillRect(b.x,b.y,b.w,b.h);
if(b.y>height||b.x<0||b.x>width) game.enemyBullets.splice(i,1);
});
if(game.boss){
game.boss.x += game.boss.speed * game.boss.direction;
if(game.boss.x<0||game.boss.x>width-game.boss.w) game.boss.direction*=-1;
bossAttack();
ctx.fillStyle='#f99';
ctx.fillRect(game.boss.x,game.boss.y,game.boss.w,game.boss.h);
ctx.fillStyle='#f33';
ctx.fillRect(game.boss.x,game.boss.y-10,game.boss.w*(game.boss.hp/60),8);
}
}
function checkCollisions() {
game.bullets.forEach((b,bi)=>{
game.enemies.forEach((e,ei)=>{
if(checkOverlap(b,e)){
game.bullets.splice(bi,1);
e.hp--;
if(e.hp<=0){
game.enemies.splice(ei,1);
game.score+=100;
updateStats();checkTalent();
}
}
});
if(game.boss&&checkOverlap(b,game.boss)){
game.bullets.splice(bi,1);
game.boss.hp--;
if(game.boss.hp<=0){
game.boss=null;
game.score+=1000;
updateStats();checkTalent();
}
}
});
game.enemyBullets.forEach((b,i)=>{
if(!game.player.isDodging&&checkOverlap(b,game.player)){
game.enemyBullets.splice(i,1);
game.lives--;
updateStats();
if(game.lives<=0)gameOver();
}
});
if(game.laserDamageActive && game.laserHitCount < game.maxLaserHits){
lasers.forEach(laser=>{
let lr = {
x:parseInt(laser.style.left),
y:parseInt(laser.style.top),
w:parseInt(laser.style.width),
h:parseInt(laser.style.height)
};
let pr = {x:game.player.x,y:game.player.y,w:game.player.w,h:game.player.h};
if(!game.player.isDodging && checkOverlap(pr,lr)){
game.laserHitCount++;
game.lives--;
updateStats();
if(game.lives<=0)gameOver();
}
});
}
game.enemies.forEach((e,i)=>{
if(!game.player.isDodging&&checkOverlap(e,game.player)){
game.enemies.splice(i,1);
game.lives--;
updateStats();
if(game.lives<=0)gameOver();
}
});
}
function checkTalent() {
if(Math.floor(game.score/500) > Math.floor(game.lastScore/500)){
game.lastScore = game.score;
showTalentPopup();
}
}
function showTalentPopup() {
let c = document.getElementById('talentContainer');
c.innerHTML = '';
let arr = [];
while(arr.length<3){
let t = talents[Math.floor(Math.random()*talents.length)];
if(!arr.includes(t)&&!(t.type==='unlockDodge'&&game.skills.dodge.unlocked)&&!(t.type==='unlockNuke'&&game.skills.nuke.unlocked)){
arr.push(t);
}
}
arr.forEach(t=>{
let d = document.createElement('div');
d.className='talent';
d.textContent = t.desc;
d.onclick = ()=>{
applyTalent(t);
document.getElementById('talentPopup').style.display='none';
};
c.appendChild(d);
});
document.getElementById('talentPopup').style.display='flex';
}
function applyTalent(t) {
switch(t.type){
case 'speed':game.player.speed+=t.value;break;
case 'bulletSpeed':game.player.bulletSpeed+=t.value;break;
case 'fireRate':game.player.fireRate*=(1-t.value);break;
case 'unlockDodge':
game.skills.dodge.unlocked=true;
dodgeBtn.classList.add('unlocked');
dodgeBtn.innerHTML='闪避 (冷却: 5秒)';
break;
case 'unlockNuke':
game.skills.nuke.unlocked=true;
nukeBtn.classList.add('unlocked');
nukeBtn.innerHTML='核弹 (冷却: 15秒)';
break;
case 'dodgeCD':game.skills.dodge.cooldown+=t.value;break;
case 'nukeCD':game.skills.nuke.cooldown+=t.value;break;
case 'lives':game.lives+=t.value;updateStats();break;
}
}
function updateStats() {
document.getElementById('score').textContent = game.score;
document.getElementById('lives').textContent = game.lives;
if(game.skills.dodge.unlocked){
if(game.skills.dodge.cd>0){
dodgeBtn.disabled=true;
dodgeBtn.innerHTML=`闪避 (${Math.ceil(game.skills.dodge.cd/1000)}秒)`;
}else{
dodgeBtn.disabled=false;
dodgeBtn.innerHTML=`闪避 (冷却: ${game.skills.dodge.cooldown/1000}秒)`;
}
}
if(game.skills.nuke.unlocked){
if(game.skills.nuke.cd>0){
nukeBtn.disabled=true;
nukeBtn.innerHTML=`核弹 (${Math.ceil(game.skills.nuke.cd/1000)}秒)`;
}else{
nukeBtn.disabled=false;
nukeBtn.innerHTML=`核弹 (冷却: ${game.skills.nuke.cooldown/1000}秒)`;
}
}
}
dodgeBtn.onclick = ()=>{
if(game.skills.dodge.unlocked && game.skills.dodge.cd<=0){
game.player.isDodging=true;
game.skills.dodge.cd = game.skills.dodge.cooldown;
setTimeout(()=>game.player.isDodging=false,1000);
}
};
nukeBtn.onclick = ()=>{
if(game.skills.nuke.unlocked && game.skills.nuke.cd<=0){
game.enemies = [];
game.enemyBullets = [];
lasers.forEach(d=>d.remove());lasers=[];
laserWarnings.forEach(d=>d.remove());laserWarnings=[];
game.skills.nuke.cd = game.skills.nuke.cooldown;
game.score += 500;
updateStats();
}
};
function gameOver() {
game.isGameOver = true;
document.getElementById('finalScore').textContent = game.score;
document.getElementById('gameOverPopup').style.display='flex';
}
function restartGame() {
document.getElementById('gameOverPopup').style.display='none';
game = {
player: {
x: 0, y: 0, w: 50, h: 50,
speed: 5,bulletSpeed:10,fireRate:300,isDodging:false
},
bullets:[],enemyBullets:[],enemies:[],boss:null,
score:0,lives:3,lastScore:0,
skills:{
dodge:{unlocked:false,cd:0,cooldown:5000},
nuke:{unlocked:false,cd:0,cooldown:15000}
},
isGameOver:false,lastEnemySpawn:0,spawnRate:1000,
bossSpawned:false,lastShot:0,
laserDamageActive:false,laserHitCount:0,maxLaserHits:1
};
dodgeBtn.classList.remove('unlocked');
dodgeBtn.innerHTML='闪避 (未解锁)';
nukeBtn.classList.remove('unlocked');
nukeBtn.innerHTML='核弹 (未解锁)';
lasers.forEach(d=>d.remove());lasers=[];
laserWarnings.forEach(d=>d.remove());laserWarnings=[];
initPlayer();updateStats();gameLoop();
}
function gameLoop() {
if(game.isGameOver) return;
ctx.fillStyle='#111';
ctx.fillRect(0,0,width,height);
if(game.skills.dodge.cd>0) game.skills.dodge.cd-=16;
if(game.skills.nuke.cd>0) game.skills.nuke.cd-=16;
shoot();spawnEnemy();drawPlayer();drawBullets();drawEnemies();checkCollisions();
requestAnimationFrame(gameLoop);
}
initPlayer();
updateStats();
gameLoop();
</script>
</body>
</html>Game Source: 飞机大战 - BOSS激光优化版
Creator: MegaFlare60
Libraries: none
Complexity: complex (503 lines, 16.0 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: boss-megaflare60" to link back to the original. Then publish at arcadelab.ai/publish.