Spaces:
Running
Running
cutechicken
commited on
Commit
โข
b3db824
1
Parent(s):
d71ef0b
Update game.js
Browse files
game.js
CHANGED
@@ -43,21 +43,6 @@ class TankPlayer {
|
|
43 |
this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
|
44 |
this.renderer = new THREE.WebGLRenderer({ antialias: true });
|
45 |
}
|
46 |
-
createCollisionBox() {
|
47 |
-
// ํฑํฌ ๋ชธ์ฒด์ ๋ง๋ ์ถฉ๋ ๋ฐ์ค ์์ฑ
|
48 |
-
const dimensions = new THREE.Vector3(4, 2, 7); // ํฑํฌ์ ๋๋ต์ ์ธ ํฌ๊ธฐ
|
49 |
-
const geometry = new THREE.BoxGeometry(dimensions.x, dimensions.y, dimensions.z);
|
50 |
-
const material = new THREE.MeshBasicMaterial({
|
51 |
-
color: 0xff0000,
|
52 |
-
wireframe: true,
|
53 |
-
visible: false // ๋๋ฒ๊น
์ true๋ก ๋ณ๊ฒฝ
|
54 |
-
});
|
55 |
-
|
56 |
-
this.collisionBox = new THREE.Mesh(geometry, material);
|
57 |
-
this.body.add(this.collisionBox);
|
58 |
-
this.collisionBox.position.set(0, dimensions.y / 2, 0);
|
59 |
-
}
|
60 |
-
|
61 |
// ๋ณ๋์ ๋ฉ์๋๋ก ๋ถ๋ฆฌ
|
62 |
createExplosionEffect(scene, position) {
|
63 |
// ํญ๋ฐ ์ค์ฌ ํ๋์
|
@@ -185,7 +170,7 @@ class TankPlayer {
|
|
185 |
this.turretGroup.add(this.turret);
|
186 |
this.body.add(this.turretGroup);
|
187 |
|
188 |
-
// ๊ทธ๋ฆผ์
|
189 |
this.body.traverse((child) => {
|
190 |
if (child.isMesh) {
|
191 |
child.castShadow = true;
|
@@ -215,15 +200,57 @@ class TankPlayer {
|
|
215 |
this.shadowPlane.position.y = 0.1;
|
216 |
this.body.add(this.shadowPlane);
|
217 |
|
218 |
-
// ์คํฐ ์์น ์ค์
|
219 |
const spawnPosition = new THREE.Vector3(
|
220 |
-
(Math.random() - 0.5) * (MAP_SIZE * 0.8),
|
221 |
-
TANK_HEIGHT,
|
222 |
(Math.random() - 0.5) * (MAP_SIZE * 0.8)
|
223 |
);
|
224 |
|
225 |
this.body.position.copy(spawnPosition);
|
226 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
227 |
scene.add(this.body);
|
228 |
this.isLoaded = true;
|
229 |
this.updateAmmoDisplay();
|
@@ -553,14 +580,80 @@ class Enemy {
|
|
553 |
this.isLoaded = false;
|
554 |
this.moveSpeed = type === 'tank' ? ENEMY_MOVE_SPEED : ENEMY_MOVE_SPEED * 0.7;
|
555 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
556 |
|
557 |
async initialize(loader) {
|
558 |
try {
|
559 |
-
const
|
|
|
560 |
this.mesh = result.scene;
|
561 |
this.mesh.position.copy(this.position);
|
562 |
this.mesh.scale.set(ENEMY_SCALE, ENEMY_SCALE, ENEMY_SCALE);
|
563 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
564 |
this.isLoaded = true;
|
565 |
} catch (error) {
|
566 |
console.error('Error loading enemy model:', error);
|
@@ -568,82 +661,230 @@ class Enemy {
|
|
568 |
}
|
569 |
}
|
570 |
|
571 |
-
setupMesh() {
|
572 |
-
this.mesh.traverse(child => {
|
573 |
-
if (child.isMesh) {
|
574 |
-
child.castShadow = true;
|
575 |
-
child.receiveShadow = true;
|
576 |
-
}
|
577 |
-
});
|
578 |
-
this.createCollisionBox();
|
579 |
-
this.scene.add(this.mesh);
|
580 |
-
}
|
581 |
-
|
582 |
-
createCollisionBox() {
|
583 |
-
const geometry = new THREE.BoxGeometry(4, 2, 7);
|
584 |
-
const material = new THREE.MeshBasicMaterial({
|
585 |
-
color: 0xff0000,
|
586 |
-
wireframe: true,
|
587 |
-
visible: false
|
588 |
-
});
|
589 |
-
this.collisionBox = new THREE.Mesh(geometry, material);
|
590 |
-
this.mesh.add(this.collisionBox);
|
591 |
-
this.collisionBox.position.set(0, 1, 0);
|
592 |
-
}
|
593 |
-
|
594 |
update(playerPosition) {
|
595 |
-
|
596 |
-
this.moveTowardsPlayer(playerPosition);
|
597 |
-
this.updateBullets();
|
598 |
-
}
|
599 |
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
604 |
|
605 |
-
const
|
606 |
-
|
607 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
608 |
}
|
609 |
-
this.mesh.lookAt(playerPosition);
|
610 |
}
|
|
|
|
|
611 |
|
612 |
shoot(playerPosition) {
|
613 |
-
|
|
|
|
|
|
|
614 |
|
615 |
-
|
616 |
-
this.scene.add(bullet);
|
617 |
-
this.bullets.push(bullet);
|
618 |
-
this.lastAttackTime = Date.now();
|
619 |
|
620 |
-
|
621 |
-
|
622 |
|
623 |
-
|
624 |
-
|
625 |
-
|
626 |
-
|
627 |
-
);
|
628 |
|
629 |
-
|
630 |
-
|
631 |
-
|
632 |
-
|
633 |
-
|
634 |
-
|
635 |
-
|
636 |
-
|
637 |
-
return bullet;
|
638 |
-
}
|
639 |
|
640 |
-
|
641 |
-
|
642 |
-
|
643 |
-
|
644 |
-
|
645 |
-
|
646 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
647 |
|
648 |
takeDamage(damage) {
|
649 |
this.health -= damage;
|
@@ -1477,71 +1718,37 @@ class Game {
|
|
1477 |
checkCollisions() {
|
1478 |
if (this.isLoading || !this.tank.isLoaded) return;
|
1479 |
|
|
|
1480 |
const hitSounds = [
|
1481 |
'sounds/hit1.ogg', 'sounds/hit2.ogg', 'sounds/hit3.ogg',
|
1482 |
'sounds/hit4.ogg', 'sounds/hit5.ogg', 'sounds/hit6.ogg', 'sounds/hit7.ogg'
|
1483 |
];
|
1484 |
|
|
|
1485 |
const beatSounds = ['sounds/beat1.ogg', 'sounds/beat2.ogg', 'sounds/beat3.ogg'];
|
1486 |
|
1487 |
const tankPosition = this.tank.getPosition();
|
1488 |
-
|
1489 |
-
|
1490 |
-
|
1491 |
-
const tankSize = new THREE.Vector3();
|
1492 |
-
tankBox.getSize(tankSize);
|
1493 |
-
|
1494 |
-
// ์ ์ฐจ ์ถฉ๋ ๋ฐ์ค ํฌ๊ธฐ ์๋ ์กฐ์
|
1495 |
-
const customTankBox = new THREE.Box3(
|
1496 |
-
new THREE.Vector3(
|
1497 |
-
tankPosition.x - tankSize.x * 0.4, // ๋๋น 20% ๊ฐ์
|
1498 |
-
tankPosition.y - tankSize.y * 0.3, // ๋์ด 40% ๊ฐ์
|
1499 |
-
tankPosition.z - tankSize.z * 0.4 // ๊ธธ์ด 20% ๊ฐ์
|
1500 |
-
),
|
1501 |
-
new THREE.Vector3(
|
1502 |
-
tankPosition.x + tankSize.x * 0.4,
|
1503 |
-
tankPosition.y + tankSize.y * 0.3,
|
1504 |
-
tankPosition.z + tankSize.z * 0.4
|
1505 |
-
)
|
1506 |
-
);
|
1507 |
-
|
1508 |
-
// ํฑํฌ์ ์ฅ์ ๋ฌผ ์ถฉ๋ ์ฒดํฌ
|
1509 |
this.obstacles.forEach(obstacle => {
|
1510 |
-
|
1511 |
-
|
1512 |
-
|
1513 |
-
|
1514 |
-
}
|
1515 |
}
|
1516 |
-
}
|
1517 |
-
|
1518 |
-
|
1519 |
this.enemies.forEach(enemy => {
|
1520 |
if (!enemy.mesh || !enemy.isLoaded) return;
|
1521 |
|
1522 |
-
const
|
1523 |
-
const enemySize = new THREE.Vector3();
|
1524 |
-
enemyBox.getSize(enemySize);
|
1525 |
-
|
1526 |
-
// ์ ์ ์ฐจ ์ถฉ๋ ๋ฐ์ค ํฌ๊ธฐ ์๋ ์กฐ์
|
1527 |
-
const customEnemyBox = new THREE.Box3(
|
1528 |
-
new THREE.Vector3(
|
1529 |
-
enemy.mesh.position.x - enemySize.x * 0.4,
|
1530 |
-
enemy.mesh.position.y - enemySize.y * 0.3,
|
1531 |
-
enemy.mesh.position.z - enemySize.z * 0.4
|
1532 |
-
),
|
1533 |
-
new THREE.Vector3(
|
1534 |
-
enemy.mesh.position.x + enemySize.x * 0.4,
|
1535 |
-
enemy.mesh.position.y + enemySize.y * 0.3,
|
1536 |
-
enemy.mesh.position.z + enemySize.z * 0.4
|
1537 |
-
)
|
1538 |
-
);
|
1539 |
-
|
1540 |
const enemyPreviousPosition = enemy.mesh.position.clone();
|
1541 |
|
1542 |
this.obstacles.forEach(obstacle => {
|
1543 |
-
const
|
1544 |
-
if (
|
1545 |
enemy.mesh.position.copy(enemyPreviousPosition);
|
1546 |
}
|
1547 |
});
|
@@ -1552,8 +1759,9 @@ class Game {
|
|
1552 |
if (!enemy.mesh || !enemy.isLoaded) return;
|
1553 |
|
1554 |
enemy.bullets.forEach((bullet, bulletIndex) => {
|
1555 |
-
const
|
1556 |
-
if (
|
|
|
1557 |
const randomBeatSound = beatSounds[Math.floor(Math.random() * beatSounds.length)];
|
1558 |
const beatAudio = new Audio(randomBeatSound);
|
1559 |
beatAudio.play();
|
@@ -1562,102 +1770,102 @@ class Game {
|
|
1562 |
this.endGame();
|
1563 |
}
|
1564 |
this.scene.remove(bullet);
|
1565 |
-
enemy.bullets.splice(bulletIndex, 1);
|
1566 |
|
1567 |
-
this.
|
1568 |
document.getElementById('health').style.width =
|
1569 |
`${(this.tank.health / MAX_HEALTH) * 100}%`;
|
1570 |
}
|
1571 |
});
|
1572 |
});
|
1573 |
|
1574 |
-
// ํ๋ ์ด์ด
|
1575 |
-
|
1576 |
-
|
1577 |
-
|
|
|
1578 |
|
1579 |
-
|
1580 |
-
|
1581 |
-
|
1582 |
-
|
1583 |
-
|
1584 |
-
|
1585 |
-
|
1586 |
-
|
1587 |
-
|
|
|
|
|
1588 |
}
|
1589 |
}
|
1590 |
}
|
|
|
1591 |
|
1592 |
-
//
|
1593 |
-
|
1594 |
-
|
1595 |
-
|
1596 |
-
|
1597 |
-
|
1598 |
-
|
1599 |
-
|
1600 |
-
|
1601 |
-
const
|
1602 |
-
|
1603 |
-
|
|
|
|
|
|
|
1604 |
|
1605 |
-
//
|
1606 |
-
|
1607 |
-
|
1608 |
-
|
1609 |
-
|
1610 |
-
|
1611 |
-
|
1612 |
-
|
1613 |
-
|
1614 |
-
|
1615 |
-
|
1616 |
-
)
|
1617 |
-
);
|
1618 |
|
1619 |
-
if (bulletBox.intersectsBox(customEnemyBox)) {
|
1620 |
-
const randomHitSound = hitSounds[Math.floor(Math.random() * hitSounds.length)];
|
1621 |
-
const hitAudio = new Audio(randomHitSound);
|
1622 |
-
hitAudio.play();
|
1623 |
|
1624 |
-
|
1625 |
-
|
1626 |
-
|
1627 |
-
|
1628 |
-
|
1629 |
-
|
1630 |
-
|
1631 |
-
|
1632 |
-
|
1633 |
-
|
1634 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1635 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1636 |
}
|
1637 |
}
|
|
|
1638 |
|
1639 |
// ํ๋ ์ด์ด ํฑํฌ์ ์ ์ ์ฐจ ์ถฉ๋ ์ฒดํฌ
|
1640 |
this.enemies.forEach(enemy => {
|
1641 |
if (!enemy.mesh || !enemy.isLoaded) return;
|
1642 |
|
1643 |
-
const
|
1644 |
-
|
1645 |
-
enemyBox.getSize(enemySize);
|
1646 |
-
|
1647 |
-
const customEnemyBox = new THREE.Box3(
|
1648 |
-
new THREE.Vector3(
|
1649 |
-
enemy.mesh.position.x - enemySize.x * 0.4,
|
1650 |
-
enemy.mesh.position.y - enemySize.y * 0.3,
|
1651 |
-
enemy.mesh.position.z - enemySize.z * 0.4
|
1652 |
-
),
|
1653 |
-
new THREE.Vector3(
|
1654 |
-
enemy.mesh.position.x + enemySize.x * 0.4,
|
1655 |
-
enemy.mesh.position.y + enemySize.y * 0.3,
|
1656 |
-
enemy.mesh.position.z + enemySize.z * 0.4
|
1657 |
-
)
|
1658 |
-
);
|
1659 |
-
|
1660 |
-
if (customTankBox.intersectsBox(customEnemyBox)) {
|
1661 |
this.tank.body.position.copy(this.previousTankPosition);
|
1662 |
}
|
1663 |
});
|
|
|
43 |
this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
|
44 |
this.renderer = new THREE.WebGLRenderer({ antialias: true });
|
45 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
// ๋ณ๋์ ๋ฉ์๋๋ก ๋ถ๋ฆฌ
|
47 |
createExplosionEffect(scene, position) {
|
48 |
// ํญ๋ฐ ์ค์ฌ ํ๋์
|
|
|
170 |
this.turretGroup.add(this.turret);
|
171 |
this.body.add(this.turretGroup);
|
172 |
|
173 |
+
// ๊ทธ๋ฆผ์ ์ค์ ์ ๊ทธ๋๋ก ์ ์ง
|
174 |
this.body.traverse((child) => {
|
175 |
if (child.isMesh) {
|
176 |
child.castShadow = true;
|
|
|
200 |
this.shadowPlane.position.y = 0.1;
|
201 |
this.body.add(this.shadowPlane);
|
202 |
|
203 |
+
// ๊ฐ๋จํ ์คํฐ ์์น ์ค์
|
204 |
const spawnPosition = new THREE.Vector3(
|
205 |
+
(Math.random() - 0.5) * (MAP_SIZE * 0.8), // MAP_SIZE์ 80%๋ง ์ฌ์ฉ
|
206 |
+
TANK_HEIGHT, // ๊ณ ์ ๋ ๋์ด ์ฌ์ฉ
|
207 |
(Math.random() - 0.5) * (MAP_SIZE * 0.8)
|
208 |
);
|
209 |
|
210 |
this.body.position.copy(spawnPosition);
|
211 |
+
|
212 |
+
// ํญ๋ฐ ์ดํํธ ๋ฉ์๋ ์ถ๊ฐ
|
213 |
+
this.createExplosionEffect = (scene, position) => {
|
214 |
+
// ํญ๋ฐ ํํฐํด
|
215 |
+
for (let i = 0; i < 15; i++) {
|
216 |
+
const size = Math.random() * 0.2 + 0.1;
|
217 |
+
const geometry = new THREE.SphereGeometry(size);
|
218 |
+
const material = new THREE.MeshBasicMaterial({
|
219 |
+
color: Math.random() < 0.5 ? 0xff4500 : 0xff8c00
|
220 |
+
});
|
221 |
+
const particle = new THREE.Mesh(geometry, material);
|
222 |
+
particle.position.copy(position);
|
223 |
+
|
224 |
+
const speed = Math.random() * 0.3 + 0.2;
|
225 |
+
const angle = Math.random() * Math.PI * 2;
|
226 |
+
const elevation = Math.random() * Math.PI - Math.PI / 2;
|
227 |
+
|
228 |
+
particle.velocity = new THREE.Vector3(
|
229 |
+
Math.cos(angle) * Math.cos(elevation) * speed,
|
230 |
+
Math.sin(elevation) * speed,
|
231 |
+
Math.sin(angle) * Math.cos(elevation) * speed
|
232 |
+
);
|
233 |
+
|
234 |
+
particle.gravity = -0.01;
|
235 |
+
particle.life = Math.random() * 20 + 20;
|
236 |
+
particle.fadeRate = 1 / particle.life;
|
237 |
+
|
238 |
+
scene.add(particle);
|
239 |
+
window.gameInstance.particles.push({
|
240 |
+
mesh: particle,
|
241 |
+
velocity: particle.velocity,
|
242 |
+
gravity: particle.gravity,
|
243 |
+
life: particle.life,
|
244 |
+
fadeRate: particle.fadeRate
|
245 |
+
});
|
246 |
+
}
|
247 |
+
|
248 |
+
// ์ถฉ๋ ์ฌ์ด๋ ์ฌ์
|
249 |
+
const explosionSound = new Audio('sounds/explosion.ogg');
|
250 |
+
explosionSound.volume = 0.3;
|
251 |
+
explosionSound.play();
|
252 |
+
};
|
253 |
+
|
254 |
scene.add(this.body);
|
255 |
this.isLoaded = true;
|
256 |
this.updateAmmoDisplay();
|
|
|
580 |
this.isLoaded = false;
|
581 |
this.moveSpeed = type === 'tank' ? ENEMY_MOVE_SPEED : ENEMY_MOVE_SPEED * 0.7;
|
582 |
}
|
583 |
+
createMuzzleFlash() {
|
584 |
+
if (!this.mesh) return;
|
585 |
+
|
586 |
+
const flashGroup = new THREE.Group();
|
587 |
+
|
588 |
+
// ํ์ผ ํฌ๊ธฐ ์ฆ๊ฐ ๋ฐ ์์ ๋ณ๊ฒฝ
|
589 |
+
const flameGeometry = new THREE.SphereGeometry(1.0, 8, 8);
|
590 |
+
const flameMaterial = new THREE.MeshBasicMaterial({
|
591 |
+
color: 0xffa500, // ๋
ธ๋์์ผ๋ก ๋ณ๊ฒฝ
|
592 |
+
transparent: true,
|
593 |
+
opacity: 0.8
|
594 |
+
});
|
595 |
+
const flame = new THREE.Mesh(flameGeometry, flameMaterial);
|
596 |
+
flame.scale.set(2, 2, 3);
|
597 |
+
flashGroup.add(flame);
|
598 |
+
|
599 |
+
// ์ฐ๊ธฐ ํจ๊ณผ ํฌ๊ธฐ ์ฆ๊ฐ
|
600 |
+
const smokeGeometry = new THREE.SphereGeometry(0.8, 8, 8);
|
601 |
+
const smokeMaterial = new THREE.MeshBasicMaterial({
|
602 |
+
color: 0x555555,
|
603 |
+
transparent: true,
|
604 |
+
opacity: 0.5
|
605 |
+
});
|
606 |
+
|
607 |
+
for (let i = 0; i < 5; i++) {
|
608 |
+
const smoke = new THREE.Mesh(smokeGeometry, smokeMaterial);
|
609 |
+
smoke.position.set(
|
610 |
+
Math.random() * 1 - 0.5,
|
611 |
+
Math.random() * 1 - 0.5,
|
612 |
+
-1 - Math.random()
|
613 |
+
);
|
614 |
+
smoke.scale.set(1.5, 1.5, 1.5);
|
615 |
+
flashGroup.add(smoke);
|
616 |
+
}
|
617 |
+
|
618 |
+
// ํฌ๊ตฌ ์์น ๊ณ์ฐ
|
619 |
+
const muzzleOffset = new THREE.Vector3(0, 0.5, 4);
|
620 |
+
const muzzlePosition = new THREE.Vector3();
|
621 |
+
const meshWorldQuaternion = new THREE.Quaternion();
|
622 |
+
|
623 |
+
this.mesh.getWorldPosition(muzzlePosition);
|
624 |
+
this.mesh.getWorldQuaternion(meshWorldQuaternion);
|
625 |
+
|
626 |
+
muzzleOffset.applyQuaternion(meshWorldQuaternion);
|
627 |
+
muzzlePosition.add(muzzleOffset);
|
628 |
+
|
629 |
+
flashGroup.position.copy(muzzlePosition);
|
630 |
+
flashGroup.quaternion.copy(meshWorldQuaternion);
|
631 |
+
|
632 |
+
this.scene.add(flashGroup);
|
633 |
+
|
634 |
+
// ์ดํํธ ์ง์ ์๊ฐ ์ฆ๊ฐ
|
635 |
+
setTimeout(() => {
|
636 |
+
this.scene.remove(flashGroup);
|
637 |
+
}, 500);
|
638 |
+
}
|
639 |
+
|
640 |
|
641 |
async initialize(loader) {
|
642 |
try {
|
643 |
+
const modelPath = this.type === 'tank' ? '/models/t90.glb' : '/models/t90.glb';
|
644 |
+
const result = await loader.loadAsync(modelPath);
|
645 |
this.mesh = result.scene;
|
646 |
this.mesh.position.copy(this.position);
|
647 |
this.mesh.scale.set(ENEMY_SCALE, ENEMY_SCALE, ENEMY_SCALE);
|
648 |
+
|
649 |
+
this.mesh.traverse((child) => {
|
650 |
+
if (child.isMesh) {
|
651 |
+
child.castShadow = true;
|
652 |
+
child.receiveShadow = true;
|
653 |
+
}
|
654 |
+
});
|
655 |
+
|
656 |
+
this.scene.add(this.mesh);
|
657 |
this.isLoaded = true;
|
658 |
} catch (error) {
|
659 |
console.error('Error loading enemy model:', error);
|
|
|
661 |
}
|
662 |
}
|
663 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
664 |
update(playerPosition) {
|
665 |
+
if (!this.mesh || !this.isLoaded) return;
|
|
|
|
|
|
|
666 |
|
667 |
+
const direction = new THREE.Vector3()
|
668 |
+
.subVectors(playerPosition, this.mesh.position)
|
669 |
+
.normalize();
|
670 |
+
|
671 |
+
const distanceToPlayer = this.mesh.position.distanceTo(playerPosition);
|
672 |
+
const minDistance = 50;
|
673 |
+
|
674 |
+
// ์ด์ ์์น ์ ์ฅ
|
675 |
+
const previousPosition = this.mesh.position.clone();
|
676 |
+
|
677 |
+
if (distanceToPlayer > minDistance) {
|
678 |
+
const moveVector = direction.multiplyScalar(this.moveSpeed);
|
679 |
+
const newPosition = this.mesh.position.clone().add(moveVector);
|
680 |
+
|
681 |
+
// ์งํ ๋์ด ๊ฐ์ ธ์ค๊ธฐ
|
682 |
+
const heightAtNewPos = window.gameInstance.getHeightAtPosition(
|
683 |
+
newPosition.x,
|
684 |
+
newPosition.z
|
685 |
+
);
|
686 |
+
newPosition.y = heightAtNewPos + TANK_HEIGHT;
|
687 |
+
|
688 |
+
// ์์๋ก ์์น ์ด๋ํ์ฌ ์ถฉ๋ ์ฒดํฌ
|
689 |
+
const originalPosition = this.mesh.position.clone();
|
690 |
+
this.mesh.position.copy(newPosition);
|
691 |
+
|
692 |
+
// ์ฅ์ ๋ฌผ๊ณผ ์ถฉ๋ ์ฒดํฌ
|
693 |
+
const enemyBox = new THREE.Box3().setFromObject(this.mesh);
|
694 |
+
let hasCollision = false;
|
695 |
+
|
696 |
+
// ๋ชจ๋ ์ฅ์ ๋ฌผ์ ๋ํด ์ถฉ๋ ๊ฒ์ฌ
|
697 |
+
for (const obstacle of window.gameInstance.obstacles) {
|
698 |
+
const obstacleBox = new THREE.Box3().setFromObject(obstacle);
|
699 |
+
if (enemyBox.intersectsBox(obstacleBox)) {
|
700 |
+
hasCollision = true;
|
701 |
+
break;
|
702 |
+
}
|
703 |
+
}
|
704 |
+
|
705 |
+
// ๋ค๋ฅธ ์ ํฑํฌ์์ ์ถฉ๋ ๊ฒ์ฌ
|
706 |
+
if (!hasCollision) {
|
707 |
+
for (const otherEnemy of window.gameInstance.enemies) {
|
708 |
+
if (otherEnemy !== this && otherEnemy.mesh) {
|
709 |
+
const otherEnemyBox = new THREE.Box3().setFromObject(otherEnemy.mesh);
|
710 |
+
if (enemyBox.intersectsBox(otherEnemyBox)) {
|
711 |
+
hasCollision = true;
|
712 |
+
break;
|
713 |
+
}
|
714 |
+
}
|
715 |
+
}
|
716 |
+
}
|
717 |
+
|
718 |
+
// ๋งต ๊ฒฝ๊ณ ์ฒดํฌ
|
719 |
+
const mapBoundary = MAP_SIZE / 2;
|
720 |
+
if (Math.abs(newPosition.x) > mapBoundary ||
|
721 |
+
Math.abs(newPosition.z) > mapBoundary) {
|
722 |
+
hasCollision = true;
|
723 |
+
}
|
724 |
+
|
725 |
+
// ์ถฉ๋์ด ์์ผ๋ฉด ์ด์ ์์น๋ก ๋ณต๊ท, ์์ผ๋ฉด ์ ์์น ์ ์ง
|
726 |
+
if (hasCollision) {
|
727 |
+
this.mesh.position.copy(previousPosition);
|
728 |
+
|
729 |
+
// ์ถฉ๋ ์ ์ฐํ ๊ฒฝ๋ก ๏ฟฝ๏ฟฝ๏ฟฝ๋
|
730 |
+
const alternateDirections = [
|
731 |
+
new THREE.Vector3(-direction.z, 0, direction.x), // ์ผ์ชฝ์ผ๋ก 90๋
|
732 |
+
new THREE.Vector3(direction.z, 0, -direction.x), // ์ค๋ฅธ์ชฝ์ผ๋ก 90๋
|
733 |
+
new THREE.Vector3(-direction.x, 0, -direction.z) // 180๋ ํ์
|
734 |
+
];
|
735 |
+
|
736 |
+
for (const altDirection of alternateDirections) {
|
737 |
+
const altMoveVector = altDirection.multiplyScalar(this.moveSpeed);
|
738 |
+
const altNewPosition = previousPosition.clone().add(altMoveVector);
|
739 |
+
|
740 |
+
this.mesh.position.copy(altNewPosition);
|
741 |
+
const altEnemyBox = new THREE.Box3().setFromObject(this.mesh);
|
742 |
+
|
743 |
+
let altHasCollision = false;
|
744 |
+
|
745 |
+
// ์๋ก์ด ๋ฐฉํฅ์ ๋ํ ์ถฉ๋ ๊ฒ์ฌ
|
746 |
+
for (const obstacle of window.gameInstance.obstacles) {
|
747 |
+
const obstacleBox = new THREE.Box3().setFromObject(obstacle);
|
748 |
+
if (altEnemyBox.intersectsBox(obstacleBox)) {
|
749 |
+
altHasCollision = true;
|
750 |
+
break;
|
751 |
+
}
|
752 |
+
}
|
753 |
+
|
754 |
+
if (!altHasCollision) {
|
755 |
+
// ์ฐํ ๊ฒฝ๋ก๊ฐ ๊ฐ๋ฅํ๋ฉด ๊ทธ ๋ฐฉํฅ์ผ๋ก ์ด๋
|
756 |
+
break;
|
757 |
+
} else {
|
758 |
+
// ์ฐํ๋ ๋ถ๊ฐ๋ฅํ๋ฉด ์ด์ ์์น๋ก ๋ณต๊ท
|
759 |
+
this.mesh.position.copy(previousPosition);
|
760 |
+
}
|
761 |
+
}
|
762 |
+
}
|
763 |
+
|
764 |
+
// ์งํ์ ๋ฐ๋ฅธ ๊ธฐ์ธ๊ธฐ ์กฐ์
|
765 |
+
const forwardVector = new THREE.Vector3(0, 0, 1).applyQuaternion(this.mesh.quaternion);
|
766 |
+
const rightVector = new THREE.Vector3(1, 0, 0).applyQuaternion(this.mesh.quaternion);
|
767 |
+
|
768 |
+
const frontHeight = window.gameInstance.getHeightAtPosition(
|
769 |
+
this.mesh.position.x + forwardVector.x,
|
770 |
+
this.mesh.position.z + forwardVector.z
|
771 |
+
);
|
772 |
+
const backHeight = window.gameInstance.getHeightAtPosition(
|
773 |
+
this.mesh.position.x - forwardVector.x,
|
774 |
+
this.mesh.position.z - forwardVector.z
|
775 |
+
);
|
776 |
+
const rightHeight = window.gameInstance.getHeightAtPosition(
|
777 |
+
this.mesh.position.x + rightVector.x,
|
778 |
+
this.mesh.position.z + rightVector.z
|
779 |
+
);
|
780 |
+
const leftHeight = window.gameInstance.getHeightAtPosition(
|
781 |
+
this.mesh.position.x - rightVector.x,
|
782 |
+
this.mesh.position.z - rightVector.z
|
783 |
+
);
|
784 |
|
785 |
+
const pitch = Math.atan2(frontHeight - backHeight, 2);
|
786 |
+
const roll = Math.atan2(rightHeight - leftHeight, 2);
|
787 |
+
|
788 |
+
// ํ์ฌ ํ์ ์ ์งํ๋ฉด์ ๊ธฐ์ธ๊ธฐ๋ง ์ ์ฉ
|
789 |
+
const currentRotation = this.mesh.rotation.y;
|
790 |
+
this.mesh.rotation.set(pitch, currentRotation, roll);
|
791 |
+
}
|
792 |
+
|
793 |
+
// ํ๋ ์ด์ด๋ฅผ ํฅํด ํฌํ ํ์
|
794 |
+
this.mesh.lookAt(playerPosition);
|
795 |
+
|
796 |
+
// ์ด์ ์
๋ฐ์ดํธ
|
797 |
+
if (this.bullets) {
|
798 |
+
for (let i = this.bullets.length - 1; i >= 0; i--) {
|
799 |
+
const bullet = this.bullets[i];
|
800 |
+
bullet.position.add(bullet.velocity);
|
801 |
+
|
802 |
+
// ์ด์์ด ๋งต ๋ฐ์ผ๋ก ๋๊ฐ๊ฑฐ๋ ์ฅ์ ๋ฌผ๊ณผ ์ถฉ๋ํ๋ฉด ์ ๊ฑฐ
|
803 |
+
if (Math.abs(bullet.position.x) > MAP_SIZE / 2 ||
|
804 |
+
Math.abs(bullet.position.z) > MAP_SIZE / 2) {
|
805 |
+
this.scene.remove(bullet);
|
806 |
+
this.bullets.splice(i, 1);
|
807 |
+
continue;
|
808 |
+
}
|
809 |
+
|
810 |
+
// ์ด์๊ณผ ์ฅ์ ๋ฌผ ์ถฉ๋ ์ฒดํฌ
|
811 |
+
const bulletBox = new THREE.Box3().setFromObject(bullet);
|
812 |
+
for (const obstacle of window.gameInstance.obstacles) {
|
813 |
+
const obstacleBox = new THREE.Box3().setFromObject(obstacle);
|
814 |
+
if (bulletBox.intersectsBox(obstacleBox)) {
|
815 |
+
this.scene.remove(bullet);
|
816 |
+
this.bullets.splice(i, 1);
|
817 |
+
break;
|
818 |
+
}
|
819 |
+
}
|
820 |
}
|
|
|
821 |
}
|
822 |
+
}
|
823 |
+
|
824 |
|
825 |
shoot(playerPosition) {
|
826 |
+
const currentTime = Date.now();
|
827 |
+
const attackInterval = this.type === 'tank' ?
|
828 |
+
ENEMY_CONFIG.ATTACK_INTERVAL :
|
829 |
+
ENEMY_CONFIG.ATTACK_INTERVAL * 1.5;
|
830 |
|
831 |
+
if (currentTime - this.lastAttackTime < attackInterval) return;
|
|
|
|
|
|
|
832 |
|
833 |
+
// ๋ฐ์ฌ ์ดํํธ ์์ฑ
|
834 |
+
this.createMuzzleFlash();
|
835 |
|
836 |
+
// ๋ฐ์ฌ ์ฌ์ด๋ ์ฌ์
|
837 |
+
const enemyFireSound = new Audio('sounds/mbtfire5.ogg');
|
838 |
+
enemyFireSound.volume = 0.3;
|
839 |
+
enemyFireSound.play();
|
|
|
840 |
|
841 |
+
// ํฌํ ์์ฑ (ํ๋ ์ด์ด์ ์ ์ฌํ๊ฒ ์์ )
|
842 |
+
const bulletGeometry = new THREE.CylinderGeometry(0.2, 0.2, 2, 8);
|
843 |
+
const bulletMaterial = new THREE.MeshBasicMaterial({
|
844 |
+
color: 0xff0000, // ๋นจ๊ฐ์ ํฌํ
|
845 |
+
emissive: 0xff0000,
|
846 |
+
emissiveIntensity: 0.5
|
847 |
+
});
|
848 |
+
const bullet = new THREE.Mesh(bulletGeometry, bulletMaterial);
|
|
|
|
|
849 |
|
850 |
+
// ํฌ๊ตฌ ์์น์์ ๋ฐ์ฌ
|
851 |
+
const muzzleOffset = new THREE.Vector3(0, 0.5, 4);
|
852 |
+
const muzzlePosition = new THREE.Vector3();
|
853 |
+
this.mesh.getWorldPosition(muzzlePosition);
|
854 |
+
muzzleOffset.applyQuaternion(this.mesh.quaternion);
|
855 |
+
muzzlePosition.add(muzzleOffset);
|
856 |
+
|
857 |
+
bullet.position.copy(muzzlePosition);
|
858 |
+
|
859 |
+
// ํฌํ ํ์ ์ค์
|
860 |
+
bullet.quaternion.copy(this.mesh.quaternion);
|
861 |
+
|
862 |
+
const direction = new THREE.Vector3()
|
863 |
+
.subVectors(playerPosition, muzzlePosition)
|
864 |
+
.normalize();
|
865 |
+
|
866 |
+
const bulletSpeed = this.type === 'tank' ?
|
867 |
+
ENEMY_CONFIG.BULLET_SPEED :
|
868 |
+
ENEMY_CONFIG.BULLET_SPEED * 0.8;
|
869 |
+
|
870 |
+
bullet.velocity = direction.multiplyScalar(bulletSpeed);
|
871 |
+
|
872 |
+
// ํฌํ ํธ๋ ์ผ ํจ๊ณผ ์ถ๊ฐ
|
873 |
+
const trailGeometry = new THREE.CylinderGeometry(0.1, 0.1, 1, 8);
|
874 |
+
const trailMaterial = new THREE.MeshBasicMaterial({
|
875 |
+
color: 0xff4444,
|
876 |
+
transparent: true,
|
877 |
+
opacity: 0.5
|
878 |
+
});
|
879 |
+
|
880 |
+
const trail = new THREE.Mesh(trailGeometry, trailMaterial);
|
881 |
+
trail.position.z = -1;
|
882 |
+
bullet.add(trail);
|
883 |
+
|
884 |
+
this.scene.add(bullet);
|
885 |
+
this.bullets.push(bullet);
|
886 |
+
this.lastAttackTime = currentTime;
|
887 |
+
}
|
888 |
|
889 |
takeDamage(damage) {
|
890 |
this.health -= damage;
|
|
|
1718 |
checkCollisions() {
|
1719 |
if (this.isLoading || !this.tank.isLoaded) return;
|
1720 |
|
1721 |
+
// ๋ช
์ค ์ฌ์ด๋ ๋ฐฐ์ด ์ ์
|
1722 |
const hitSounds = [
|
1723 |
'sounds/hit1.ogg', 'sounds/hit2.ogg', 'sounds/hit3.ogg',
|
1724 |
'sounds/hit4.ogg', 'sounds/hit5.ogg', 'sounds/hit6.ogg', 'sounds/hit7.ogg'
|
1725 |
];
|
1726 |
|
1727 |
+
// ํผ๊ฒฉ ์ฌ์ด๋ ๋ฐฐ์ด ์ ์
|
1728 |
const beatSounds = ['sounds/beat1.ogg', 'sounds/beat2.ogg', 'sounds/beat3.ogg'];
|
1729 |
|
1730 |
const tankPosition = this.tank.getPosition();
|
1731 |
+
const tankBoundingBox = new THREE.Box3().setFromObject(this.tank.body);
|
1732 |
+
|
1733 |
+
// ํฑํฌ์ ์ฅ์ ๋ฌผ ์ถฉ๋ ์ฒดํฌ (๊ฐ์ )
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1734 |
this.obstacles.forEach(obstacle => {
|
1735 |
+
if (obstacle.userData.isCollidable) { // ์ถฉ๋ ๊ฐ๋ฅํ ๊ฐ์ฒด๋ง ๊ฒ์ฌ
|
1736 |
+
const obstacleBoundingBox = new THREE.Box3().setFromObject(obstacle);
|
1737 |
+
if (tankBoundingBox.intersectsBox(obstacleBoundingBox)) {
|
1738 |
+
this.tank.body.position.copy(this.previousTankPosition);
|
|
|
1739 |
}
|
1740 |
+
}
|
1741 |
+
});
|
1742 |
+
// ์ ํฑํฌ์ ์ฅ์ ๋ฌผ ์ถฉ๋ ์ฒดํฌ (์ถ๊ฐ)
|
1743 |
this.enemies.forEach(enemy => {
|
1744 |
if (!enemy.mesh || !enemy.isLoaded) return;
|
1745 |
|
1746 |
+
const enemyBoundingBox = new THREE.Box3().setFromObject(enemy.mesh);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1747 |
const enemyPreviousPosition = enemy.mesh.position.clone();
|
1748 |
|
1749 |
this.obstacles.forEach(obstacle => {
|
1750 |
+
const obstacleBoundingBox = new THREE.Box3().setFromObject(obstacle);
|
1751 |
+
if (enemyBoundingBox.intersectsBox(obstacleBoundingBox)) {
|
1752 |
enemy.mesh.position.copy(enemyPreviousPosition);
|
1753 |
}
|
1754 |
});
|
|
|
1759 |
if (!enemy.mesh || !enemy.isLoaded) return;
|
1760 |
|
1761 |
enemy.bullets.forEach((bullet, bulletIndex) => {
|
1762 |
+
const distance = bullet.position.distanceTo(tankPosition);
|
1763 |
+
if (distance < 1) {
|
1764 |
+
// ํ๋ ์ด์ด ํผ๊ฒฉ ์ฌ์ด๋ ์ฌ์
|
1765 |
const randomBeatSound = beatSounds[Math.floor(Math.random() * beatSounds.length)];
|
1766 |
const beatAudio = new Audio(randomBeatSound);
|
1767 |
beatAudio.play();
|
|
|
1770 |
this.endGame();
|
1771 |
}
|
1772 |
this.scene.remove(bullet);
|
1773 |
+
enemy.bullets.splice(bulletIndex, 1); // filter ๋์ splice ์ฌ์ฉ
|
1774 |
|
1775 |
+
this.createExplosion(bullet.position);
|
1776 |
document.getElementById('health').style.width =
|
1777 |
`${(this.tank.health / MAX_HEALTH) * 100}%`;
|
1778 |
}
|
1779 |
});
|
1780 |
});
|
1781 |
|
1782 |
+
// ํ๋ ์ด์ด ํฌํ
|
1783 |
+
// ํฌํ๊ณผ ์ฅ์ ๋ฌผ ์ถฉ๋ ์ฒดํฌ
|
1784 |
+
for (let i = this.tank.bullets.length - 1; i >= 0; i--) {
|
1785 |
+
const bullet = this.tank.bullets[i];
|
1786 |
+
const bulletBox = new THREE.Box3().setFromObject(bullet);
|
1787 |
|
1788 |
+
for (const obstacle of this.obstacles) {
|
1789 |
+
if (obstacle.userData.isCollidable) { // ์ถฉ๋ ๊ฐ๋ฅํ ๊ฐ์ฒด๋ง ๊ฒ์ฌ
|
1790 |
+
const obstacleBox = new THREE.Box3().setFromObject(obstacle);
|
1791 |
+
if (bulletBox.intersectsBox(obstacleBox)) {
|
1792 |
+
// ํญ๋ฐ ์ดํํธ ์์ฑ
|
1793 |
+
this.tank.createExplosionEffect(this.scene, bullet.position);
|
1794 |
+
|
1795 |
+
// ํฌํ ์ ๊ฑฐ
|
1796 |
+
this.scene.remove(bullet);
|
1797 |
+
this.tank.bullets.splice(i, 1);
|
1798 |
+
break;
|
1799 |
}
|
1800 |
}
|
1801 |
}
|
1802 |
+
}
|
1803 |
|
1804 |
+
// ์ ํฑํฌ์ ์ฅ์ ๋ฌผ ์ถฉ๋ ์ฒดํฌ
|
1805 |
+
this.enemies.forEach(enemy => {
|
1806 |
+
if (!enemy.mesh || !enemy.isLoaded) return;
|
1807 |
+
|
1808 |
+
enemy.bullets.forEach((bullet, bulletIndex) => {
|
1809 |
+
const distance = bullet.position.distanceTo(tankPosition);
|
1810 |
+
if (distance < 1) {
|
1811 |
+
// ํผ๊ฒฉ ์ฌ์ด๋ ์ฌ์
|
1812 |
+
const randomBeatSound = beatSounds[Math.floor(Math.random() * beatSounds.length)];
|
1813 |
+
const beatAudio = new Audio(randomBeatSound);
|
1814 |
+
beatAudio.play();
|
1815 |
+
|
1816 |
+
if (this.tank.takeDamage(250)) {
|
1817 |
+
this.endGame();
|
1818 |
+
}
|
1819 |
|
1820 |
+
// ๊ธฐ์กด์ createExplosion ๋์ createExplosionEffect ์ฌ์ฉ
|
1821 |
+
this.tank.createExplosionEffect(this.scene, bullet.position);
|
1822 |
+
|
1823 |
+
this.scene.remove(bullet);
|
1824 |
+
enemy.bullets.splice(bulletIndex, 1);
|
1825 |
+
|
1826 |
+
document.getElementById('health').style.width =
|
1827 |
+
`${(this.tank.health / MAX_HEALTH) * 100}%`;
|
1828 |
+
}
|
1829 |
+
});
|
1830 |
+
});
|
|
|
|
|
1831 |
|
|
|
|
|
|
|
|
|
1832 |
|
1833 |
+
// ํ๋ ์ด์ด ์ด์๊ณผ ์ ์ถฉ๋ ์ฒดํฌ
|
1834 |
+
for (let i = this.tank.bullets.length - 1; i >= 0; i--) {
|
1835 |
+
const bullet = this.tank.bullets[i];
|
1836 |
+
for (let j = this.enemies.length - 1; j >= 0; j--) {
|
1837 |
+
const enemy = this.enemies[j];
|
1838 |
+
if (!enemy.mesh || !enemy.isLoaded) continue;
|
1839 |
+
|
1840 |
+
const distance = bullet.position.distanceTo(enemy.mesh.position);
|
1841 |
+
if (distance < 2) {
|
1842 |
+
const randomHitSound = hitSounds[Math.floor(Math.random() * hitSounds.length)];
|
1843 |
+
const hitAudio = new Audio(randomHitSound);
|
1844 |
+
hitAudio.play();
|
1845 |
+
|
1846 |
+
if (enemy.takeDamage(50)) {
|
1847 |
+
enemy.destroy();
|
1848 |
+
this.enemies.splice(j, 1);
|
1849 |
+
this.score += 100;
|
1850 |
+
document.getElementById('score').textContent = `Score: ${this.score}`;
|
1851 |
}
|
1852 |
+
|
1853 |
+
// ์ฌ๊ธฐ๋ ๋์ผํ๊ฒ ์์
|
1854 |
+
this.tank.createExplosionEffect(this.scene, bullet.position);
|
1855 |
+
|
1856 |
+
this.scene.remove(bullet);
|
1857 |
+
this.tank.bullets.splice(i, 1);
|
1858 |
+
break;
|
1859 |
}
|
1860 |
}
|
1861 |
+
}
|
1862 |
|
1863 |
// ํ๋ ์ด์ด ํฑํฌ์ ์ ์ ์ฐจ ์ถฉ๋ ์ฒดํฌ
|
1864 |
this.enemies.forEach(enemy => {
|
1865 |
if (!enemy.mesh || !enemy.isLoaded) return;
|
1866 |
|
1867 |
+
const enemyBoundingBox = new THREE.Box3().setFromObject(enemy.mesh);
|
1868 |
+
if (tankBoundingBox.intersectsBox(enemyBoundingBox)) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1869 |
this.tank.body.position.copy(this.previousTankPosition);
|
1870 |
}
|
1871 |
});
|