Spaces:
Running
Running
Update game.js
Browse files
game.js
CHANGED
@@ -1297,10 +1297,7 @@ class Game {
|
|
1297 |
|
1298 |
async loadFieldTerrain() {
|
1299 |
try {
|
1300 |
-
|
1301 |
-
const tilesPerSide = Math.ceil(GAME_CONSTANTS.MAP_SIZE / fieldTileSize);
|
1302 |
-
|
1303 |
-
// field.glb๋ฅผ ํ ๋ฒ๋ง ๋ก๋
|
1304 |
const result = await this.loader.loadAsync('models/field.glb');
|
1305 |
const fieldModel = result.scene;
|
1306 |
|
@@ -1308,36 +1305,29 @@ class Game {
|
|
1308 |
const box = new THREE.Box3().setFromObject(fieldModel);
|
1309 |
const size = box.getSize(new THREE.Vector3());
|
1310 |
|
1311 |
-
// ๋งต
|
1312 |
-
|
1313 |
-
|
1314 |
-
|
1315 |
-
|
1316 |
-
|
1317 |
-
|
1318 |
-
|
1319 |
-
|
1320 |
-
|
1321 |
-
|
1322 |
-
|
1323 |
-
|
1324 |
-
|
1325 |
-
// ๋๋ค ํ์ ์ผ๋ก ๋ฐ๋ณต ํจํด ๋ฐฉ์ง
|
1326 |
-
fieldClone.rotation.y = Math.floor(Math.random() * 4) * Math.PI / 2;
|
1327 |
-
|
1328 |
-
// ๊ทธ๋ฆผ์ ์ค์
|
1329 |
-
fieldClone.traverse((child) => {
|
1330 |
-
if (child.isMesh) {
|
1331 |
-
child.receiveShadow = true;
|
1332 |
-
child.castShadow = true;
|
1333 |
-
}
|
1334 |
-
});
|
1335 |
|
1336 |
-
|
|
|
|
|
|
|
1337 |
}
|
1338 |
-
}
|
1339 |
|
1340 |
-
|
|
|
1341 |
|
1342 |
} catch (error) {
|
1343 |
console.error('Field terrain loading failed:', error);
|
@@ -1379,27 +1369,209 @@ class Game {
|
|
1379 |
}
|
1380 |
|
1381 |
addAtmosphericEffects() {
|
1382 |
-
//
|
1383 |
-
|
1384 |
-
|
1385 |
-
|
1386 |
-
|
1387 |
-
|
1388 |
-
|
1389 |
-
|
1390 |
-
|
1391 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1392 |
});
|
1393 |
|
1394 |
-
const
|
1395 |
-
|
1396 |
-
|
1397 |
-
|
1398 |
-
|
|
|
|
|
|
|
1399 |
);
|
1400 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1401 |
|
1402 |
-
|
|
|
1403 |
}
|
1404 |
}
|
1405 |
|
|
|
1297 |
|
1298 |
async loadFieldTerrain() {
|
1299 |
try {
|
1300 |
+
// field.glb๋ฅผ ํ ๋ฒ๋ง ๋ก๋ํ์ฌ ์ ์ฒด ๋งต ํฌ๊ธฐ๋ก ์ค์ผ์ผ
|
|
|
|
|
|
|
1301 |
const result = await this.loader.loadAsync('models/field.glb');
|
1302 |
const fieldModel = result.scene;
|
1303 |
|
|
|
1305 |
const box = new THREE.Box3().setFromObject(fieldModel);
|
1306 |
const size = box.getSize(new THREE.Vector3());
|
1307 |
|
1308 |
+
// ์ ์ฒด ๋งต ํฌ๊ธฐ์ ๋ง๊ฒ ์ค์ผ์ผ ๊ณ์ฐ
|
1309 |
+
const scaleX = GAME_CONSTANTS.MAP_SIZE / size.x;
|
1310 |
+
const scaleZ = GAME_CONSTANTS.MAP_SIZE / size.z;
|
1311 |
+
const scale = Math.max(scaleX, scaleZ); // ๋งต์ ์์ ํ ์ฑ์ฐ๋๋ก ๋ ํฐ ์ค์ผ์ผ ์ฌ์ฉ
|
1312 |
+
|
1313 |
+
fieldModel.scale.set(scale, scale, scale);
|
1314 |
+
fieldModel.position.set(0, 0, 0);
|
1315 |
+
|
1316 |
+
// ๊ทธ๋ฆผ์ ์ค์
|
1317 |
+
fieldModel.traverse((child) => {
|
1318 |
+
if (child.isMesh) {
|
1319 |
+
child.receiveShadow = true;
|
1320 |
+
child.castShadow = true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1321 |
|
1322 |
+
// ์งํ ๋จธํฐ๋ฆฌ์ผ ๊ฐ์
|
1323 |
+
if (child.material) {
|
1324 |
+
child.material.side = THREE.DoubleSide;
|
1325 |
+
}
|
1326 |
}
|
1327 |
+
});
|
1328 |
|
1329 |
+
this.scene.add(fieldModel);
|
1330 |
+
console.log(`Field terrain loaded and scaled to ${scale}x`);
|
1331 |
|
1332 |
} catch (error) {
|
1333 |
console.error('Field terrain loading failed:', error);
|
|
|
1369 |
}
|
1370 |
|
1371 |
addAtmosphericEffects() {
|
1372 |
+
// ๊ฐ์ ๋ ์ฐ ์งํ ์์คํ
|
1373 |
+
this.createMountainRanges();
|
1374 |
+
|
1375 |
+
// ๋๊ธฐ ์ฐ๋ ํจ๊ณผ
|
1376 |
+
this.addAtmosphericScattering();
|
1377 |
+
}
|
1378 |
+
|
1379 |
+
createMountainRanges() {
|
1380 |
+
// ์ฐ๋งฅ ์์ฑ์ ์ํ ์ค์
|
1381 |
+
const mountainRanges = [
|
1382 |
+
{
|
1383 |
+
distance: GAME_CONSTANTS.MAP_SIZE * 0.6,
|
1384 |
+
count: 25,
|
1385 |
+
heightRange: { min: 2000, max: 5000 },
|
1386 |
+
widthRange: { min: 1500, max: 3000 },
|
1387 |
+
color: 0x5a6b7a
|
1388 |
+
},
|
1389 |
+
{
|
1390 |
+
distance: GAME_CONSTANTS.MAP_SIZE * 0.8,
|
1391 |
+
count: 20,
|
1392 |
+
heightRange: { min: 3000, max: 7000 },
|
1393 |
+
widthRange: { min: 2000, max: 4000 },
|
1394 |
+
color: 0x4a5b6a
|
1395 |
+
},
|
1396 |
+
{
|
1397 |
+
distance: GAME_CONSTANTS.MAP_SIZE * 1.0,
|
1398 |
+
count: 15,
|
1399 |
+
heightRange: { min: 4000, max: 9000 },
|
1400 |
+
widthRange: { min: 3000, max: 5000 },
|
1401 |
+
color: 0x3a4b5a
|
1402 |
+
}
|
1403 |
+
];
|
1404 |
+
|
1405 |
+
mountainRanges.forEach(range => {
|
1406 |
+
for (let i = 0; i < range.count; i++) {
|
1407 |
+
const angle = (i / range.count) * Math.PI * 2 + (Math.random() - 0.5) * 0.3;
|
1408 |
+
const distance = range.distance + (Math.random() - 0.5) * 1000;
|
1409 |
+
|
1410 |
+
// ์ฐ ๊ทธ๋ฃน ์์ฑ (์ฌ๋ฌ ๋ด์ฐ๋ฆฌ๋ก ๊ตฌ์ฑ)
|
1411 |
+
const mountainGroup = new THREE.Group();
|
1412 |
+
|
1413 |
+
// ๋ฉ์ธ ๋ด์ฐ๋ฆฌ
|
1414 |
+
const mainPeakHeight = range.heightRange.min + Math.random() * (range.heightRange.max - range.heightRange.min);
|
1415 |
+
const mainPeakWidth = range.widthRange.min + Math.random() * (range.widthRange.max - range.widthRange.min);
|
1416 |
+
|
1417 |
+
// ๊ฐ์ ๋ ์ฐ ํํ (ํ๊ฐ๋ฟ ๋์ ๋ ์์ฐ์ค๋ฌ์ด ํํ)
|
1418 |
+
const mainPeakGeometry = new THREE.ConeGeometry(mainPeakWidth, mainPeakHeight, 12);
|
1419 |
+
const vertices = mainPeakGeometry.attributes.position.array;
|
1420 |
+
|
1421 |
+
// ์ ์ ๋ณํ์ผ๋ก ๋ ์์ฐ์ค๋ฌ์ด ์ฐ ํํ ๋ง๋ค๊ธฐ
|
1422 |
+
for (let j = 0; j < vertices.length; j += 3) {
|
1423 |
+
const x = vertices[j];
|
1424 |
+
const y = vertices[j + 1];
|
1425 |
+
const z = vertices[j + 2];
|
1426 |
+
|
1427 |
+
// ๋์ด์ ๋ฐ๋ฅธ ๋๋ค ๋ณํ
|
1428 |
+
const heightFactor = (y + mainPeakHeight * 0.5) / mainPeakHeight;
|
1429 |
+
const noise = (Math.random() - 0.5) * mainPeakWidth * 0.3 * (1 - heightFactor);
|
1430 |
+
|
1431 |
+
vertices[j] += noise;
|
1432 |
+
vertices[j + 2] += noise;
|
1433 |
+
}
|
1434 |
+
|
1435 |
+
mainPeakGeometry.computeVertexNormals();
|
1436 |
+
|
1437 |
+
const mainPeakMaterial = new THREE.MeshLambertMaterial({
|
1438 |
+
color: range.color,
|
1439 |
+
flatShading: true,
|
1440 |
+
fog: true
|
1441 |
+
});
|
1442 |
+
|
1443 |
+
const mainPeak = new THREE.Mesh(mainPeakGeometry, mainPeakMaterial);
|
1444 |
+
mountainGroup.add(mainPeak);
|
1445 |
+
|
1446 |
+
// ์ฃผ๋ณ ์์ ๋ด์ฐ๋ฆฌ๋ค ์ถ๊ฐ
|
1447 |
+
const subPeakCount = 2 + Math.floor(Math.random() * 3);
|
1448 |
+
for (let j = 0; j < subPeakCount; j++) {
|
1449 |
+
const subPeakHeight = mainPeakHeight * (0.5 + Math.random() * 0.3);
|
1450 |
+
const subPeakWidth = mainPeakWidth * (0.6 + Math.random() * 0.3);
|
1451 |
+
|
1452 |
+
const subPeakGeometry = new THREE.ConeGeometry(subPeakWidth, subPeakHeight, 8);
|
1453 |
+
const subPeak = new THREE.Mesh(subPeakGeometry, mainPeakMaterial);
|
1454 |
+
|
1455 |
+
// ๋ฉ์ธ ๋ด์ฐ๋ฆฌ ์ฃผ๋ณ์ ๋ฐฐ์น
|
1456 |
+
const offsetAngle = (j / subPeakCount) * Math.PI * 2;
|
1457 |
+
const offsetDistance = mainPeakWidth * (0.6 + Math.random() * 0.4);
|
1458 |
+
|
1459 |
+
subPeak.position.set(
|
1460 |
+
Math.cos(offsetAngle) * offsetDistance,
|
1461 |
+
-mainPeakHeight * 0.3,
|
1462 |
+
Math.sin(offsetAngle) * offsetDistance
|
1463 |
+
);
|
1464 |
+
|
1465 |
+
mountainGroup.add(subPeak);
|
1466 |
+
}
|
1467 |
+
|
1468 |
+
// ์ฐ ๊ธฐ๋ฐ๋ถ (๋ ์์ฐ์ค๋ฌ์ด ๊ฒฝ์ฌ)
|
1469 |
+
const baseGeometry = new THREE.CylinderGeometry(
|
1470 |
+
mainPeakWidth * 1.5,
|
1471 |
+
mainPeakWidth * 2.5,
|
1472 |
+
mainPeakHeight * 0.3,
|
1473 |
+
12
|
1474 |
+
);
|
1475 |
+
const baseMaterial = new THREE.MeshLambertMaterial({
|
1476 |
+
color: new THREE.Color(range.color).multiplyScalar(0.8),
|
1477 |
+
flatShading: true,
|
1478 |
+
fog: true
|
1479 |
+
});
|
1480 |
+
const base = new THREE.Mesh(baseGeometry, baseMaterial);
|
1481 |
+
base.position.y = -mainPeakHeight * 0.35;
|
1482 |
+
mountainGroup.add(base);
|
1483 |
+
|
1484 |
+
// ์ฐ ๊ทธ๋ฃน ์์น ์ค์
|
1485 |
+
mountainGroup.position.set(
|
1486 |
+
Math.cos(angle) * distance,
|
1487 |
+
mainPeakHeight * 0.5,
|
1488 |
+
Math.sin(angle) * distance
|
1489 |
+
);
|
1490 |
+
|
1491 |
+
// ์ฝ๊ฐ์ ๋๋ค ํ์
|
1492 |
+
mountainGroup.rotation.y = Math.random() * Math.PI;
|
1493 |
+
|
1494 |
+
// ๊ฑฐ๋ฆฌ์ ๋ฐ๋ฅธ ์๊ฐ ํจ๊ณผ๋ฅผ ์ํ ๋ ์ด์ด ์ค์
|
1495 |
+
mountainGroup.layers.set(0);
|
1496 |
+
|
1497 |
+
this.scene.add(mountainGroup);
|
1498 |
+
}
|
1499 |
+
});
|
1500 |
+
|
1501 |
+
// ๊ฐ์ฅ ๋จผ ๊ฑฐ๋ฆฌ์ ๊ฑฐ๋ํ ์ฐ๋งฅ ์ค๋ฃจ์ฃ ์ถ๊ฐ
|
1502 |
+
this.createDistantMountainSilhouette();
|
1503 |
+
}
|
1504 |
+
|
1505 |
+
createDistantMountainSilhouette() {
|
1506 |
+
// ์๊ฑฐ๋ฆฌ ์ฐ๋งฅ ์ค๋ฃจ์ฃ (2D ํ๋ฉด)
|
1507 |
+
const silhouetteDistance = GAME_CONSTANTS.MAP_SIZE * 1.2;
|
1508 |
+
const segments = 50;
|
1509 |
+
|
1510 |
+
for (let side = 0; side < 4; side++) {
|
1511 |
+
const curve = new THREE.CatmullRomCurve3([]);
|
1512 |
+
|
1513 |
+
// ์ฐ๋งฅ ์ค๋ฃจ์ฃ ๊ณก์ ์์ฑ
|
1514 |
+
for (let i = 0; i <= segments; i++) {
|
1515 |
+
const t = i / segments;
|
1516 |
+
const x = (t - 0.5) * GAME_CONSTANTS.MAP_SIZE;
|
1517 |
+
const baseHeight = 3000;
|
1518 |
+
const variation = Math.sin(t * Math.PI * 3) * 2000 +
|
1519 |
+
Math.sin(t * Math.PI * 7) * 1000 +
|
1520 |
+
Math.sin(t * Math.PI * 13) * 500;
|
1521 |
+
const y = baseHeight + variation + Math.random() * 500;
|
1522 |
+
|
1523 |
+
curve.points.push(new THREE.Vector3(x, y, 0));
|
1524 |
+
}
|
1525 |
+
|
1526 |
+
// ์ค๋ฃจ์ฃ ๋ฉ์ ์์ฑ
|
1527 |
+
const points = curve.getPoints(100);
|
1528 |
+
const shape = new THREE.Shape();
|
1529 |
+
|
1530 |
+
shape.moveTo(points[0].x, 0);
|
1531 |
+
points.forEach(p => shape.lineTo(p.x, p.y));
|
1532 |
+
shape.lineTo(points[points.length - 1].x, 0);
|
1533 |
+
shape.lineTo(points[0].x, 0);
|
1534 |
+
|
1535 |
+
const silhouetteGeometry = new THREE.ShapeGeometry(shape);
|
1536 |
+
const silhouetteMaterial = new THREE.MeshBasicMaterial({
|
1537 |
+
color: 0x2a3b4a,
|
1538 |
+
side: THREE.DoubleSide,
|
1539 |
+
fog: true,
|
1540 |
+
transparent: true,
|
1541 |
+
opacity: 0.7
|
1542 |
});
|
1543 |
|
1544 |
+
const silhouette = new THREE.Mesh(silhouetteGeometry, silhouetteMaterial);
|
1545 |
+
|
1546 |
+
// ๊ฐ ๋ฉด์ ๋ฐฐ์น
|
1547 |
+
const angle = (side / 4) * Math.PI * 2;
|
1548 |
+
silhouette.position.set(
|
1549 |
+
Math.cos(angle) * silhouetteDistance,
|
1550 |
+
0,
|
1551 |
+
Math.sin(angle) * silhouetteDistance
|
1552 |
);
|
1553 |
+
silhouette.lookAt(0, 0, 0);
|
1554 |
+
|
1555 |
+
this.scene.add(silhouette);
|
1556 |
+
}
|
1557 |
+
}
|
1558 |
+
|
1559 |
+
addAtmosphericScattering() {
|
1560 |
+
// ๋๊ธฐ ์ฐ๋ ํจ๊ณผ๋ฅผ ์ํ ๋ฐํฌ๋ช
๊ตฌ์ฒด๋ค
|
1561 |
+
const scatteringLayers = 5;
|
1562 |
+
for (let i = 0; i < scatteringLayers; i++) {
|
1563 |
+
const radius = 20000 + i * 5000;
|
1564 |
+
const geometry = new THREE.SphereGeometry(radius, 32, 32);
|
1565 |
+
const material = new THREE.MeshBasicMaterial({
|
1566 |
+
color: 0x87CEEB,
|
1567 |
+
transparent: true,
|
1568 |
+
opacity: 0.02,
|
1569 |
+
side: THREE.BackSide,
|
1570 |
+
depthWrite: false
|
1571 |
+
});
|
1572 |
|
1573 |
+
const atmosphere = new THREE.Mesh(geometry, material);
|
1574 |
+
this.scene.add(atmosphere);
|
1575 |
}
|
1576 |
}
|
1577 |
|