Spaces:
Running
Running
Update game.js
Browse files
game.js
CHANGED
|
@@ -792,7 +792,7 @@ class Fighter {
|
|
| 792 |
}
|
| 793 |
}
|
| 794 |
|
| 795 |
-
// ์ ์ ํฌ๊ธฐ ํด๋์ค -
|
| 796 |
class EnemyFighter {
|
| 797 |
constructor(scene, position) {
|
| 798 |
this.mesh = null;
|
|
@@ -838,9 +838,6 @@ class EnemyFighter {
|
|
| 838 |
this.nearbyEnemies = [];
|
| 839 |
this.avoidanceVector = new THREE.Vector3();
|
| 840 |
|
| 841 |
-
// ํ๊ดด ์ํ ์ถ๊ฐ
|
| 842 |
-
this.isDestroyed = false;
|
| 843 |
-
|
| 844 |
// ์ด๊ธฐ ๋ชฉํ ์ค์
|
| 845 |
this.selectNewPatrolTarget();
|
| 846 |
}
|
|
@@ -892,7 +889,7 @@ class EnemyFighter {
|
|
| 892 |
}
|
| 893 |
|
| 894 |
update(playerPosition, deltaTime) {
|
| 895 |
-
if (!this.mesh || !this.isLoaded
|
| 896 |
|
| 897 |
// ํํผ ํ์ด๋จธ ์
๋ฐ์ดํธ
|
| 898 |
if (this.temporaryEvadeMode && this.evadeTimer > 0) {
|
|
@@ -908,6 +905,7 @@ class EnemyFighter {
|
|
| 908 |
if (distanceToPlayer < 100) {
|
| 909 |
this.isRetreating = true;
|
| 910 |
this.aiState = 'retreat';
|
|
|
|
| 911 |
console.log(`Enemy retreating! Distance: ${distanceToPlayer.toFixed(1)}m`);
|
| 912 |
}
|
| 913 |
|
|
@@ -920,6 +918,7 @@ class EnemyFighter {
|
|
| 920 |
// ์ํ ๊ฒฐ์ - ํํด๊ฐ ์ต์ฐ์
|
| 921 |
if (this.isRetreating) {
|
| 922 |
this.aiState = 'retreat';
|
|
|
|
| 923 |
} else if (this.temporaryEvadeMode) {
|
| 924 |
this.aiState = 'evade';
|
| 925 |
} else if (distanceToPlayer <= 3000) {
|
|
@@ -957,6 +956,7 @@ class EnemyFighter {
|
|
| 957 |
this.updateBullets(deltaTime);
|
| 958 |
}
|
| 959 |
|
|
|
|
| 960 |
executeRetreat(playerPosition, deltaTime) {
|
| 961 |
// ํ๋ ์ด์ด๋ก๋ถํฐ ๋ฉ์ด์ง๋ ๋ฐฉํฅ ๊ณ์ฐ
|
| 962 |
const retreatDirection = this.position.clone().sub(playerPosition).normalize();
|
|
@@ -1294,7 +1294,7 @@ class EnemyFighter {
|
|
| 1294 |
}
|
| 1295 |
|
| 1296 |
updatePhysics(deltaTime) {
|
| 1297 |
-
if (!this.mesh
|
| 1298 |
|
| 1299 |
// ์๋ ๋ฒกํฐ ๊ณ์ฐ (ํญ์ ์ ์ง)
|
| 1300 |
const forward = new THREE.Vector3(0, 0, 1);
|
|
@@ -1482,330 +1482,17 @@ class EnemyFighter {
|
|
| 1482 |
|
| 1483 |
takeDamage(damage) {
|
| 1484 |
this.health -= damage;
|
| 1485 |
-
|
| 1486 |
-
// ์ฒด๋ ฅ์ด 0 ์ดํ๊ฐ ๋๋ฉด true ๋ฐํ (ํ๊ดด๋จ)
|
| 1487 |
-
if (this.health <= 0) {
|
| 1488 |
-
this.isDestroyed = true;
|
| 1489 |
-
return true;
|
| 1490 |
-
}
|
| 1491 |
-
return false;
|
| 1492 |
}
|
| 1493 |
|
| 1494 |
destroy() {
|
| 1495 |
-
|
| 1496 |
-
|
| 1497 |
-
if (this.isDestroyed) {
|
| 1498 |
-
console.log('Already destroyed, skipping');
|
| 1499 |
-
return;
|
| 1500 |
-
}
|
| 1501 |
-
|
| 1502 |
-
// ํ๊ดด ์ํ ์ค์
|
| 1503 |
-
this.isDestroyed = true;
|
| 1504 |
-
|
| 1505 |
-
// ๋ฉ์๊ฐ ์๋์ง ํ์ธํ๊ณ ์ ๊ฑฐ
|
| 1506 |
-
if (this.mesh) {
|
| 1507 |
-
console.log('Removing enemy mesh from scene');
|
| 1508 |
-
|
| 1509 |
-
// GLB ๋ชจ๋ธ์ ๋ณต์กํ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง ์ ์์ผ๋ฏ๋ก ์ ์ฒด ๊ณ์ธต๊ตฌ์กฐ๋ฅผ ์ํํ๋ฉฐ ์ ๊ฑฐ
|
| 1510 |
-
this.mesh.traverse((child) => {
|
| 1511 |
-
if (child.isMesh) {
|
| 1512 |
-
console.log('Disposing mesh:', child.name || 'unnamed');
|
| 1513 |
-
|
| 1514 |
-
// geometry ์ ๊ฑฐ
|
| 1515 |
-
if (child.geometry) {
|
| 1516 |
-
child.geometry.dispose();
|
| 1517 |
-
}
|
| 1518 |
-
|
| 1519 |
-
// material ์ ๊ฑฐ
|
| 1520 |
-
if (child.material) {
|
| 1521 |
-
if (Array.isArray(child.material)) {
|
| 1522 |
-
child.material.forEach(mat => {
|
| 1523 |
-
if (mat.dispose) mat.dispose();
|
| 1524 |
-
});
|
| 1525 |
-
} else {
|
| 1526 |
-
if (child.material.dispose) child.material.dispose();
|
| 1527 |
-
}
|
| 1528 |
-
}
|
| 1529 |
-
}
|
| 1530 |
-
});
|
| 1531 |
-
|
| 1532 |
-
// ์ฌ์์ ๋ฉ์ ์ ๊ฑฐ - ์ฌ๋ฌ ๋ฐฉ๋ฒ ์๋
|
| 1533 |
-
if (this.scene) {
|
| 1534 |
this.scene.remove(this.mesh);
|
| 1535 |
-
|
| 1536 |
-
|
| 1537 |
-
|
| 1538 |
-
if (this.mesh.parent) {
|
| 1539 |
-
console.log('Mesh still has parent, forcing removal from parent');
|
| 1540 |
-
this.mesh.parent.remove(this.mesh);
|
| 1541 |
-
}
|
| 1542 |
-
|
| 1543 |
-
// ๋ฉ์๋ฅผ ๋นํ์ฑํ
|
| 1544 |
-
this.mesh.visible = false;
|
| 1545 |
-
|
| 1546 |
-
// ๋ฉ์ ์ฐธ์กฐ ์์ ํ ์ ๊ฑฐ
|
| 1547 |
-
this.mesh = null;
|
| 1548 |
-
}
|
| 1549 |
-
|
| 1550 |
-
// ๋จ์ ํํ๋ค ์ ๊ฑฐ
|
| 1551 |
-
console.log(`Removing ${this.bullets.length} enemy bullets`);
|
| 1552 |
-
this.bullets.forEach(bullet => {
|
| 1553 |
-
// ํํ์ geometry์ material๋ ์ ๋ฆฌ
|
| 1554 |
-
if (bullet.geometry) bullet.geometry.dispose();
|
| 1555 |
-
if (bullet.material) bullet.material.dispose();
|
| 1556 |
-
|
| 1557 |
-
this.scene.remove(bullet);
|
| 1558 |
-
|
| 1559 |
-
// ํํ์ด ์ค์ ๋ก ์ ๊ฑฐ๋์๋์ง ํ์ธ
|
| 1560 |
-
if (bullet.parent) {
|
| 1561 |
-
bullet.parent.remove(bullet);
|
| 1562 |
-
}
|
| 1563 |
-
});
|
| 1564 |
-
this.bullets = [];
|
| 1565 |
-
|
| 1566 |
-
this.isLoaded = false;
|
| 1567 |
-
console.log('EnemyFighter destroyed successfully');
|
| 1568 |
-
}
|
| 1569 |
-
|
| 1570 |
-
// Game ํด๋์ค์ checkCollisions ๋ฉ์๋ ์์
|
| 1571 |
-
checkCollisions() {
|
| 1572 |
-
// ํ๋ ์ด์ด ํํ vs ์ ๊ธฐ ์ถฉ๋
|
| 1573 |
-
for (let i = this.fighter.bullets.length - 1; i >= 0; i--) {
|
| 1574 |
-
const bullet = this.fighter.bullets[i];
|
| 1575 |
-
|
| 1576 |
-
for (let j = this.enemies.length - 1; j >= 0; j--) {
|
| 1577 |
-
const enemy = this.enemies[j];
|
| 1578 |
-
|
| 1579 |
-
// ์ด๋ฏธ ํ๊ดด๋ ์ ์ ๊ฑด๋๋ฐ๊ธฐ
|
| 1580 |
-
if (!enemy.mesh || !enemy.isLoaded || enemy.isDestroyed) continue;
|
| 1581 |
-
|
| 1582 |
-
const distance = bullet.position.distanceTo(enemy.position);
|
| 1583 |
-
if (distance < 90) {
|
| 1584 |
-
console.log(`Hit detected! Distance: ${distance}, Enemy health: ${enemy.health}`);
|
| 1585 |
-
|
| 1586 |
-
// ํํธ ํ์ ์ถ๊ฐ
|
| 1587 |
-
this.showHitMarker(enemy.position);
|
| 1588 |
-
// ํผ๊ฒฉ ์ดํํธ ์ถ๊ฐ
|
| 1589 |
-
this.createHitEffect(enemy.position);
|
| 1590 |
-
|
| 1591 |
-
// ํํ ์ ๊ฑฐ
|
| 1592 |
-
this.scene.remove(bullet);
|
| 1593 |
-
this.fighter.bullets.splice(i, 1);
|
| 1594 |
-
|
| 1595 |
-
// ์ ๊ธฐ์ ๋ฐ๋ฏธ์ง ์
ํ๊ธฐ
|
| 1596 |
-
const isDestroyed = enemy.takeDamage(GAME_CONSTANTS.BULLET_DAMAGE);
|
| 1597 |
-
console.log(`Enemy damaged. New health: ${enemy.health}, Destroyed: ${isDestroyed}`);
|
| 1598 |
-
|
| 1599 |
-
if (isDestroyed) {
|
| 1600 |
-
console.log('Enemy destroyed! Creating explosion effect...');
|
| 1601 |
-
|
| 1602 |
-
// ํญ๋ฐ ํจ๊ณผ ์์ฑ - ์์น๋ฅผ ๋ณต์ฌํด์ ์ ๋ฌ
|
| 1603 |
-
const explosionPosition = enemy.position.clone();
|
| 1604 |
-
console.log('Explosion position:', explosionPosition);
|
| 1605 |
-
|
| 1606 |
-
// ํญ๋ฐ ํจ๊ณผ ์ฆ์ ์์ฑ
|
| 1607 |
-
this.createExplosionEffect(explosionPosition);
|
| 1608 |
-
|
| 1609 |
-
// ์ ๊ธฐ ๋ฉ์๋ฅผ ๋จผ์ ์จ๊ธฐ๊ธฐ
|
| 1610 |
-
if (enemy.mesh) {
|
| 1611 |
-
enemy.mesh.visible = false;
|
| 1612 |
-
}
|
| 1613 |
-
|
| 1614 |
-
// ์ ๊ธฐ ์ ๊ฑฐ
|
| 1615 |
-
enemy.destroy();
|
| 1616 |
-
|
| 1617 |
-
// ๋ฐฐ์ด์์ ์ ๊ฑฐ
|
| 1618 |
-
this.enemies.splice(j, 1);
|
| 1619 |
-
this.score += 100;
|
| 1620 |
-
|
| 1621 |
-
console.log(`Enemy removed. Remaining enemies: ${this.enemies.length}`);
|
| 1622 |
-
}
|
| 1623 |
-
break;
|
| 1624 |
-
}
|
| 1625 |
-
}
|
| 1626 |
-
}
|
| 1627 |
-
|
| 1628 |
-
// ์ ํํ vs ํ๋ ์ด์ด ์ถฉ๋
|
| 1629 |
-
this.enemies.forEach(enemy => {
|
| 1630 |
-
// ํ๊ดด๋ ์ ์ ์ฒ๋ฆฌํ์ง ์์
|
| 1631 |
-
if (enemy.isDestroyed) return;
|
| 1632 |
-
|
| 1633 |
-
for (let index = enemy.bullets.length - 1; index >= 0; index--) {
|
| 1634 |
-
const bullet = enemy.bullets[index];
|
| 1635 |
-
const distance = bullet.position.distanceTo(this.fighter.position);
|
| 1636 |
-
if (distance < 100) {
|
| 1637 |
-
// ํ๋ ์ด์ด ํผ๊ฒฉ ์ดํํธ
|
| 1638 |
-
this.createHitEffect(this.fighter.position);
|
| 1639 |
-
|
| 1640 |
-
// ํํ ์ ๊ฑฐ
|
| 1641 |
-
this.scene.remove(bullet);
|
| 1642 |
-
enemy.bullets.splice(index, 1);
|
| 1643 |
-
|
| 1644 |
-
if (this.fighter.takeDamage(GAME_CONSTANTS.BULLET_DAMAGE)) {
|
| 1645 |
-
// ํ๋ ์ด์ด ํ๊ดด ์ ํญ๋ฐ ํจ๊ณผ ์ถ๊ฐ
|
| 1646 |
-
this.createExplosionEffect(this.fighter.position);
|
| 1647 |
-
|
| 1648 |
-
this.endGame(false);
|
| 1649 |
-
}
|
| 1650 |
-
}
|
| 1651 |
-
}
|
| 1652 |
-
});
|
| 1653 |
-
}
|
| 1654 |
-
|
| 1655 |
-
// EnemyFighter์ update ๋ฉ์๋์๋ ์ถ๊ฐ ๋ณดํธ ์ฅ์น
|
| 1656 |
-
update(playerPosition, deltaTime) {
|
| 1657 |
-
// ํ๊ดด๋์๊ฑฐ๋ ๋ฉ์๊ฐ ์์ผ๋ฉด ์๋ฌด๊ฒ๋ ํ์ง ์์
|
| 1658 |
-
if (!this.mesh || !this.isLoaded || this.isDestroyed || !this.mesh.visible) return;
|
| 1659 |
-
|
| 1660 |
-
// ... ๋๋จธ์ง update ๋ก์ง
|
| 1661 |
-
|
| 1662 |
-
// ํํผ ํ์ด๋จธ ์
๋ฐ์ดํธ
|
| 1663 |
-
if (this.temporaryEvadeMode && this.evadeTimer > 0) {
|
| 1664 |
-
this.evadeTimer -= deltaTime;
|
| 1665 |
-
if (this.evadeTimer <= 0) {
|
| 1666 |
-
this.temporaryEvadeMode = false;
|
| 1667 |
-
}
|
| 1668 |
-
}
|
| 1669 |
-
|
| 1670 |
-
const distanceToPlayer = this.position.distanceTo(playerPosition);
|
| 1671 |
-
|
| 1672 |
-
// 100m ์ด๋ด๋ฉด ์ฆ์ ํํด ๋ชจ๋ ํ์ฑํ
|
| 1673 |
-
if (distanceToPlayer < 100) {
|
| 1674 |
-
this.isRetreating = true;
|
| 1675 |
-
this.aiState = 'retreat';
|
| 1676 |
-
console.log(`Enemy retreating! Distance: ${distanceToPlayer.toFixed(1)}m`);
|
| 1677 |
-
}
|
| 1678 |
-
|
| 1679 |
-
// 250m ์ด์ ๋จ์ด์ง๋ฉด ํํด ๋ชจ๋ ํด์
|
| 1680 |
-
if (this.isRetreating && distanceToPlayer >= this.retreatTargetDistance) {
|
| 1681 |
-
this.isRetreating = false;
|
| 1682 |
-
console.log(`Enemy retreat complete. Distance: ${distanceToPlayer.toFixed(1)}m`);
|
| 1683 |
-
}
|
| 1684 |
-
|
| 1685 |
-
// ์ํ ๊ฒฐ์ - ํํด๊ฐ ์ต์ฐ์
|
| 1686 |
-
if (this.isRetreating) {
|
| 1687 |
-
this.aiState = 'retreat';
|
| 1688 |
-
} else if (this.temporaryEvadeMode) {
|
| 1689 |
-
this.aiState = 'evade';
|
| 1690 |
-
} else if (distanceToPlayer <= 3000) {
|
| 1691 |
-
this.aiState = 'combat';
|
| 1692 |
-
} else {
|
| 1693 |
-
this.aiState = 'patrol';
|
| 1694 |
-
}
|
| 1695 |
-
|
| 1696 |
-
// ์ถฉ๋ ํํผ ๊ณ์ฐ (ํํด ์ค์ด ์๋ ๋๋ง)
|
| 1697 |
-
if (!this.isRetreating) {
|
| 1698 |
-
this.calculateAvoidance();
|
| 1699 |
-
this.checkCollisionPrediction(deltaTime);
|
| 1700 |
-
}
|
| 1701 |
-
|
| 1702 |
-
// AI ํ๋ ์คํ
|
| 1703 |
-
switch (this.aiState) {
|
| 1704 |
-
case 'patrol':
|
| 1705 |
-
this.executePatrol(deltaTime);
|
| 1706 |
-
break;
|
| 1707 |
-
case 'combat':
|
| 1708 |
-
this.executeCombat(playerPosition, deltaTime);
|
| 1709 |
-
break;
|
| 1710 |
-
case 'evade':
|
| 1711 |
-
this.executeEmergencyEvade(deltaTime);
|
| 1712 |
-
break;
|
| 1713 |
-
case 'retreat':
|
| 1714 |
-
this.executeRetreat(playerPosition, deltaTime);
|
| 1715 |
-
break;
|
| 1716 |
-
}
|
| 1717 |
-
|
| 1718 |
-
// ๋ฌผ๋ฆฌ ์
๋ฐ์ดํธ
|
| 1719 |
-
this.updatePhysics(deltaTime);
|
| 1720 |
-
|
| 1721 |
-
// ํํ ์
๋ฐ์ดํธ
|
| 1722 |
-
this.updateBullets(deltaTime);
|
| 1723 |
-
}
|
| 1724 |
-
|
| 1725 |
-
// Game ํด๋์ค์ checkCollisions ๋ฉ์๋๋ ์ฝ๊ฐ ์์
|
| 1726 |
-
checkCollisions() {
|
| 1727 |
-
// ํ๋ ์ด์ด ํํ vs ์ ๊ธฐ ์ถฉ๋
|
| 1728 |
-
for (let i = this.fighter.bullets.length - 1; i >= 0; i--) {
|
| 1729 |
-
const bullet = this.fighter.bullets[i];
|
| 1730 |
-
|
| 1731 |
-
for (let j = this.enemies.length - 1; j >= 0; j--) {
|
| 1732 |
-
const enemy = this.enemies[j];
|
| 1733 |
-
|
| 1734 |
-
// ์ด๋ฏธ ํ๊ดด๋ ์ ์ ๊ฑด๋๋ฐ๊ธฐ
|
| 1735 |
-
if (!enemy.mesh || !enemy.isLoaded || enemy.isDestroyed) continue;
|
| 1736 |
-
|
| 1737 |
-
const distance = bullet.position.distanceTo(enemy.position);
|
| 1738 |
-
if (distance < 90) {
|
| 1739 |
-
console.log(`Hit detected! Distance: ${distance}, Enemy health: ${enemy.health}`);
|
| 1740 |
-
|
| 1741 |
-
// ํํธ ํ์ ์ถ๊ฐ
|
| 1742 |
-
this.showHitMarker(enemy.position);
|
| 1743 |
-
// ํผ๊ฒฉ ์ดํํธ ์ถ๊ฐ
|
| 1744 |
-
this.createHitEffect(enemy.position);
|
| 1745 |
-
|
| 1746 |
-
// ํํ ์ ๊ฑฐ
|
| 1747 |
-
this.scene.remove(bullet);
|
| 1748 |
-
this.fighter.bullets.splice(i, 1);
|
| 1749 |
-
|
| 1750 |
-
// ์ ๊ธฐ์ ๋ฐ๋ฏธ์ง ์
ํ๊ธฐ
|
| 1751 |
-
const isDestroyed = enemy.takeDamage(GAME_CONSTANTS.BULLET_DAMAGE);
|
| 1752 |
-
console.log(`Enemy damaged. New health: ${enemy.health}, Destroyed: ${isDestroyed}`);
|
| 1753 |
-
|
| 1754 |
-
if (isDestroyed) {
|
| 1755 |
-
console.log('Enemy destroyed! Creating explosion effect...');
|
| 1756 |
-
|
| 1757 |
-
// ํญ๋ฐ ํจ๊ณผ ์์ฑ - ์์น๋ฅผ ๋ณต์ฌํด์ ์ ๋ฌ
|
| 1758 |
-
const explosionPosition = enemy.position.clone();
|
| 1759 |
-
console.log('Explosion position:', explosionPosition);
|
| 1760 |
-
|
| 1761 |
-
// ํญ๋ฐ ํจ๊ณผ ์ฆ์ ์์ฑ
|
| 1762 |
-
this.createExplosionEffect(explosionPosition);
|
| 1763 |
-
|
| 1764 |
-
// ์ ๊ธฐ ๋ฉ์ ์ ๊ฑฐ (destroy ํธ์ถ ์ ์ ๋ช
์์ ์ผ๋ก ์ ๊ฑฐ)
|
| 1765 |
-
if (enemy.mesh && enemy.mesh.parent) {
|
| 1766 |
-
console.log('Explicitly removing enemy mesh before destroy');
|
| 1767 |
-
enemy.mesh.parent.remove(enemy.mesh);
|
| 1768 |
-
}
|
| 1769 |
-
|
| 1770 |
-
// ์ ๊ธฐ ์ ๊ฑฐ
|
| 1771 |
-
enemy.destroy();
|
| 1772 |
-
|
| 1773 |
-
// ๋ฐฐ์ด์์ ์ ๊ฑฐ
|
| 1774 |
-
this.enemies.splice(j, 1);
|
| 1775 |
-
this.score += 100;
|
| 1776 |
-
|
| 1777 |
-
console.log(`Enemy removed. Remaining enemies: ${this.enemies.length}`);
|
| 1778 |
-
}
|
| 1779 |
-
break;
|
| 1780 |
-
}
|
| 1781 |
}
|
| 1782 |
}
|
| 1783 |
-
|
| 1784 |
-
// ์ ํํ vs ํ๋ ์ด์ด ์ถฉ๋
|
| 1785 |
-
this.enemies.forEach(enemy => {
|
| 1786 |
-
// ํ๊ดด๋ ์ ์ ์ฒ๋ฆฌํ์ง ์์
|
| 1787 |
-
if (enemy.isDestroyed) return;
|
| 1788 |
-
|
| 1789 |
-
for (let index = enemy.bullets.length - 1; index >= 0; index--) {
|
| 1790 |
-
const bullet = enemy.bullets[index];
|
| 1791 |
-
const distance = bullet.position.distanceTo(this.fighter.position);
|
| 1792 |
-
if (distance < 100) {
|
| 1793 |
-
// ํ๋ ์ด์ด ํผ๊ฒฉ ์ดํํธ
|
| 1794 |
-
this.createHitEffect(this.fighter.position);
|
| 1795 |
-
|
| 1796 |
-
// ํํ ์ ๊ฑฐ
|
| 1797 |
-
this.scene.remove(bullet);
|
| 1798 |
-
enemy.bullets.splice(index, 1);
|
| 1799 |
-
|
| 1800 |
-
if (this.fighter.takeDamage(GAME_CONSTANTS.BULLET_DAMAGE)) {
|
| 1801 |
-
// ํ๋ ์ด์ด ํ๊ดด ์ ํญ๋ฐ ํจ๊ณผ ์ถ๊ฐ
|
| 1802 |
-
this.createExplosionEffect(this.fighter.position);
|
| 1803 |
-
|
| 1804 |
-
this.endGame(false);
|
| 1805 |
-
}
|
| 1806 |
-
}
|
| 1807 |
-
}
|
| 1808 |
-
});
|
| 1809 |
}
|
| 1810 |
|
| 1811 |
// ๋ฉ์ธ ๊ฒ์ ํด๋์ค
|
|
@@ -2070,78 +1757,82 @@ class Game {
|
|
| 2070 |
}
|
| 2071 |
|
| 2072 |
setupEventListeners() {
|
| 2073 |
-
|
| 2074 |
-
|
| 2075 |
-
|
| 2076 |
-
|
| 2077 |
-
|
| 2078 |
-
|
| 2079 |
-
|
| 2080 |
-
|
| 2081 |
-
|
| 2082 |
-
|
| 2083 |
-
|
| 2084 |
-
|
| 2085 |
-
|
| 2086 |
-
|
| 2087 |
-
|
| 2088 |
-
|
| 2089 |
-
|
| 2090 |
-
|
| 2091 |
-
|
| 2092 |
-
|
| 2093 |
-
|
| 2094 |
-
|
| 2095 |
-
|
| 2096 |
-
|
| 2097 |
-
|
| 2098 |
-
|
| 2099 |
-
}
|
|
|
|
| 2100 |
|
| 2101 |
-
|
| 2102 |
-
|
| 2103 |
-
|
| 2104 |
-
|
| 2105 |
-
|
| 2106 |
-
|
| 2107 |
-
|
| 2108 |
-
|
| 2109 |
-
|
| 2110 |
-
|
| 2111 |
-
|
| 2112 |
-
|
| 2113 |
-
}
|
|
|
|
| 2114 |
|
| 2115 |
-
|
| 2116 |
-
|
| 2117 |
-
|
| 2118 |
-
|
| 2119 |
-
|
| 2120 |
-
|
| 2121 |
-
|
| 2122 |
-
|
|
|
|
| 2123 |
|
| 2124 |
-
|
| 2125 |
-
|
| 2126 |
-
|
| 2127 |
-
|
| 2128 |
-
|
| 2129 |
-
|
| 2130 |
-
|
| 2131 |
-
}
|
|
|
|
| 2132 |
|
| 2133 |
-
|
| 2134 |
-
|
| 2135 |
-
|
| 2136 |
-
|
| 2137 |
-
|
| 2138 |
|
| 2139 |
-
|
| 2140 |
-
|
| 2141 |
-
|
| 2142 |
-
|
| 2143 |
-
|
| 2144 |
-
|
| 2145 |
|
| 2146 |
startGame() {
|
| 2147 |
if (!this.isLoaded) {
|
|
@@ -2806,88 +2497,72 @@ class Game {
|
|
| 2806 |
animateImpact();
|
| 2807 |
|
| 2808 |
// ์์ ์ถฉ๋์
|
| 2809 |
-
|
| 2810 |
-
|
| 2811 |
-
|
| 2812 |
-
|
| 2813 |
-
|
| 2814 |
-
|
| 2815 |
-
|
| 2816 |
-
|
| 2817 |
-
}
|
| 2818 |
}
|
| 2819 |
-
|
| 2820 |
checkCollisions() {
|
| 2821 |
-
|
| 2822 |
-
|
| 2823 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2824 |
|
| 2825 |
-
|
| 2826 |
-
|
| 2827 |
-
|
|
|
|
|
|
|
|
|
|
| 2828 |
|
| 2829 |
-
|
| 2830 |
-
|
| 2831 |
-
|
| 2832 |
-
|
| 2833 |
-
|
| 2834 |
-
|
| 2835 |
-
|
| 2836 |
-
this.createHitEffect(enemy.position);
|
| 2837 |
-
|
| 2838 |
-
// ํํ ์ ๊ฑฐ
|
| 2839 |
-
this.scene.remove(bullet);
|
| 2840 |
-
this.fighter.bullets.splice(i, 1);
|
| 2841 |
-
|
| 2842 |
-
// ์ ๊ธฐ์ ๋ฐ๋ฏธ์ง ์
ํ๊ธฐ
|
| 2843 |
-
const isDestroyed = enemy.takeDamage(GAME_CONSTANTS.BULLET_DAMAGE);
|
| 2844 |
-
console.log(`Enemy damaged. New health: ${enemy.health}, Destroyed: ${isDestroyed}`);
|
| 2845 |
|
| 2846 |
-
|
| 2847 |
-
|
| 2848 |
-
|
| 2849 |
-
// ํญ๋ฐ ํจ๊ณผ ์์ฑ - ์์น๋ฅผ ๋ณต์ฌํด์ ์ ๋ฌ
|
| 2850 |
-
const explosionPosition = enemy.position.clone();
|
| 2851 |
-
console.log('Explosion position:', explosionPosition);
|
| 2852 |
-
|
| 2853 |
-
// ํญ๋ฐ ํจ๊ณผ ์ฆ์ ์์ฑ
|
| 2854 |
-
this.createExplosionEffect(explosionPosition);
|
| 2855 |
-
|
| 2856 |
-
// ์ ๊ธฐ ์ ๊ฑฐ
|
| 2857 |
-
enemy.destroy();
|
| 2858 |
-
this.enemies.splice(j, 1);
|
| 2859 |
-
this.score += 100;
|
| 2860 |
-
|
| 2861 |
-
console.log(`Enemy removed. Remaining enemies: ${this.enemies.length}`);
|
| 2862 |
-
}
|
| 2863 |
-
break;
|
| 2864 |
}
|
|
|
|
| 2865 |
}
|
| 2866 |
}
|
| 2867 |
-
|
| 2868 |
-
|
| 2869 |
-
|
| 2870 |
-
|
| 2871 |
-
|
| 2872 |
-
|
| 2873 |
-
|
| 2874 |
-
|
| 2875 |
-
|
| 2876 |
-
|
| 2877 |
-
|
| 2878 |
-
|
| 2879 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2880 |
|
| 2881 |
-
|
| 2882 |
-
// ํ๋ ์ด์ด ํ๊ดด ์ ํญ๋ฐ ํจ๊ณผ ์ถ๊ฐ
|
| 2883 |
-
this.createExplosionEffect(this.fighter.position);
|
| 2884 |
-
|
| 2885 |
-
this.endGame(false);
|
| 2886 |
-
}
|
| 2887 |
}
|
| 2888 |
}
|
| 2889 |
-
}
|
| 2890 |
-
}
|
|
|
|
| 2891 |
|
| 2892 |
createHitEffect(position) {
|
| 2893 |
// ํผ๊ฒฉ ํํฐํด ํจ๊ณผ ์์ฑ
|
|
@@ -2988,192 +2663,169 @@ class Game {
|
|
| 2988 |
}
|
| 2989 |
|
| 2990 |
createExplosionEffect(position) {
|
| 2991 |
-
|
| 2992 |
-
|
| 2993 |
-
|
| 2994 |
-
|
| 2995 |
-
console.error('Invalid position for explosion effect');
|
| 2996 |
-
return;
|
| 2997 |
-
}
|
| 2998 |
|
| 2999 |
-
//
|
| 3000 |
-
|
| 3001 |
-
|
| 3002 |
-
|
| 3003 |
-
|
| 3004 |
-
// ํ๋ ์ด์ด์์ ๊ฑฐ๋ฆฌ์ ๋ฐ๋ฅธ ๋ณผ๋ฅจ ์กฐ์
|
| 3005 |
-
if (this.fighter && this.fighter.position) {
|
| 3006 |
-
const distanceToPlayer = position.distanceTo(this.fighter.position);
|
| 3007 |
-
const maxAudibleDistance = 5000;
|
| 3008 |
-
if (distanceToPlayer < maxAudibleDistance) {
|
| 3009 |
-
explosionSound.volume = Math.max(0.3, 1.0 - (distanceToPlayer / maxAudibleDistance));
|
| 3010 |
-
console.log(`Explosion sound volume: ${explosionSound.volume} (distance: ${distanceToPlayer})`);
|
| 3011 |
-
}
|
| 3012 |
-
}
|
| 3013 |
-
|
| 3014 |
-
// ์ฆ์ ์ฌ์ ์๋
|
| 3015 |
-
const playPromise = explosionSound.play();
|
| 3016 |
-
if (playPromise !== undefined) {
|
| 3017 |
-
playPromise
|
| 3018 |
-
.then(() => console.log('Explosion sound played successfully'))
|
| 3019 |
-
.catch(e => console.error('Explosion sound failed:', e));
|
| 3020 |
-
}
|
| 3021 |
-
} catch (e) {
|
| 3022 |
-
console.error('Explosion sound error:', e);
|
| 3023 |
}
|
| 3024 |
-
|
| 3025 |
-
|
| 3026 |
-
|
| 3027 |
-
|
| 3028 |
-
|
| 3029 |
-
|
| 3030 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3031 |
transparent: true,
|
| 3032 |
opacity: 1.0
|
| 3033 |
});
|
| 3034 |
|
| 3035 |
-
const
|
| 3036 |
-
|
| 3037 |
-
this.scene.add(explosion);
|
| 3038 |
-
console.log('Explosion mesh added to scene');
|
| 3039 |
-
|
| 3040 |
-
// ํญ๋ฐ ํํธ๋ค
|
| 3041 |
-
const debrisCount = 30;
|
| 3042 |
-
const debris = [];
|
| 3043 |
-
|
| 3044 |
-
for (let i = 0; i < debrisCount; i++) {
|
| 3045 |
-
const debrisGeometry = new THREE.BoxGeometry(
|
| 3046 |
-
2 + Math.random() * 4,
|
| 3047 |
-
2 + Math.random() * 4,
|
| 3048 |
-
2 + Math.random() * 4
|
| 3049 |
-
);
|
| 3050 |
-
const debrisMaterial = new THREE.MeshBasicMaterial({
|
| 3051 |
-
color: Math.random() > 0.5 ? 0xff6600 : 0x333333,
|
| 3052 |
-
transparent: true,
|
| 3053 |
-
opacity: 1.0
|
| 3054 |
-
});
|
| 3055 |
-
|
| 3056 |
-
const debrisPiece = new THREE.Mesh(debrisGeometry, debrisMaterial);
|
| 3057 |
-
debrisPiece.position.copy(position);
|
| 3058 |
-
|
| 3059 |
-
// ๋๋ค ์๋์ ํ์
|
| 3060 |
-
debrisPiece.velocity = new THREE.Vector3(
|
| 3061 |
-
(Math.random() - 0.5) * 200,
|
| 3062 |
-
(Math.random() - 0.5) * 200,
|
| 3063 |
-
(Math.random() - 0.5) * 200
|
| 3064 |
-
);
|
| 3065 |
-
debrisPiece.rotationSpeed = new THREE.Vector3(
|
| 3066 |
-
Math.random() * 10,
|
| 3067 |
-
Math.random() * 10,
|
| 3068 |
-
Math.random() * 10
|
| 3069 |
-
);
|
| 3070 |
-
debrisPiece.life = 2.0;
|
| 3071 |
-
|
| 3072 |
-
this.scene.add(debrisPiece);
|
| 3073 |
-
debris.push(debrisPiece);
|
| 3074 |
-
}
|
| 3075 |
|
| 3076 |
-
//
|
| 3077 |
-
|
| 3078 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3079 |
|
| 3080 |
-
|
| 3081 |
-
|
| 3082 |
-
|
| 3083 |
-
|
| 3084 |
-
|
| 3085 |
-
|
| 3086 |
-
|
| 3087 |
-
|
| 3088 |
-
|
| 3089 |
-
|
| 3090 |
-
|
| 3091 |
-
|
| 3092 |
-
|
| 3093 |
-
|
| 3094 |
-
|
| 3095 |
-
|
| 3096 |
-
smokePuff.velocity = new THREE.Vector3(
|
| 3097 |
-
(Math.random() - 0.5) * 50,
|
| 3098 |
-
Math.random() * 50 + 20,
|
| 3099 |
-
(Math.random() - 0.5) * 50
|
| 3100 |
-
);
|
| 3101 |
-
smokePuff.life = 3.0;
|
| 3102 |
-
|
| 3103 |
-
this.scene.add(smokePuff);
|
| 3104 |
-
smoke.push(smokePuff);
|
| 3105 |
-
}
|
| 3106 |
|
| 3107 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3108 |
|
| 3109 |
-
|
| 3110 |
-
|
| 3111 |
-
|
| 3112 |
-
|
| 3113 |
-
|
| 3114 |
-
|
| 3115 |
-
|
| 3116 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3117 |
allDead = false;
|
| 3118 |
-
|
| 3119 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3120 |
}
|
| 3121 |
-
|
| 3122 |
-
|
| 3123 |
-
|
| 3124 |
-
|
| 3125 |
-
|
| 3126 |
-
|
| 3127 |
-
|
| 3128 |
-
|
| 3129 |
-
|
| 3130 |
-
|
| 3131 |
-
|
| 3132 |
-
|
| 3133 |
-
|
| 3134 |
-
|
| 3135 |
-
|
| 3136 |
-
|
| 3137 |
-
|
| 3138 |
-
|
| 3139 |
-
|
| 3140 |
-
|
| 3141 |
-
|
| 3142 |
-
|
| 3143 |
-
|
| 3144 |
-
});
|
| 3145 |
-
|
| 3146 |
-
// ์ฐ๊ธฐ ์ ๋๋ฉ์ด์
|
| 3147 |
-
smoke.forEach(puff => {
|
| 3148 |
-
if (puff.life > 0) {
|
| 3149 |
-
allDead = false;
|
| 3150 |
-
puff.life -= 0.02;
|
| 3151 |
-
|
| 3152 |
-
// ์์น ์
๋ฐ์ดํธ
|
| 3153 |
-
puff.position.add(puff.velocity.clone().multiplyScalar(0.02));
|
| 3154 |
-
|
| 3155 |
-
// ์์น ๊ฐ์
|
| 3156 |
-
puff.velocity.y *= 0.98;
|
| 3157 |
-
puff.velocity.x *= 0.98;
|
| 3158 |
-
puff.velocity.z *= 0.98;
|
| 3159 |
-
|
| 3160 |
-
// ํ์ฐ
|
| 3161 |
-
puff.scale.multiplyScalar(1.02);
|
| 3162 |
-
|
| 3163 |
-
// ํ์ด๋ ์์
|
| 3164 |
-
puff.material.opacity = (puff.life / 3) * 0.8;
|
| 3165 |
-
} else if (puff.parent) {
|
| 3166 |
-
this.scene.remove(puff);
|
| 3167 |
-
}
|
| 3168 |
-
});
|
| 3169 |
-
|
| 3170 |
-
if (!allDead) {
|
| 3171 |
-
requestAnimationFrame(animateExplosion);
|
| 3172 |
}
|
| 3173 |
-
};
|
| 3174 |
|
| 3175 |
-
|
| 3176 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3177 |
|
| 3178 |
showHitMarker(position) {
|
| 3179 |
// ํํธ ๋ง์ปค div ์์ฑ
|
|
@@ -3226,101 +2878,101 @@ class Game {
|
|
| 3226 |
}
|
| 3227 |
|
| 3228 |
animate() {
|
| 3229 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3230 |
|
| 3231 |
-
|
|
|
|
| 3232 |
|
| 3233 |
-
|
| 3234 |
-
|
| 3235 |
-
this.lastTime = currentTime;
|
| 3236 |
|
| 3237 |
-
|
| 3238 |
-
|
| 3239 |
-
|
| 3240 |
-
|
| 3241 |
-
|
| 3242 |
-
|
| 3243 |
-
|
| 3244 |
-
|
| 3245 |
-
|
| 3246 |
-
|
| 3247 |
-
|
| 3248 |
-
|
| 3249 |
-
// ๋ฌผ๋ฆฌ ์
๋ฐ์ดํธ
|
| 3250 |
-
this.fighter.updatePhysics(deltaTime);
|
| 3251 |
-
|
| 3252 |
-
// ํํ ์
๋ฐ์ดํธ
|
| 3253 |
-
this.fighter.updateBullets(this.scene, deltaTime, this);
|
| 3254 |
-
|
| 3255 |
-
// ๋ง์ฐ์ค ๋๋ฆ ์ํ์ผ ๋ ์ฐ์ ๋ฐ์ฌ
|
| 3256 |
-
if (this.fighter.isMouseDown) {
|
| 3257 |
-
const currentShootTime = Date.now();
|
| 3258 |
-
if (!this.lastShootTime || currentShootTime - this.lastShootTime >= 100) {
|
| 3259 |
-
this.fighter.shoot(this.scene);
|
| 3260 |
-
this.lastShootTime = currentShootTime;
|
| 3261 |
-
}
|
| 3262 |
-
}
|
| 3263 |
-
|
| 3264 |
-
// ์ ๊ธฐ ์
๋ฐ์ดํธ
|
| 3265 |
-
this.enemies.forEach(enemy => {
|
| 3266 |
-
enemy.nearbyEnemies = this.enemies;
|
| 3267 |
-
});
|
| 3268 |
-
|
| 3269 |
-
this.enemies.forEach(enemy => {
|
| 3270 |
-
enemy.update(this.fighter.position, deltaTime);
|
| 3271 |
-
});
|
| 3272 |
-
|
| 3273 |
-
// ์ถฉ๋ ์ฒดํฌ
|
| 3274 |
-
this.checkCollisions();
|
| 3275 |
-
|
| 3276 |
-
// ๊ฒ์ ์ข
๋ฃ ์กฐ๊ฑด ์ฒดํฌ
|
| 3277 |
-
if (this.fighter.health <= 0) {
|
| 3278 |
-
if (this.fighter.position.y <= 0) {
|
| 3279 |
-
this.endGame(false, "GROUND COLLISION");
|
| 3280 |
-
} else {
|
| 3281 |
-
this.endGame(false);
|
| 3282 |
-
}
|
| 3283 |
-
return;
|
| 3284 |
}
|
| 3285 |
-
|
| 3286 |
-
|
| 3287 |
-
|
| 3288 |
-
|
| 3289 |
-
|
| 3290 |
-
|
| 3291 |
-
|
| 3292 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3293 |
}
|
| 3294 |
-
|
| 3295 |
-
// ๊ฒ์์ด ์์๋์ง ์์์ ๋๋ ๋ฌผ๋ฆฌ๋ ์
๋ฐ์ดํธ (์นด๋ฉ๋ผ ์์ง์์ ์ํด)
|
| 3296 |
-
this.fighter.updatePhysics(deltaTime);
|
| 3297 |
-
}
|
| 3298 |
-
|
| 3299 |
-
// ๊ตฌ๋ฆ ์ ๋๋ฉ์ด์
|
| 3300 |
-
if (this.clouds) {
|
| 3301 |
-
this.clouds.forEach(cloud => {
|
| 3302 |
-
cloud.userData.time += deltaTime;
|
| 3303 |
-
cloud.position.x += cloud.userData.driftSpeed;
|
| 3304 |
-
cloud.position.y = cloud.userData.initialY +
|
| 3305 |
-
Math.sin(cloud.userData.time * cloud.userData.floatSpeed) * 20;
|
| 3306 |
-
|
| 3307 |
-
const mapLimit = GAME_CONSTANTS.MAP_SIZE / 2;
|
| 3308 |
-
if (cloud.position.x > mapLimit) cloud.position.x = -mapLimit;
|
| 3309 |
-
if (cloud.position.x < -mapLimit) cloud.position.x = mapLimit;
|
| 3310 |
-
});
|
| 3311 |
}
|
| 3312 |
|
| 3313 |
-
//
|
| 3314 |
-
|
| 3315 |
-
|
| 3316 |
-
|
| 3317 |
-
|
| 3318 |
-
|
| 3319 |
-
this.
|
| 3320 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3321 |
|
| 3322 |
-
this.
|
|
|
|
| 3323 |
}
|
|
|
|
|
|
|
|
|
|
| 3324 |
|
| 3325 |
endGame(victory = false, reason = "") {
|
| 3326 |
this.isGameOver = true;
|
|
|
|
| 792 |
}
|
| 793 |
}
|
| 794 |
|
| 795 |
+
// ์ ์ ํฌ๊ธฐ ํด๋์ค - ์์ ํ ์ฌ์ค๊ณ
|
| 796 |
class EnemyFighter {
|
| 797 |
constructor(scene, position) {
|
| 798 |
this.mesh = null;
|
|
|
|
| 838 |
this.nearbyEnemies = [];
|
| 839 |
this.avoidanceVector = new THREE.Vector3();
|
| 840 |
|
|
|
|
|
|
|
|
|
|
| 841 |
// ์ด๊ธฐ ๋ชฉํ ์ค์
|
| 842 |
this.selectNewPatrolTarget();
|
| 843 |
}
|
|
|
|
| 889 |
}
|
| 890 |
|
| 891 |
update(playerPosition, deltaTime) {
|
| 892 |
+
if (!this.mesh || !this.isLoaded) return;
|
| 893 |
|
| 894 |
// ํํผ ํ์ด๋จธ ์
๋ฐ์ดํธ
|
| 895 |
if (this.temporaryEvadeMode && this.evadeTimer > 0) {
|
|
|
|
| 905 |
if (distanceToPlayer < 100) {
|
| 906 |
this.isRetreating = true;
|
| 907 |
this.aiState = 'retreat';
|
| 908 |
+
// ํํด ์์ ์ ๋ก๊ทธ
|
| 909 |
console.log(`Enemy retreating! Distance: ${distanceToPlayer.toFixed(1)}m`);
|
| 910 |
}
|
| 911 |
|
|
|
|
| 918 |
// ์ํ ๊ฒฐ์ - ํํด๊ฐ ์ต์ฐ์
|
| 919 |
if (this.isRetreating) {
|
| 920 |
this.aiState = 'retreat';
|
| 921 |
+
// ํํด ์ค์๋ ๋ค๋ฅธ ์ํ๋ก ๋ณ๊ฒฝ ๋ถ๊ฐ
|
| 922 |
} else if (this.temporaryEvadeMode) {
|
| 923 |
this.aiState = 'evade';
|
| 924 |
} else if (distanceToPlayer <= 3000) {
|
|
|
|
| 956 |
this.updateBullets(deltaTime);
|
| 957 |
}
|
| 958 |
|
| 959 |
+
// ์๋ก์ด ๋ฉ์๋: ํํด ์คํ
|
| 960 |
executeRetreat(playerPosition, deltaTime) {
|
| 961 |
// ํ๋ ์ด์ด๋ก๋ถํฐ ๋ฉ์ด์ง๋ ๋ฐฉํฅ ๊ณ์ฐ
|
| 962 |
const retreatDirection = this.position.clone().sub(playerPosition).normalize();
|
|
|
|
| 1294 |
}
|
| 1295 |
|
| 1296 |
updatePhysics(deltaTime) {
|
| 1297 |
+
if (!this.mesh) return;
|
| 1298 |
|
| 1299 |
// ์๋ ๋ฒกํฐ ๊ณ์ฐ (ํญ์ ์ ์ง)
|
| 1300 |
const forward = new THREE.Vector3(0, 0, 1);
|
|
|
|
| 1482 |
|
| 1483 |
takeDamage(damage) {
|
| 1484 |
this.health -= damage;
|
| 1485 |
+
return this.health <= 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1486 |
}
|
| 1487 |
|
| 1488 |
destroy() {
|
| 1489 |
+
if (this.mesh) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1490 |
this.scene.remove(this.mesh);
|
| 1491 |
+
this.bullets.forEach(bullet => this.scene.remove(bullet));
|
| 1492 |
+
this.bullets = [];
|
| 1493 |
+
this.isLoaded = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1494 |
}
|
| 1495 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1496 |
}
|
| 1497 |
|
| 1498 |
// ๋ฉ์ธ ๊ฒ์ ํด๋์ค
|
|
|
|
| 1757 |
}
|
| 1758 |
|
| 1759 |
setupEventListeners() {
|
| 1760 |
+
document.addEventListener('keydown', (event) => {
|
| 1761 |
+
if (this.isGameOver) return;
|
| 1762 |
+
|
| 1763 |
+
// gameStarted ๋์ this.isStarted ์ฌ์ฉ
|
| 1764 |
+
if (!this.isStarted) return;
|
| 1765 |
+
|
| 1766 |
+
switch(event.code) {
|
| 1767 |
+
case 'KeyW':
|
| 1768 |
+
this.keys.w = true;
|
| 1769 |
+
console.log('W key pressed - Accelerating');
|
| 1770 |
+
break;
|
| 1771 |
+
case 'KeyA':
|
| 1772 |
+
this.keys.a = true;
|
| 1773 |
+
console.log('A key pressed - Turning left');
|
| 1774 |
+
break;
|
| 1775 |
+
case 'KeyS':
|
| 1776 |
+
this.keys.s = true;
|
| 1777 |
+
console.log('S key pressed - Decelerating');
|
| 1778 |
+
break;
|
| 1779 |
+
case 'KeyD':
|
| 1780 |
+
this.keys.d = true;
|
| 1781 |
+
console.log('D key pressed - Turning right');
|
| 1782 |
+
break;
|
| 1783 |
+
case 'KeyF':
|
| 1784 |
+
this.keys.f = true;
|
| 1785 |
+
break;
|
| 1786 |
+
}
|
| 1787 |
+
});
|
| 1788 |
|
| 1789 |
+
document.addEventListener('keyup', (event) => {
|
| 1790 |
+
if (this.isGameOver) return;
|
| 1791 |
+
|
| 1792 |
+
// gameStarted ๋์ this.isStarted ์ฌ์ฉ
|
| 1793 |
+
if (!this.isStarted) return;
|
| 1794 |
+
|
| 1795 |
+
switch(event.code) {
|
| 1796 |
+
case 'KeyW': this.keys.w = false; break;
|
| 1797 |
+
case 'KeyA': this.keys.a = false; break;
|
| 1798 |
+
case 'KeyS': this.keys.s = false; break;
|
| 1799 |
+
case 'KeyD': this.keys.d = false; break;
|
| 1800 |
+
case 'KeyF': this.keys.f = false; break;
|
| 1801 |
+
}
|
| 1802 |
+
});
|
| 1803 |
|
| 1804 |
+
document.addEventListener('mousemove', (event) => {
|
| 1805 |
+
// ์ฌ๊ธฐ๋ gameStarted๋ฅผ this.isStarted๋ก ๋ณ๊ฒฝ
|
| 1806 |
+
if (!document.pointerLockElement || this.isGameOver || !this.isStarted) return;
|
| 1807 |
+
|
| 1808 |
+
const deltaX = event.movementX || 0;
|
| 1809 |
+
const deltaY = event.movementY || 0;
|
| 1810 |
+
|
| 1811 |
+
this.fighter.updateMouseInput(deltaX, deltaY);
|
| 1812 |
+
});
|
| 1813 |
|
| 1814 |
+
document.addEventListener('mousedown', (event) => {
|
| 1815 |
+
// ์ฌ๊ธฐ๋ gameStarted๋ฅผ this.isStarted๋ก ๋ณ๊ฒฝ
|
| 1816 |
+
if (!document.pointerLockElement || this.isGameOver || !this.isStarted) return;
|
| 1817 |
+
|
| 1818 |
+
if (event.button === 0) {
|
| 1819 |
+
this.fighter.isMouseDown = true;
|
| 1820 |
+
this.lastShootTime = 0;
|
| 1821 |
+
}
|
| 1822 |
+
});
|
| 1823 |
|
| 1824 |
+
document.addEventListener('mouseup', (event) => {
|
| 1825 |
+
if (event.button === 0) {
|
| 1826 |
+
this.fighter.isMouseDown = false;
|
| 1827 |
+
}
|
| 1828 |
+
});
|
| 1829 |
|
| 1830 |
+
window.addEventListener('resize', () => {
|
| 1831 |
+
this.camera.aspect = window.innerWidth / window.innerHeight;
|
| 1832 |
+
this.camera.updateProjectionMatrix();
|
| 1833 |
+
this.renderer.setSize(window.innerWidth, window.innerHeight);
|
| 1834 |
+
});
|
| 1835 |
+
}
|
| 1836 |
|
| 1837 |
startGame() {
|
| 1838 |
if (!this.isLoaded) {
|
|
|
|
| 2497 |
animateImpact();
|
| 2498 |
|
| 2499 |
// ์์ ์ถฉ๋์
|
| 2500 |
+
try {
|
| 2501 |
+
const impactSound = new Audio('sounds/hit.ogg');
|
| 2502 |
+
impactSound.volume = 0.2;
|
| 2503 |
+
impactSound.play().catch(e => {
|
| 2504 |
+
console.log('Impact sound not found or failed to play');
|
| 2505 |
+
});
|
| 2506 |
+
} catch (e) {
|
| 2507 |
+
console.log('Impact sound error:', e);
|
|
|
|
| 2508 |
}
|
| 2509 |
+
}
|
| 2510 |
checkCollisions() {
|
| 2511 |
+
// ํ๋ ์ด์ด ํํ vs ์ ๊ธฐ ์ถฉ๋
|
| 2512 |
+
for (let i = this.fighter.bullets.length - 1; i >= 0; i--) {
|
| 2513 |
+
const bullet = this.fighter.bullets[i];
|
| 2514 |
+
|
| 2515 |
+
for (let j = this.enemies.length - 1; j >= 0; j--) {
|
| 2516 |
+
const enemy = this.enemies[j];
|
| 2517 |
+
if (!enemy.mesh || !enemy.isLoaded) continue;
|
| 2518 |
|
| 2519 |
+
const distance = bullet.position.distanceTo(enemy.position);
|
| 2520 |
+
if (distance < 90) {
|
| 2521 |
+
// ํํธ ํ์ ์ถ๊ฐ
|
| 2522 |
+
this.showHitMarker(enemy.position);
|
| 2523 |
+
// ํผ๊ฒฉ ์ดํํธ ์ถ๊ฐ
|
| 2524 |
+
this.createHitEffect(enemy.position);
|
| 2525 |
|
| 2526 |
+
// ํํ ์ ๊ฑฐ๋ ์ดํํธ ์์ฑ ํ์
|
| 2527 |
+
this.scene.remove(bullet);
|
| 2528 |
+
this.fighter.bullets.splice(i, 1);
|
| 2529 |
+
|
| 2530 |
+
if (enemy.takeDamage(GAME_CONSTANTS.BULLET_DAMAGE)) {
|
| 2531 |
+
// ์ ๊ธฐ ํ๊ดด ์ ํญ๋ฐ ํจ๊ณผ ์ถ๊ฐ - ์์น ํ์ธ
|
| 2532 |
+
this.createExplosionEffect(enemy.position);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2533 |
|
| 2534 |
+
enemy.destroy();
|
| 2535 |
+
this.enemies.splice(j, 1);
|
| 2536 |
+
this.score += 100;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2537 |
}
|
| 2538 |
+
break;
|
| 2539 |
}
|
| 2540 |
}
|
| 2541 |
+
}
|
| 2542 |
+
|
| 2543 |
+
// ์ ํํ vs ํ๋ ์ด์ด ์ถฉ๋
|
| 2544 |
+
this.enemies.forEach(enemy => {
|
| 2545 |
+
for (let index = enemy.bullets.length - 1; index >= 0; index--) {
|
| 2546 |
+
const bullet = enemy.bullets[index];
|
| 2547 |
+
const distance = bullet.position.distanceTo(this.fighter.position);
|
| 2548 |
+
if (distance < 100) {
|
| 2549 |
+
// ํ๋ ์ด์ด ํผ๊ฒฉ ์ดํํธ
|
| 2550 |
+
this.createHitEffect(this.fighter.position);
|
| 2551 |
+
|
| 2552 |
+
// ํํ ์ ๊ฑฐ
|
| 2553 |
+
this.scene.remove(bullet);
|
| 2554 |
+
enemy.bullets.splice(index, 1);
|
| 2555 |
+
|
| 2556 |
+
if (this.fighter.takeDamage(GAME_CONSTANTS.BULLET_DAMAGE)) {
|
| 2557 |
+
// ํ๋ ์ด์ด ํ๊ดด ์ ํญ๋ฐ ํจ๊ณผ ์ถ๊ฐ
|
| 2558 |
+
this.createExplosionEffect(this.fighter.position);
|
| 2559 |
|
| 2560 |
+
this.endGame(false);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2561 |
}
|
| 2562 |
}
|
| 2563 |
+
}
|
| 2564 |
+
});
|
| 2565 |
+
}
|
| 2566 |
|
| 2567 |
createHitEffect(position) {
|
| 2568 |
// ํผ๊ฒฉ ํํฐํด ํจ๊ณผ ์์ฑ
|
|
|
|
| 2663 |
}
|
| 2664 |
|
| 2665 |
createExplosionEffect(position) {
|
| 2666 |
+
// ํญ๋ฐ์์ ๊ฐ์ฅ ๋จผ์ ์ฌ์ (์๊ฐํจ๊ณผ๋ณด๋ค ์ฐ์ )
|
| 2667 |
+
try {
|
| 2668 |
+
const explosionSound = new Audio('sounds/bang.ogg');
|
| 2669 |
+
explosionSound.volume = 1.0; // ์ต๋ ์๋
|
|
|
|
|
|
|
|
|
|
| 2670 |
|
| 2671 |
+
// ์ฆ์ ์ฌ์ ์๋
|
| 2672 |
+
const playPromise = explosionSound.play();
|
| 2673 |
+
if (playPromise !== undefined) {
|
| 2674 |
+
playPromise.catch(e => console.log('Explosion sound failed:', e));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2675 |
}
|
| 2676 |
+
} catch (e) {
|
| 2677 |
+
console.log('Explosion sound error:', e);
|
| 2678 |
+
}
|
| 2679 |
+
|
| 2680 |
+
// ๋ฉ์ธ ํญ๋ฐ ํ๋์
|
| 2681 |
+
const explosionGeometry = new THREE.SphereGeometry(50, 16, 16);
|
| 2682 |
+
const explosionMaterial = new THREE.MeshBasicMaterial({
|
| 2683 |
+
color: 0xffaa00,
|
| 2684 |
+
emissive: 0xffaa00,
|
| 2685 |
+
emissiveIntensity: 3.0,
|
| 2686 |
+
transparent: true,
|
| 2687 |
+
opacity: 1.0
|
| 2688 |
+
});
|
| 2689 |
+
|
| 2690 |
+
const explosion = new THREE.Mesh(explosionGeometry, explosionMaterial);
|
| 2691 |
+
explosion.position.copy(position);
|
| 2692 |
+
this.scene.add(explosion);
|
| 2693 |
+
|
| 2694 |
+
// ํญ๋ฐ ํํธ๋ค
|
| 2695 |
+
const debrisCount = 30;
|
| 2696 |
+
const debris = [];
|
| 2697 |
+
|
| 2698 |
+
for (let i = 0; i < debrisCount; i++) {
|
| 2699 |
+
const debrisGeometry = new THREE.BoxGeometry(
|
| 2700 |
+
2 + Math.random() * 4,
|
| 2701 |
+
2 + Math.random() * 4,
|
| 2702 |
+
2 + Math.random() * 4
|
| 2703 |
+
);
|
| 2704 |
+
const debrisMaterial = new THREE.MeshBasicMaterial({
|
| 2705 |
+
color: Math.random() > 0.5 ? 0xff6600 : 0x333333,
|
| 2706 |
transparent: true,
|
| 2707 |
opacity: 1.0
|
| 2708 |
});
|
| 2709 |
|
| 2710 |
+
const debrisPiece = new THREE.Mesh(debrisGeometry, debrisMaterial);
|
| 2711 |
+
debrisPiece.position.copy(position);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2712 |
|
| 2713 |
+
// ๋๋ค ์๋์ ํ์
|
| 2714 |
+
debrisPiece.velocity = new THREE.Vector3(
|
| 2715 |
+
(Math.random() - 0.5) * 200,
|
| 2716 |
+
(Math.random() - 0.5) * 200,
|
| 2717 |
+
(Math.random() - 0.5) * 200
|
| 2718 |
+
);
|
| 2719 |
+
debrisPiece.rotationSpeed = new THREE.Vector3(
|
| 2720 |
+
Math.random() * 10,
|
| 2721 |
+
Math.random() * 10,
|
| 2722 |
+
Math.random() * 10
|
| 2723 |
+
);
|
| 2724 |
+
debrisPiece.life = 2.0;
|
| 2725 |
|
| 2726 |
+
this.scene.add(debrisPiece);
|
| 2727 |
+
debris.push(debrisPiece);
|
| 2728 |
+
}
|
| 2729 |
+
|
| 2730 |
+
// ์ฐ๊ธฐ ํจ๊ณผ
|
| 2731 |
+
const smokeCount = 10;
|
| 2732 |
+
const smoke = [];
|
| 2733 |
+
|
| 2734 |
+
for (let i = 0; i < smokeCount; i++) {
|
| 2735 |
+
const smokeGeometry = new THREE.SphereGeometry(10 + Math.random() * 20, 8, 8);
|
| 2736 |
+
const smokeMaterial = new THREE.MeshBasicMaterial({
|
| 2737 |
+
color: 0x222222,
|
| 2738 |
+
transparent: true,
|
| 2739 |
+
opacity: 0.8
|
| 2740 |
+
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2741 |
|
| 2742 |
+
const smokePuff = new THREE.Mesh(smokeGeometry, smokeMaterial);
|
| 2743 |
+
smokePuff.position.copy(position);
|
| 2744 |
+
smokePuff.position.add(new THREE.Vector3(
|
| 2745 |
+
(Math.random() - 0.5) * 20,
|
| 2746 |
+
(Math.random() - 0.5) * 20,
|
| 2747 |
+
(Math.random() - 0.5) * 20
|
| 2748 |
+
));
|
| 2749 |
+
|
| 2750 |
+
smokePuff.velocity = new THREE.Vector3(
|
| 2751 |
+
(Math.random() - 0.5) * 50,
|
| 2752 |
+
Math.random() * 50 + 20,
|
| 2753 |
+
(Math.random() - 0.5) * 50
|
| 2754 |
+
);
|
| 2755 |
+
smokePuff.life = 3.0;
|
| 2756 |
|
| 2757 |
+
this.scene.add(smokePuff);
|
| 2758 |
+
smoke.push(smokePuff);
|
| 2759 |
+
}
|
| 2760 |
+
|
| 2761 |
+
// ์ ๋๋ฉ์ด์
|
| 2762 |
+
const animateExplosion = () => {
|
| 2763 |
+
let allDead = true;
|
| 2764 |
+
|
| 2765 |
+
// ๋ฉ์ธ ํญ๋ฐ ์ ๋๋ฉ์ด์
|
| 2766 |
+
if (explosion.material.opacity > 0) {
|
| 2767 |
+
explosion.material.opacity -= 0.02;
|
| 2768 |
+
explosion.scale.multiplyScalar(1.08);
|
| 2769 |
+
allDead = false;
|
| 2770 |
+
} else if (explosion.parent) {
|
| 2771 |
+
this.scene.remove(explosion);
|
| 2772 |
+
}
|
| 2773 |
+
|
| 2774 |
+
// ํํธ ์ ๋๋ฉ์ด์
|
| 2775 |
+
debris.forEach(piece => {
|
| 2776 |
+
if (piece.life > 0) {
|
| 2777 |
allDead = false;
|
| 2778 |
+
piece.life -= 0.02;
|
| 2779 |
+
|
| 2780 |
+
// ์์น ์
๋ฐ์ดํธ
|
| 2781 |
+
piece.position.add(piece.velocity.clone().multiplyScalar(0.02));
|
| 2782 |
+
|
| 2783 |
+
// ์ค๋ ฅ
|
| 2784 |
+
piece.velocity.y -= 3;
|
| 2785 |
+
|
| 2786 |
+
// ํ์
|
| 2787 |
+
piece.rotation.x += piece.rotationSpeed.x * 0.02;
|
| 2788 |
+
piece.rotation.y += piece.rotationSpeed.y * 0.02;
|
| 2789 |
+
piece.rotation.z += piece.rotationSpeed.z * 0.02;
|
| 2790 |
+
|
| 2791 |
+
// ํ์ด๋ ์์
|
| 2792 |
+
piece.material.opacity = piece.life / 2;
|
| 2793 |
+
} else if (piece.parent) {
|
| 2794 |
+
this.scene.remove(piece);
|
| 2795 |
}
|
| 2796 |
+
});
|
| 2797 |
+
|
| 2798 |
+
// ์ฐ๊ธฐ ์ ๋๋ฉ์ด์
|
| 2799 |
+
smoke.forEach(puff => {
|
| 2800 |
+
if (puff.life > 0) {
|
| 2801 |
+
allDead = false;
|
| 2802 |
+
puff.life -= 0.02;
|
| 2803 |
+
|
| 2804 |
+
// ์์น ์
๋ฐ์ดํธ
|
| 2805 |
+
puff.position.add(puff.velocity.clone().multiplyScalar(0.02));
|
| 2806 |
+
|
| 2807 |
+
// ์์น ๊ฐ์
|
| 2808 |
+
puff.velocity.y *= 0.98;
|
| 2809 |
+
puff.velocity.x *= 0.98;
|
| 2810 |
+
puff.velocity.z *= 0.98;
|
| 2811 |
+
|
| 2812 |
+
// ํ์ฐ
|
| 2813 |
+
puff.scale.multiplyScalar(1.02);
|
| 2814 |
+
|
| 2815 |
+
// ํ์ด๋ ์์
|
| 2816 |
+
puff.material.opacity = (puff.life / 3) * 0.8;
|
| 2817 |
+
} else if (puff.parent) {
|
| 2818 |
+
this.scene.remove(puff);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2819 |
}
|
| 2820 |
+
});
|
| 2821 |
|
| 2822 |
+
if (!allDead) {
|
| 2823 |
+
requestAnimationFrame(animateExplosion);
|
| 2824 |
+
}
|
| 2825 |
+
};
|
| 2826 |
+
|
| 2827 |
+
animateExplosion();
|
| 2828 |
+
}
|
| 2829 |
|
| 2830 |
showHitMarker(position) {
|
| 2831 |
// ํํธ ๋ง์ปค div ์์ฑ
|
|
|
|
| 2878 |
}
|
| 2879 |
|
| 2880 |
animate() {
|
| 2881 |
+
if (this.isGameOver) return;
|
| 2882 |
+
|
| 2883 |
+
this.animationFrameId = requestAnimationFrame(() => this.animate());
|
| 2884 |
+
|
| 2885 |
+
const currentTime = performance.now();
|
| 2886 |
+
const deltaTime = Math.min((currentTime - this.lastTime) / 1000, 0.1);
|
| 2887 |
+
this.lastTime = currentTime;
|
| 2888 |
+
|
| 2889 |
+
if (this.isLoaded && this.fighter.isLoaded && this.isStarted) {
|
| 2890 |
+
// ํค ์ํ ๋๋ฒ๊น
|
| 2891 |
+
if (this.keys.w || this.keys.s || this.keys.a || this.keys.d) {
|
| 2892 |
+
console.log('animate() - Keys state:', this.keys);
|
| 2893 |
+
}
|
| 2894 |
|
| 2895 |
+
// Fํค ์ํ๋ฅผ Fighter์ ์ ๋ฌ
|
| 2896 |
+
this.fighter.escapeKeyPressed = this.keys.f;
|
| 2897 |
|
| 2898 |
+
// ์ปจํธ๋กค ์
๋ฐ์ดํธ - ๋ฐ๋์ ๋ฌผ๋ฆฌ ์
๋ฐ์ดํธ ์ ์ ํธ์ถ
|
| 2899 |
+
this.fighter.updateControls(this.keys, deltaTime);
|
|
|
|
| 2900 |
|
| 2901 |
+
// ๋ฌผ๋ฆฌ ์
๋ฐ์ดํธ
|
| 2902 |
+
this.fighter.updatePhysics(deltaTime);
|
| 2903 |
+
|
| 2904 |
+
// ํํ ์
๋ฐ์ดํธ
|
| 2905 |
+
this.fighter.updateBullets(this.scene, deltaTime, this);
|
| 2906 |
+
|
| 2907 |
+
// ๋ง์ฐ์ค ๋๋ฆ ์ํ์ผ ๋ ์ฐ์ ๋ฐ์ฌ
|
| 2908 |
+
if (this.fighter.isMouseDown) {
|
| 2909 |
+
const currentShootTime = Date.now();
|
| 2910 |
+
if (!this.lastShootTime || currentShootTime - this.lastShootTime >= 100) {
|
| 2911 |
+
this.fighter.shoot(this.scene);
|
| 2912 |
+
this.lastShootTime = currentShootTime;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2913 |
}
|
| 2914 |
+
}
|
| 2915 |
+
|
| 2916 |
+
// ์ ๊ธฐ ์
๋ฐ์ดํธ
|
| 2917 |
+
this.enemies.forEach(enemy => {
|
| 2918 |
+
enemy.nearbyEnemies = this.enemies;
|
| 2919 |
+
});
|
| 2920 |
+
|
| 2921 |
+
this.enemies.forEach(enemy => {
|
| 2922 |
+
enemy.update(this.fighter.position, deltaTime);
|
| 2923 |
+
});
|
| 2924 |
+
|
| 2925 |
+
// ์ถฉ๋ ์ฒดํฌ
|
| 2926 |
+
this.checkCollisions();
|
| 2927 |
+
|
| 2928 |
+
// ๊ฒ์ ์ข
๋ฃ ์กฐ๊ฑด ์ฒดํฌ
|
| 2929 |
+
if (this.fighter.health <= 0) {
|
| 2930 |
+
if (this.fighter.position.y <= 0) {
|
| 2931 |
+
this.endGame(false, "GROUND COLLISION");
|
| 2932 |
+
} else {
|
| 2933 |
+
this.endGame(false);
|
| 2934 |
}
|
| 2935 |
+
return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2936 |
}
|
| 2937 |
|
| 2938 |
+
// UI ์
๋ฐ์ดํธ
|
| 2939 |
+
this.updateUI();
|
| 2940 |
+
this.updateRadar();
|
| 2941 |
+
|
| 2942 |
+
// ์ ์ด ๋ชจ๋ ์ ๊ฑฐ๋์๋์ง ์ฒดํฌ
|
| 2943 |
+
if (this.enemies.length === 0) {
|
| 2944 |
+
this.endGame(true);
|
| 2945 |
}
|
| 2946 |
+
} else if (this.isLoaded && this.fighter.isLoaded) {
|
| 2947 |
+
// ๊ฒ์์ด ์์๋์ง ์์์ ๋๋ ๋ฌผ๋ฆฌ๋ ์
๋ฐ์ดํธ (์นด๋ฉ๋ผ ์์ง์์ ์ํด)
|
| 2948 |
+
this.fighter.updatePhysics(deltaTime);
|
| 2949 |
+
}
|
| 2950 |
+
|
| 2951 |
+
// ๊ตฌ๋ฆ ์ ๋๋ฉ์ด์
|
| 2952 |
+
if (this.clouds) {
|
| 2953 |
+
this.clouds.forEach(cloud => {
|
| 2954 |
+
cloud.userData.time += deltaTime;
|
| 2955 |
+
cloud.position.x += cloud.userData.driftSpeed;
|
| 2956 |
+
cloud.position.y = cloud.userData.initialY +
|
| 2957 |
+
Math.sin(cloud.userData.time * cloud.userData.floatSpeed) * 20;
|
| 2958 |
+
|
| 2959 |
+
const mapLimit = GAME_CONSTANTS.MAP_SIZE / 2;
|
| 2960 |
+
if (cloud.position.x > mapLimit) cloud.position.x = -mapLimit;
|
| 2961 |
+
if (cloud.position.x < -mapLimit) cloud.position.x = mapLimit;
|
| 2962 |
+
});
|
| 2963 |
+
}
|
| 2964 |
+
|
| 2965 |
+
// ์นด๋ฉ๋ผ ์
๋ฐ์ดํธ
|
| 2966 |
+
if (this.fighter.isLoaded) {
|
| 2967 |
+
const targetCameraPos = this.fighter.getCameraPosition();
|
| 2968 |
+
const targetCameraTarget = this.fighter.getCameraTarget();
|
| 2969 |
|
| 2970 |
+
this.camera.position.lerp(targetCameraPos, this.fighter.cameraLag);
|
| 2971 |
+
this.camera.lookAt(targetCameraTarget);
|
| 2972 |
}
|
| 2973 |
+
|
| 2974 |
+
this.renderer.render(this.scene, this.camera);
|
| 2975 |
+
}
|
| 2976 |
|
| 2977 |
endGame(victory = false, reason = "") {
|
| 2978 |
this.isGameOver = true;
|