<!DOCTYPE html> <html> <head> <title>Tank Battle</title> <style> body { margin: 0; overflow: hidden; background: #333; font-family: Arial; } #gameCanvas { background-repeat: repeat; } #instructions { position: fixed; top: 10px; right: 10px; color: white; background: rgba(0,0,0,0.7); padding: 10px; border-radius: 5px; z-index: 1000; } #weaponInfo { position: fixed; top: 150px; right: 10px; color: white; background: rgba(0,0,0,0.7); padding: 10px; border-radius: 5px; z-index: 1000; font-size: 18px; } .button { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); padding: 20px 40px; font-size: 24px; background: #4CAF50; color: white; border: none; border-radius: 5px; cursor: pointer; display: none; z-index: 1000; } #countdown { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 72px; color: white; text-shadow: 2px 2px 4px rgba(0,0,0,0.5); z-index: 1000; display: none; } #winMessage { font-size: 72px; background: none; top: 30%; /* 화면의 위쪽으로 이동 */ left: 50%; transform: translate(-50%, -50%); z-index: 1001; /* 다음 라운드 버튼보다 위로 설정 */ } #nextRound { top: 80%; /* 화면의 아래쪽으로 이동 */ z-index: 1000; /* "You Win"보다 아래로 설정 */ } #nextRound { top: 80%; /* 아래쪽으로 이동 */ } </style> </head> <body> <div id="instructions"> Controls:<br> WASD - Move tank<br> Mouse - Aim<br> Space - Fire<br> C - Switch Weapon<br> R - Toggle Auto-fire </div> <div id="weaponInfo">Current Weapon: Cannon</div> <div id="countdown">3</div> <button id="nextRound" class="button">Next Round</button> <button id="restart" class="button">Restart Game</button> <canvas id="gameCanvas"></canvas> <div id="shop" style="display:none; position:fixed; top:50%; left:50%; transform:translate(-50%,-50%); background:rgba(0,0,0,0.9); padding:20px; border-radius:10px; color:white; z-index:1000;"> <h2>Tank Shop</h2> <div style="display:flex; gap:20px;"> <div id="tank1" style="text-align:center;"> <h3>PZ.IV</h3> <img src="player2.png" width="90" height="50"> <p>300 Gold</p> <p style="color: #4CAF50;">+50% HP</p> <button onclick="buyTank('player2.png', 300, 'tank1')">Buy</button> </div> <div id="tank2" style="text-align:center;"> <h3>TIGER</h3> <img src="player3.png" width="110" height="55"> <p>500 Gold</p> <p style="color: #4CAF50;">+100% HP</p> <p style="color: #ff6b6b;">-30% Speed</p> <button onclick="buyTank('player3.png', 500, 'tank2')">Buy</button> </div> <div id="bf109" style="text-align:center;"> <h3>BF-109</h3> <img src="bf109.png" width="100" height="100"> <p>1000 Gold</p> <p style="color: #4CAF50;">Air support from BF-109</p> <button onclick="buyBF109()">Buy</button> </div> <div id="ju87" style="text-align:center;"> <h3>JU-87</h3> <img src="ju87.png" width="100" height="100"> <p>1500 Gold</p> <p style="color: #4CAF50;">Get ju-87 air support</p> <button onclick="buyJU87()">Buy</button> </div> <div id="apcr" style="text-align:center;"> <h3>APCR</h3> <img src="apcr.png" width="80" height="20"> <!-- 여기를 80x20으로 수정 --> <p>1000 Gold</p> <p style="color: #4CAF50;">+100% Bullet Speed</p> <button onclick="buyAPCR()">Buy</button> </div> </div> </div> <button id="bossButton" class="button">Fight Boss!</button> <div id="winMessage" class="button" style="font-size: 72px; background: none;">You Win!</div> <script> const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); const nextRoundBtn = document.getElementById('nextRound'); const restartBtn = document.getElementById('restart'); const weaponInfo = document.getElementById('weaponInfo'); const countdownEl = document.getElementById('countdown'); const bossButton = document.getElementById('bossButton'); canvas.width = window.innerWidth; canvas.height = window.innerHeight; // Game state let currentRound = 1; let gameOver = false; let currentWeapon = 'cannon'; let enemies = []; let bullets = []; let items = []; let lastShot = 0; let isCountingDown = true; let countdownTime = 3; let autoFire = false; let gold = 0; let isBossStage = false; let effects = []; let hasAPCR = false; // APCR 구매 여부 let hasBF109 = false; // BF-109 구매 여부 let hasJU87 = false; // JU-87 구매 여부 let lastJU87Spawn = 0; // 마지막 JU-87 생성 시간 let supportUnits = []; // 지원 유닛 배열 let lastSupportSpawn = 0; // 마지막 지원 유닛 생성 시간 // Load assets const backgroundImg = new Image(); backgroundImg.src = 'city.png'; const playerImg = new Image(); playerImg.src = 'player.png'; const enemyImg = new Image(); enemyImg.src = 'enemy.png'; const bulletImg = new Image(); // APCR 총알 이미지 bulletImg.src = 'apcr2.png'; // Audio setup const cannonSound = new Audio('firemn.ogg'); const machinegunSound = new Audio('firemg.ogg'); const enemyFireSound = new Audio('fireenemy.ogg'); const bgm = new Audio('BGM2.ogg'); const countSound = new Audio('count.ogg'); const deathSound = new Audio('death.ogg'); bgm.loop = true; enemyFireSound.volume = 0.5; const weapons = { cannon: { fireRate: 1000, damage: 0.25, bulletSize: 5, sound: cannonSound }, machinegun: { fireRate: 200, damage: 0.05, bulletSize: 2, sound: machinegunSound } }; // Player setup const player = { x: canvas.width/2, y: canvas.height/2, speed: 5, angle: 0, width: 100, height: 45, health: 1000, maxHealth: 1000 }; function startCountdown() { isCountingDown = true; countdownTime = 3; countdownEl.style.display = 'block'; countdownEl.textContent = countdownTime; bgm.pause(); countSound.play(); const countInterval = setInterval(() => { countdownTime--; if(countdownTime <= 0) { clearInterval(countInterval); countdownEl.style.display = 'none'; isCountingDown = false; bgm.play(); } countdownEl.textContent = countdownTime > 0 ? countdownTime : 'GO!'; }, 1000); } class Effect { constructor(x, y, duration, type, angle = 0, parent = null) { this.x = x; this.y = y; this.startTime = Date.now(); this.duration = duration; this.type = type; this.angle = angle; this.parent = parent; // 부모 유닛 (발사한 유닛) this.offset = { x: Math.cos(angle) * 30, y: Math.sin(angle) * 30 }; // 부모로부터의 오프셋 this.img = new Image(); this.img.src = type === 'death' ? 'bang.png' : 'fire2.png'; } update() { if(this.parent && this.type === 'fire') { this.x = this.parent.x + this.offset.x; this.y = this.parent.y + this.offset.y; this.angle = this.parent.angle; } } isExpired() { return Date.now() - this.startTime > this.duration; } } class SupportUnit { constructor(yPosition) { this.x = 0; this.y = yPosition; this.speed = 5; this.lastShot = 0; this.width = 100; this.height = 100; this.angle = 0; // 항상 오른쪽을 향함 this.img = new Image(); this.img.src = 'bf109.png'; this.hasPlayedSound = false; // 소리 재생 여부 추가 } update() { // 이동 this.x += this.speed; // 발사 (1초에 5발) const now = Date.now(); if (now - this.lastShot > 200) { this.shoot(); this.lastShot = now; } return this.x < canvas.width; } shoot() { // 카운트다운 중이 아닐 때만 소리 재생 if (!this.hasPlayedSound && !isCountingDown) { new Audio('bf109mg.ogg').play(); this.hasPlayedSound = true; } bullets.push({ x: this.x + Math.cos(this.angle) * 30, y: this.y + Math.sin(this.angle) * 30, angle: this.angle, speed: 10, isEnemy: false, damage: weapons.machinegun.damage, size: weapons.machinegun.bulletSize }); } } class JU87 { constructor() { this.x = canvas.width; this.y = 50; this.speed = 5; this.width = 100; this.height = 100; this.angle = Math.PI; this.img = new Image(); this.img.src = 'ju87.png'; this.target = null; this.lastShot = 0; this.spawnTime = Date.now(); this.hasPlayedSound = false; this.hasPlayedMGSound = false; this.isReturning = false; this.circleAngle = 0; this.returningToCenter = false; // 중앙으로 돌아가는 상태 추가 } selectTarget() { return enemies.length > 0 ? enemies[Math.floor(Math.random() * enemies.length)] : null; } shoot() { // 카운트다운 중이 아닐 때만 소리 재생 if (!this.hasPlayedMGSound && !isCountingDown) { const mgSound = new Audio('ju87mg.ogg'); mgSound.volume = 1.0; mgSound.currentTime = 0; mgSound.play().catch(error => console.error('Audio play failed:', error)); this.hasPlayedMGSound = true; } // 100x100 픽셀 기준으로 날개 위치 좌표 설정 [[20, 50], [80, 50]].forEach(([x, y]) => { const offsetX = x - 50; const offsetY = y - 50; const rotatedX = this.x + (Math.cos(this.angle) * offsetX - Math.sin(this.angle) * offsetY); const rotatedY = this.y + (Math.sin(this.angle) * offsetX + Math.cos(this.angle) * offsetY); bullets.push({ x: rotatedX, y: rotatedY, angle: this.angle, speed: 10, isEnemy: false, damage: weapons.machinegun.damage * 2, size: weapons.machinegun.bulletSize }); }); } update() { if (!this.hasPlayedSound) { const sirenSound = new Audio('ju87siren.ogg'); sirenSound.volume = 1.0; sirenSound.play().catch(error => console.error('Audio play failed:', error)); this.hasPlayedSound = true; } const timeSinceSpawn = Date.now() - this.spawnTime; const centerX = canvas.width / 2; const centerY = canvas.height / 2; if (timeSinceSpawn > 5000) { if (!this.isReturning) { this.isReturning = true; this.target = null; this.angle = Math.atan2(centerY - this.y, centerX - this.x); } else { this.angle = Math.PI; this.x -= this.speed; return this.x > 0; } } else if (this.returningToCenter) { const distToCenter = Math.hypot(this.x - centerX, this.y - centerY); if (distToCenter > 50) { this.angle = Math.atan2(centerY - this.y, centerX - this.x); } else { this.returningToCenter = false; this.target = this.selectTarget(); } } else if (this.target) { const distToTarget = Math.hypot(this.x - this.target.x, this.y - this.target.y); if (!enemies.includes(this.target) || distToTarget < 30) { this.returningToCenter = true; this.target = null; } else { this.angle = Math.atan2(this.target.y - this.y, this.target.x - this.x); } } else { this.target = this.selectTarget(); if (!this.target) { this.circleAngle += 0.02; const radius = Math.min(canvas.width, canvas.height) / 4; const targetX = centerX + Math.cos(this.circleAngle) * radius; const targetY = centerY + Math.sin(this.circleAngle) * radius; this.angle = Math.atan2(targetY - this.y, targetX - this.x); } } this.x += Math.cos(this.angle) * this.speed; this.y += Math.sin(this.angle) * this.speed; if (!this.returningToCenter && !this.isReturning && this.target && Date.now() - this.lastShot > 200) { this.shoot(); this.lastShot = Date.now(); } return true; } } class Enemy { constructor(isBoss = false) { this.x = Math.random() * canvas.width; this.y = Math.random() * canvas.height; this.health = isBoss ? 15000 : 1000; this.maxHealth = this.health; this.speed = isBoss ? 1 : 2; this.lastShot = 0; this.shootInterval = isBoss ? 1000 : 1000; this.angle = 0; this.width = 100; this.height = 45; this.moveTimer = 0; this.moveInterval = Math.random() * 2000 + 1000; this.moveAngle = Math.random() * Math.PI * 2; this.isBoss = isBoss; if (isBoss) { this.enemyImg = new Image(); this.enemyImg.src = 'boss.png'; } else if (currentRound >= 7) { this.enemyImg = new Image(); this.enemyImg.src = 'enemy3.png'; } else if (currentRound >= 4) { this.enemyImg = new Image(); this.enemyImg.src = 'enemy2.png'; } } update() { if(isCountingDown) return; const now = Date.now(); if (now - this.moveTimer > this.moveInterval) { this.moveAngle = Math.random() * Math.PI * 2; this.moveTimer = now; } this.x += Math.cos(this.moveAngle) * this.speed; this.y += Math.sin(this.moveAngle) * this.speed; this.x = Math.max(this.width/2, Math.min(canvas.width - this.width/2, this.x)); this.y = Math.max(this.height/2, Math.min(canvas.height - this.height/2, this.y)); this.angle = Math.atan2(player.y - this.y, player.x - this.x); if (now - this.lastShot > this.shootInterval && !isCountingDown) { this.shoot(); this.lastShot = now; } } shoot() { const sound = this.isBoss ? new Audio('firemn.ogg') : enemyFireSound.cloneNode(); sound.play(); // 발사 이펙트 추가 effects.push(new Effect( this.x + Math.cos(this.angle) * 30, this.y + Math.sin(this.angle) * 30, 500, 'fire', this.angle, this // 자신을 부모로 전달 )); bullets.push({ x: this.x + Math.cos(this.angle) * 30, y: this.y + Math.sin(this.angle) * 30, angle: this.angle, speed: this.isBoss ? 10 : 5, isEnemy: true, size: this.isBoss ? 5 : 3, damage: this.isBoss ? 300 : 150 }); } } function showShop() { document.getElementById('shop').style.display = 'block'; } // 플레이어의 기본 상태를 저장 const defaultPlayerStats = { maxHealth: 1000, speed: 5, width: 100, height: 45 }; function buyTank(tankImg, cost, tankId) { if (gold >= cost) { gold -= cost; playerImg.src = tankImg; document.getElementById(tankId).style.display = 'none'; document.getElementById('shop').style.display = 'none'; if (tankId === 'tank1') { // PZ.IV player.maxHealth = 1500; player.speed = defaultPlayerStats.speed; // 기본 이동속도로 복구 player.width = 90; // PZ.IV의 크기 설정 player.height = 50; // PZ.IV의 크기 설정 } else if (tankId === 'tank2') { // TIGER player.maxHealth = 2000; player.speed = defaultPlayerStats.speed * 0.7; player.width = 100; // TIGER는 기본 크기 유지 player.height = 45; // TIGER는 기본 크기 유지 } player.health = player.maxHealth; } } function buyAPCR() { if (gold >= 1000 && !hasAPCR) { gold -= 1000; hasAPCR = true; document.getElementById('apcr').style.display = 'none'; document.getElementById('shop').style.display = 'none'; } } function buyBF109() { if (gold >= 1000 && !hasBF109) { gold -= 1000; hasBF109 = true; document.getElementById('bf109').style.display = 'none'; document.getElementById('shop').style.display = 'none'; } } function buyJU87() { if (gold >= 1500 && !hasJU87) { gold -= 1500; hasJU87 = true; document.getElementById('ju87').style.display = 'none'; document.getElementById('shop').style.display = 'none'; lastJU87Spawn = Date.now(); // 구매 즉시 스폰 타이머 초기화 } } function initRound() { enemies = []; for(let i = 0; i < 1 * currentRound; i++) { enemies.push(new Enemy()); } player.health = player.maxHealth; bullets = []; items = []; supportUnits = []; lastSupportSpawn = 0; // 카운트다운 시작 startCountdown(); // 카운트다운이 끝나면 JU87 스폰 setTimeout(() => { if (hasJU87) { supportUnits.push(new JU87()); lastJU87Spawn = Date.now(); } }, 3000); // 3초 후에 스폰 } function startBossStage() { isBossStage = true; enemies = []; enemies.push(new Enemy(true)); player.health = player.maxHealth; bullets = []; items = []; document.getElementById('bossButton').style.display = 'none'; bgm.src = 'BGM.ogg'; // 보스전 BGM으로 변경 startCountdown(); } canvas.addEventListener('mousemove', (e) => { player.angle = Math.atan2(e.clientY - player.y, e.clientX - player.x); }); const keys = {}; document.addEventListener('keydown', e => { keys[e.key] = true; if(e.key.toLowerCase() === 'c') { currentWeapon = currentWeapon === 'cannon' ? 'machinegun' : 'cannon'; weaponInfo.textContent = `Current Weapon: ${currentWeapon.charAt(0).toUpperCase() + currentWeapon.slice(1)}`; } else if(e.key.toLowerCase() === 'r') { autoFire = !autoFire; } }); document.addEventListener('keyup', e => keys[e.key] = false); function fireBullet() { if(isCountingDown) return; const weapon = weapons[currentWeapon]; const now = Date.now(); if ((keys[' '] || autoFire) && now - lastShot > weapon.fireRate) { weapon.sound.cloneNode().play(); effects.push(new Effect( player.x + Math.cos(player.angle) * 30, player.y + Math.sin(player.angle) * 30, 500, 'fire', player.angle, player )); bullets.push({ x: player.x + Math.cos(player.angle) * 30, y: player.y + Math.sin(player.angle) * 30, angle: player.angle, speed: hasAPCR ? 20 : 10, // APCR 적용 시 100% 증가 isEnemy: false, damage: weapon.damage, size: weapon.bulletSize, isAPCR: hasAPCR }); lastShot = now; } } function updateGame() { if(gameOver) return; if(!isCountingDown) { // 플레이어 움직임 if(keys['w']) player.y -= player.speed; if(keys['s']) player.y += player.speed; if(keys['a']) player.x -= player.speed; if(keys['d']) player.x += player.speed; player.x = Math.max(player.width/2, Math.min(canvas.width - player.width/2, player.x)); player.y = Math.max(player.height/2, Math.min(canvas.height - player.height/2, player.y)); fireBullet(); } // BF109 관련 코드 if (hasBF109 && !isCountingDown) { const now = Date.now(); if (now - lastSupportSpawn > 10000) { // 10초마다 supportUnits.push( new SupportUnit(canvas.height * 0.2), new SupportUnit(canvas.height * 0.5), new SupportUnit(canvas.height * 0.8) ); lastSupportSpawn = now; } } // JU87 관련 코드 추가 if (hasJU87 && !isCountingDown) { const now = Date.now(); if (now - lastJU87Spawn > 15000) { // 15초마다 supportUnits.push(new JU87()); lastJU87Spawn = now; } } // 모든 지원 유닛 업데이트 (BF109와 JU87 모두 처리) supportUnits = supportUnits.filter(unit => unit.update()); enemies.forEach(enemy => enemy.update()); if(!isCountingDown) { bullets = bullets.filter(bullet => { bullet.x += Math.cos(bullet.angle) * bullet.speed; bullet.y += Math.sin(bullet.angle) * bullet.speed; if(!bullet.isEnemy) { enemies = enemies.filter(enemy => { const dist = Math.hypot(bullet.x - enemy.x, bullet.y - enemy.y); if(dist < 30) { let damage = currentWeapon === 'cannon' ? 250 : 50; // 고정 데미지로 변경 enemy.health -= damage; if(enemy.health <= 0) { spawnHealthItem(enemy.x, enemy.y); gold += 100; // 죽음 이펙트와 사운드 추가 effects.push(new Effect(enemy.x, enemy.y, 1000, 'death')); deathSound.cloneNode().play(); return false; } if(player.health <= 0) { gameOver = true; restartBtn.style.display = 'block'; effects.push(new Effect(player.x, player.y, 1000, 'death')); deathSound.cloneNode().play(); } return true; } return true; }); } else { const dist = Math.hypot(bullet.x - player.x, bullet.y - player.y); if(dist < 30) { player.health -= bullet.damage || 100; if(player.health <= 0) { gameOver = true; restartBtn.style.display = 'block'; } return false; } } return bullet.x >= 0 && bullet.x <= canvas.width && bullet.y >= 0 && bullet.y <= canvas.height; }); items = items.filter(item => { const dist = Math.hypot(item.x - player.x, item.y - player.y); if(dist < 30) { player.health = Math.min(player.health + 200, player.maxHealth); return false; } return true; }); if(enemies.length === 0) { if (!isBossStage) { if(currentRound < 10) { nextRoundBtn.style.display = 'block'; showShop(); } else { document.getElementById('bossButton').style.display = 'block'; } } else { gameOver = true; document.getElementById('winMessage').style.display = 'block'; restartBtn.style.display = 'block'; bgm.pause(); // 현재 BGM 정지 const victorySound = new Audio('victory.ogg'); // 승리 사운드 생성 victorySound.play(); // 승리 사운드 재생 } } } enemies.forEach(enemy => enemy.update()); } function spawnHealthItem(x, y) { items.push({x, y}); } function drawHealthBar(x, y, health, maxHealth, width, height, color) { ctx.fillStyle = '#333'; ctx.fillRect(x - width/2, y - height/2, width, height); ctx.fillStyle = color; ctx.fillRect(x - width/2, y - height/2, width * (health/maxHealth), height); } function drawGame() { ctx.clearRect(0, 0, canvas.width, canvas.height); const pattern = ctx.createPattern(backgroundImg, 'repeat'); ctx.fillStyle = pattern; ctx.fillRect(0, 0, canvas.width, canvas.height); // 플레이어 그리기 ctx.save(); ctx.translate(player.x, player.y); ctx.rotate(player.angle); ctx.drawImage(playerImg, -player.width/2, -player.height/2, player.width, player.height); ctx.restore(); // 체력바 drawHealthBar(canvas.width/2, 30, player.health, player.maxHealth, 200, 20, 'green'); // 적 그리기 enemies.forEach(enemy => { ctx.save(); ctx.translate(enemy.x, enemy.y); ctx.rotate(enemy.angle); const img = enemy.isBoss ? enemy.enemyImg : (enemy.enemyImg || enemyImg); ctx.drawImage(img, -enemy.width/2, -enemy.height/2, enemy.width, enemy.height); ctx.restore(); drawHealthBar(enemy.x, enemy.y - 40, enemy.health, enemy.maxHealth, 60, 5, 'red'); }); supportUnits.forEach(unit => { ctx.save(); ctx.translate(unit.x, unit.y); ctx.rotate(unit.angle); ctx.drawImage(unit.img, -unit.width/2, -unit.height/2, unit.width, unit.height); ctx.restore(); }); // 총알 그리기 bullets.forEach(bullet => { if (bullet.isEnemy || !bullet.isAPCR) { ctx.beginPath(); ctx.fillStyle = bullet.isEnemy ? 'red' : 'blue'; ctx.arc(bullet.x, bullet.y, bullet.size, 0, Math.PI * 2); ctx.fill(); } else { ctx.save(); ctx.translate(bullet.x, bullet.y); ctx.rotate(bullet.angle); // 기관총일 때 크기 50% 감소 const width = currentWeapon === 'machinegun' ? 10 : 20; // 기관총일 때 10, 캐논일 때 20 const height = currentWeapon === 'machinegun' ? 5 : 10; // 기관총일 때 5, 캐논일 때 10 ctx.drawImage(bulletImg, -width/2, -height/2, width, height); ctx.restore(); } }); // 아이템 그리기 items.forEach(item => { ctx.beginPath(); ctx.fillStyle = 'green'; ctx.arc(item.x, item.y, 10, 0, Math.PI * 2); ctx.fill(); }); // UI 그리기 ctx.fillStyle = 'white'; ctx.font = '24px Arial'; ctx.fillText(`Round ${currentRound}/10`, 10, 30); ctx.fillText(`Gold: ${gold}`, 10, 60); // 이펙트 그리기 effects = effects.filter(effect => !effect.isExpired()); effects.forEach(effect => { effect.update(); // 이펙트 위치 업데이트 ctx.save(); ctx.translate(effect.x, effect.y); if(effect.type === 'fire') ctx.rotate(effect.angle); // bang.png는 1.5배 크게 const size = effect.type === 'death' ? 75 : 42; // death는 75px (1.5배), fire는 42px ctx.drawImage(effect.img, -size/2, -size/2, size, size); ctx.restore(); }); if(isCountingDown) { ctx.fillStyle = 'rgba(0, 0, 0, 0.5)'; ctx.fillRect(0, 0, canvas.width, canvas.height); } } // drawGame 함수 밖으로 이동 function gameLoop() { updateGame(); drawGame(); requestAnimationFrame(gameLoop); } nextRoundBtn.addEventListener('click', () => { currentRound++; nextRoundBtn.style.display = 'none'; document.getElementById('shop').style.display = 'none'; initRound(); }); bossButton.addEventListener('click', startBossStage); restartBtn.addEventListener('click', () => { currentRound = 1; gameOver = false; isBossStage = false; gold = 0; hasAPCR = false; // APCR 초기화 hasBF109 = false; // BF109 초기화 hasJU87 = false; supportUnits = []; // 지원 유닛 배열 초기화 restartBtn.style.display = 'none'; document.getElementById('winMessage').style.display = 'none'; document.getElementById('tank1').style.display = 'block'; document.getElementById('tank2').style.display = 'block'; document.getElementById('apcr').style.display = 'block'; document.getElementById('bf109').style.display = 'block'; // BF109 상점 아이템 다시 표시 playerImg.src = 'player.png'; bgm.src = 'BGM2.ogg'; bgm.play(); initRound(); }); Promise.all([ new Promise(resolve => backgroundImg.onload = resolve), new Promise(resolve => playerImg.onload = resolve), new Promise(resolve => enemyImg.onload = resolve) ]).then(() => { initRound(); gameLoop(); bgm.play(); }); window.addEventListener('resize', () => { canvas.width = window.innerWidth; canvas.height = window.innerHeight; }); </script> </body> </html>