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;
|