cutechicken commited on
Commit
bd21aea
ยท
verified ยท
1 Parent(s): 2ba793e

Update game.js

Browse files
Files changed (1) hide show
  1. game.js +315 -154
game.js CHANGED
@@ -1195,46 +1195,32 @@ class Game {
1195
  }
1196
 
1197
  setupScene() {
1198
- // ๊ณ ๊ธ‰ ํ•˜๋Š˜ ์„ค์ •
1199
- this.createSkybox();
1200
- this.scene.fog = new THREE.Fog(0xc0d8ff, 1000, 30000);
1201
 
1202
- // ๊ฐœ์„ ๋œ ์กฐ๋ช… ์„ค์ •
1203
- const ambientLight = new THREE.AmbientLight(0xffffff, 0.7);
1204
  this.scene.add(ambientLight);
1205
 
1206
- const directionalLight = new THREE.DirectionalLight(0xffffcc, 1.2);
1207
- directionalLight.position.set(8000, 10000, 8000);
1208
  directionalLight.castShadow = true;
1209
- directionalLight.shadow.mapSize.width = 4096;
1210
- directionalLight.shadow.mapSize.height = 4096;
1211
  directionalLight.shadow.camera.near = 0.5;
1212
- directionalLight.shadow.camera.far = 20000;
1213
- directionalLight.shadow.camera.left = -10000;
1214
- directionalLight.shadow.camera.right = 10000;
1215
- directionalLight.shadow.camera.top = 10000;
1216
- directionalLight.shadow.camera.bottom = -10000;
1217
  this.scene.add(directionalLight);
1218
 
1219
- // ๋ณด์กฐ ์กฐ๋ช… ์ถ”๊ฐ€
1220
- const fillLight = new THREE.DirectionalLight(0x88aaff, 0.4);
1221
- fillLight.position.set(-5000, 8000, -5000);
1222
- this.scene.add(fillLight);
1223
 
1224
- // ๊ธฐ๋ณธ ์ง€๋ฉด (field.glb ๋กœ๋”ฉ ์‹คํŒจ ์‹œ ํด๋ฐฑ)
1225
- const groundGeometry = new THREE.PlaneGeometry(GAME_CONSTANTS.MAP_SIZE, GAME_CONSTANTS.MAP_SIZE);
1226
- const groundMaterial = new THREE.MeshLambertMaterial({
1227
- color: 0x4a5d23,
1228
- transparent: true,
1229
- opacity: 0.9
1230
- });
1231
- const ground = new THREE.Mesh(groundGeometry, groundMaterial);
1232
- ground.rotation.x = -Math.PI / 2;
1233
- ground.receiveShadow = true;
1234
- this.scene.add(ground);
1235
-
1236
- // field.glb ๋ชจ๋ธ๋กœ ์ง€ํ˜• ์ฑ„์šฐ๊ธฐ
1237
- this.loadFieldTerrain();
1238
 
1239
  // ๊ฐœ์„ ๋œ ๊ตฌ๋ฆ„ ์ถ”๊ฐ€
1240
  this.addClouds();
@@ -1243,102 +1229,9 @@ class Game {
1243
  this.addAtmosphericEffects();
1244
  }
1245
 
1246
- createSkybox() {
1247
- // ๊ทธ๋ผ๋ฐ์ด์…˜ ํ•˜๋Š˜ ์ƒ์„ฑ
1248
- const skyGeo = new THREE.SphereGeometry(40000, 32, 32);
1249
- const skyMat = new THREE.ShaderMaterial({
1250
- uniforms: {
1251
- topColor: { value: new THREE.Color(0x0077ff) },
1252
- bottomColor: { value: new THREE.Color(0xffffff) },
1253
- offset: { value: 33 },
1254
- exponent: { value: 0.6 }
1255
- },
1256
- vertexShader: `
1257
- varying vec3 vWorldPosition;
1258
- void main() {
1259
- vec4 worldPosition = modelMatrix * vec4(position, 1.0);
1260
- vWorldPosition = worldPosition.xyz;
1261
- gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
1262
- }
1263
- `,
1264
- fragmentShader: `
1265
- uniform vec3 topColor;
1266
- uniform vec3 bottomColor;
1267
- uniform float offset;
1268
- uniform float exponent;
1269
- varying vec3 vWorldPosition;
1270
- void main() {
1271
- float h = normalize(vWorldPosition + offset).y;
1272
- gl_FragColor = vec4(mix(bottomColor, topColor, max(pow(max(h, 0.0), exponent), 0.0)), 1.0);
1273
- }
1274
- `,
1275
- side: THREE.BackSide
1276
- });
1277
-
1278
- const sky = new THREE.Mesh(skyGeo, skyMat);
1279
- this.scene.add(sky);
1280
-
1281
- // ํƒœ์–‘ ์ถ”๊ฐ€
1282
- const sunGeometry = new THREE.SphereGeometry(800, 32, 32);
1283
- const sunMaterial = new THREE.MeshBasicMaterial({
1284
- color: 0xffffaa,
1285
- emissive: 0xffffaa,
1286
- emissiveIntensity: 2
1287
- });
1288
- const sun = new THREE.Mesh(sunGeometry, sunMaterial);
1289
- sun.position.set(10000, 15000, 10000);
1290
- this.scene.add(sun);
1291
-
1292
- // ํƒœ์–‘ ๊ด‘์„  ํšจ๊ณผ
1293
- const sunlight = new THREE.PointLight(0xffffaa, 0.5, 30000);
1294
- sunlight.position.copy(sun.position);
1295
- this.scene.add(sunlight);
1296
- }
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
-
1304
- // ๋ชจ๋ธ์˜ ์‹ค์ œ ํฌ๊ธฐ ๊ณ„์‚ฐ
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);
1334
- // ํด๋ฐฑ: ํ”„๋กœ์‹œ์ €๋Ÿด ์ง€ํ˜• ์ƒ์„ฑ
1335
- this.createProceduralTerrain();
1336
- }
1337
- }
1338
-
1339
  createProceduralTerrain() {
1340
- // field.glb ๋กœ๋”ฉ ์‹คํŒจ ์‹œ ํ”„๋กœ์‹œ์ €๋Ÿด ์ง€ํ˜• ์ƒ์„ฑ
1341
- const segments = 50;
1342
  const terrainGeometry = new THREE.PlaneGeometry(
1343
  GAME_CONSTANTS.MAP_SIZE,
1344
  GAME_CONSTANTS.MAP_SIZE,
@@ -1348,32 +1241,319 @@ class Game {
1348
 
1349
  // ๋ฒ„ํ…์Šค์— ๋…ธ์ด์ฆˆ ์ถ”๊ฐ€ํ•˜์—ฌ ์–ธ๋• ํšจ๊ณผ
1350
  const vertices = terrainGeometry.attributes.position.array;
 
 
1351
  for (let i = 0; i < vertices.length; i += 3) {
1352
  const x = vertices[i];
1353
  const y = vertices[i + 1];
1354
- const distance = Math.sqrt(x * x + y * y);
1355
- vertices[i + 2] = Math.sin(distance * 0.0003) * 50 + Math.random() * 20;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1356
  }
1357
 
1358
  terrainGeometry.computeVertexNormals();
1359
 
 
1360
  const terrainMaterial = new THREE.MeshLambertMaterial({
1361
- color: 0x3a5f3a,
1362
- flatShading: true
1363
  });
1364
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1365
  const terrain = new THREE.Mesh(terrainGeometry, terrainMaterial);
1366
  terrain.rotation.x = -Math.PI / 2;
1367
  terrain.receiveShadow = true;
 
 
 
 
 
 
1368
  this.scene.add(terrain);
1369
  }
1370
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1371
  addAtmosphericEffects() {
1372
  // ๊ฐœ์„ ๋œ ์‚ฐ ์ง€ํ˜• ์‹œ์Šคํ…œ
1373
  this.createMountainRanges();
1374
-
1375
- // ๋Œ€๊ธฐ ์‚ฐ๋ž€ ํšจ๊ณผ
1376
- this.addAtmosphericScattering();
1377
  }
1378
 
1379
  createMountainRanges() {
@@ -1491,7 +1671,7 @@ class Game {
1491
  // ์•ฝ๊ฐ„์˜ ๋žœ๋ค ํšŒ์ „
1492
  mountainGroup.rotation.y = Math.random() * Math.PI;
1493
 
1494
- // ๊ฑฐ๋ฆฌ์— ๋”ฐ๋ฅธ ์•ˆ๊ฐœ ํšจ๊ณผ๋ฅผ ์œ„ํ•œ ๋ ˆ์ด์–ด ์„ค์ •
1495
  mountainGroup.layers.set(0);
1496
 
1497
  this.scene.add(mountainGroup);
@@ -1555,25 +1735,6 @@ class Game {
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
 
1578
  addClouds() {
1579
  // ๋‹ค์ธต ๊ตฌ๋ฆ„ ์‹œ์Šคํ…œ
 
1195
  }
1196
 
1197
  setupScene() {
1198
+ // ๊ธฐ๋ณธ ํ•˜๋Š˜ ์„ค์ •
1199
+ this.scene.background = new THREE.Color(0x87CEEB);
1200
+ this.scene.fog = new THREE.Fog(0x87CEEB, 1000, 30000);
1201
 
1202
+ // ๊ธฐ๋ณธ ์กฐ๋ช… ์„ค์ •
1203
+ const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
1204
  this.scene.add(ambientLight);
1205
 
1206
+ const directionalLight = new THREE.DirectionalLight(0xffffff, 1.0);
1207
+ directionalLight.position.set(5000, 8000, 5000);
1208
  directionalLight.castShadow = true;
1209
+ directionalLight.shadow.mapSize.width = 2048;
1210
+ directionalLight.shadow.mapSize.height = 2048;
1211
  directionalLight.shadow.camera.near = 0.5;
1212
+ directionalLight.shadow.camera.far = 15000;
1213
+ directionalLight.shadow.camera.left = -8000;
1214
+ directionalLight.shadow.camera.right = 8000;
1215
+ directionalLight.shadow.camera.top = 8000;
1216
+ directionalLight.shadow.camera.bottom = -8000;
1217
  this.scene.add(directionalLight);
1218
 
1219
+ // ํ”„๋กœ์‹œ์ €๋Ÿด ์ง€ํ˜• ์ƒ์„ฑ
1220
+ this.createProceduralTerrain();
 
 
1221
 
1222
+ // ์ดˆ๋ชฉ ๋ฐฐ์น˜
1223
+ this.addVegetation();
 
 
 
 
 
 
 
 
 
 
 
 
1224
 
1225
  // ๊ฐœ์„ ๋œ ๊ตฌ๋ฆ„ ์ถ”๊ฐ€
1226
  this.addClouds();
 
1229
  this.addAtmosphericEffects();
1230
  }
1231
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1232
  createProceduralTerrain() {
1233
+ // ์ง€ํ˜• ์ƒ์„ฑ์„ ์œ„ํ•œ ์„ธ๊ทธ๋จผํŠธ
1234
+ const segments = 100;
1235
  const terrainGeometry = new THREE.PlaneGeometry(
1236
  GAME_CONSTANTS.MAP_SIZE,
1237
  GAME_CONSTANTS.MAP_SIZE,
 
1241
 
1242
  // ๋ฒ„ํ…์Šค์— ๋…ธ์ด์ฆˆ ์ถ”๊ฐ€ํ•˜์—ฌ ์–ธ๋• ํšจ๊ณผ
1243
  const vertices = terrainGeometry.attributes.position.array;
1244
+ const heightMap = [];
1245
+
1246
  for (let i = 0; i < vertices.length; i += 3) {
1247
  const x = vertices[i];
1248
  const y = vertices[i + 1];
1249
+
1250
+ // ๋‹ค์ค‘ ์Šค์ผ€์ผ ๋…ธ์ด์ฆˆ๋กœ ์ž์—ฐ์Šค๋Ÿฌ์šด ์ง€ํ˜• ์ƒ์„ฑ
1251
+ let height = 0;
1252
+
1253
+ // ํฐ ์Šค์ผ€์ผ ์–ธ๋• (์ฃผ์š” ์ง€ํ˜•)
1254
+ height += Math.sin(x * 0.0001) * Math.cos(y * 0.0001) * 300;
1255
+
1256
+ // ์ค‘๊ฐ„ ์Šค์ผ€์ผ ์–ธ๋•
1257
+ height += Math.sin(x * 0.0003) * Math.cos(y * 0.0003) * 150;
1258
+
1259
+ // ์ž‘์€ ์Šค์ผ€์ผ ๋””ํ…Œ์ผ
1260
+ height += Math.sin(x * 0.001) * Math.cos(y * 0.001) * 50;
1261
+
1262
+ // ๋žœ๋ค ๋…ธ์ด์ฆˆ
1263
+ height += (Math.random() - 0.5) * 20;
1264
+
1265
+ // ๋งต ๊ฐ€์žฅ์ž๋ฆฌ๋กœ ๊ฐˆ์ˆ˜๋ก ๋†’์ด ๊ฐ์†Œ (๊ฒฝ๊ณ„ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ)
1266
+ const distanceFromCenter = Math.sqrt(x * x + y * y);
1267
+ const maxDistance = GAME_CONSTANTS.MAP_SIZE * 0.4;
1268
+ if (distanceFromCenter > maxDistance) {
1269
+ const fade = 1 - (distanceFromCenter - maxDistance) / (GAME_CONSTANTS.MAP_SIZE * 0.1);
1270
+ height *= Math.max(0, fade);
1271
+ }
1272
+
1273
+ vertices[i + 2] = height;
1274
+ heightMap.push(height);
1275
  }
1276
 
1277
  terrainGeometry.computeVertexNormals();
1278
 
1279
+ // ์ง€ํ˜• ๋จธํ‹ฐ๋ฆฌ์–ผ - ๋†’์ด์— ๋”ฐ๋ฅธ ์ƒ‰์ƒ ๋ณ€ํ™”
1280
  const terrainMaterial = new THREE.MeshLambertMaterial({
1281
+ vertexColors: true,
1282
+ flatShading: false
1283
  });
1284
 
1285
+ // ๋†’์ด์— ๋”ฐ๋ฅธ ์ƒ‰์ƒ ์„ค์ •
1286
+ const colors = [];
1287
+ for (let i = 0; i < heightMap.length; i++) {
1288
+ const height = heightMap[i];
1289
+ let color;
1290
+
1291
+ if (height < 50) {
1292
+ // ๋‚ฎ์€ ์ง€์—ญ - ์ง„ํ•œ ์ดˆ๋ก
1293
+ color = new THREE.Color(0x2d4a2b);
1294
+ } else if (height < 150) {
1295
+ // ์ค‘๊ฐ„ ์ง€์—ญ - ์ผ๋ฐ˜ ์ดˆ๋ก
1296
+ color = new THREE.Color(0x3a5f3a);
1297
+ } else if (height < 250) {
1298
+ // ๋†’์€ ์ง€์—ญ - ๋ฐ์€ ์ดˆ๋ก/๊ฐˆ์ƒ‰
1299
+ color = new THREE.Color(0x4a6741);
1300
+ } else {
1301
+ // ๋งค์šฐ ๋†’์€ ์ง€์—ญ - ๊ฐˆ์ƒ‰/ํšŒ์ƒ‰
1302
+ color = new THREE.Color(0x5a5a4a);
1303
+ }
1304
+
1305
+ // ์•ฝ๊ฐ„์˜ ๋ณ€ํ™” ์ถ”๊ฐ€
1306
+ const variation = 0.1;
1307
+ color.r += (Math.random() - 0.5) * variation;
1308
+ color.g += (Math.random() - 0.5) * variation;
1309
+ color.b += (Math.random() - 0.5) * variation;
1310
+
1311
+ colors.push(color.r, color.g, color.b);
1312
+ }
1313
+
1314
+ terrainGeometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
1315
+
1316
  const terrain = new THREE.Mesh(terrainGeometry, terrainMaterial);
1317
  terrain.rotation.x = -Math.PI / 2;
1318
  terrain.receiveShadow = true;
1319
+
1320
+ // ์ง€ํ˜• ์ •๋ณด ์ €์žฅ (์ดˆ๋ชฉ ๋ฐฐ์น˜์šฉ)
1321
+ this.terrain = terrain;
1322
+ this.terrainGeometry = terrainGeometry;
1323
+ this.heightMap = heightMap;
1324
+
1325
  this.scene.add(terrain);
1326
  }
1327
 
1328
+ addVegetation() {
1329
+ if (!this.terrain || !this.heightMap) return;
1330
+
1331
+ // ๋‚˜๋ฌด ์ข…๋ฅ˜๋ณ„ ์„ค์ •
1332
+ const treeTypes = [
1333
+ {
1334
+ name: 'pine',
1335
+ count: 500,
1336
+ minHeight: 50,
1337
+ maxHeight: 200,
1338
+ trunkColor: 0x4a3c28,
1339
+ leavesColor: 0x1a4d1a,
1340
+ trunkHeight: 20,
1341
+ trunkRadius: 1.5,
1342
+ leavesRadius: 8,
1343
+ leavesHeight: 25
1344
+ },
1345
+ {
1346
+ name: 'oak',
1347
+ count: 300,
1348
+ minHeight: 0,
1349
+ maxHeight: 150,
1350
+ trunkColor: 0x5c4033,
1351
+ leavesColor: 0x2d5016,
1352
+ trunkHeight: 15,
1353
+ trunkRadius: 2,
1354
+ leavesRadius: 12,
1355
+ leavesHeight: 20
1356
+ },
1357
+ {
1358
+ name: 'bush',
1359
+ count: 800,
1360
+ minHeight: 0,
1361
+ maxHeight: 100,
1362
+ trunkColor: 0x3a2f1a,
1363
+ leavesColor: 0x3a5f3a,
1364
+ trunkHeight: 3,
1365
+ trunkRadius: 0.5,
1366
+ leavesRadius: 5,
1367
+ leavesHeight: 6
1368
+ }
1369
+ ];
1370
+
1371
+ // ๊ฐ ๋‚˜๋ฌด ํƒ€์ž…๋ณ„๋กœ ๋ฐฐ์น˜
1372
+ treeTypes.forEach(treeType => {
1373
+ for (let i = 0; i < treeType.count; i++) {
1374
+ // ๋žœ๋ค ์œ„์น˜ ์„ ํƒ
1375
+ const x = (Math.random() - 0.5) * GAME_CONSTANTS.MAP_SIZE * 0.8;
1376
+ const z = (Math.random() - 0.5) * GAME_CONSTANTS.MAP_SIZE * 0.8;
1377
+
1378
+ // ํ•ด๋‹น ์œ„์น˜์˜ ์ง€ํ˜• ๋†’์ด ๊ณ„์‚ฐ
1379
+ const terrainHeight = this.getTerrainHeightAt(x, z);
1380
+
1381
+ // ๋†’์ด ์ œํ•œ ํ™•์ธ
1382
+ if (terrainHeight < treeType.minHeight || terrainHeight > treeType.maxHeight) {
1383
+ continue;
1384
+ }
1385
+
1386
+ // ๋‚˜๋ฌด ๊ทธ๋ฃน ์ƒ์„ฑ
1387
+ const tree = new THREE.Group();
1388
+
1389
+ // ๋ชธํ†ต
1390
+ const trunkGeometry = new THREE.CylinderGeometry(
1391
+ treeType.trunkRadius * 0.8,
1392
+ treeType.trunkRadius,
1393
+ treeType.trunkHeight,
1394
+ 8
1395
+ );
1396
+ const trunkMaterial = new THREE.MeshLambertMaterial({
1397
+ color: treeType.trunkColor
1398
+ });
1399
+ const trunk = new THREE.Mesh(trunkGeometry, trunkMaterial);
1400
+ trunk.position.y = treeType.trunkHeight / 2;
1401
+ trunk.castShadow = true;
1402
+ trunk.receiveShadow = true;
1403
+ tree.add(trunk);
1404
+
1405
+ // ์žŽ (๊ตฌํ˜• ๋˜๋Š” ์›๋ฟ”ํ˜•)
1406
+ let leavesGeometry;
1407
+ if (treeType.name === 'pine') {
1408
+ // ์†Œ๋‚˜๋ฌด๋Š” ์›๋ฟ”ํ˜•
1409
+ leavesGeometry = new THREE.ConeGeometry(
1410
+ treeType.leavesRadius,
1411
+ treeType.leavesHeight,
1412
+ 8
1413
+ );
1414
+ } else {
1415
+ // ๋‹ค๋ฅธ ๋‚˜๋ฌด๋Š” ๊ตฌํ˜•
1416
+ leavesGeometry = new THREE.SphereGeometry(
1417
+ treeType.leavesRadius,
1418
+ 8,
1419
+ 6
1420
+ );
1421
+ }
1422
+
1423
+ const leavesMaterial = new THREE.MeshLambertMaterial({
1424
+ color: treeType.leavesColor
1425
+ });
1426
+ const leaves = new THREE.Mesh(leavesGeometry, leavesMaterial);
1427
+ leaves.position.y = treeType.trunkHeight + treeType.leavesHeight / 2;
1428
+ leaves.castShadow = true;
1429
+ leaves.receiveShadow = true;
1430
+ tree.add(leaves);
1431
+
1432
+ // ๋‚˜๋ฌด ์œ„์น˜ ๋ฐ ํฌ๊ธฐ ์„ค์ •
1433
+ tree.position.set(x, terrainHeight, z);
1434
+
1435
+ // ํฌ๊ธฐ ๋ณ€ํ™”
1436
+ const scale = 0.7 + Math.random() * 0.6;
1437
+ tree.scale.set(scale, scale, scale);
1438
+
1439
+ // ๋žœ๋ค ํšŒ์ „
1440
+ tree.rotation.y = Math.random() * Math.PI * 2;
1441
+
1442
+ this.scene.add(tree);
1443
+ }
1444
+ });
1445
+
1446
+ // ๋ฐ”์œ„ ์ถ”๊ฐ€
1447
+ this.addRocks();
1448
+
1449
+ // ํ’€ ์ถ”๊ฐ€
1450
+ this.addGrass();
1451
+ }
1452
+
1453
+ addRocks() {
1454
+ const rockCount = 200;
1455
+
1456
+ for (let i = 0; i < rockCount; i++) {
1457
+ const x = (Math.random() - 0.5) * GAME_CONSTANTS.MAP_SIZE * 0.9;
1458
+ const z = (Math.random() - 0.5) * GAME_CONSTANTS.MAP_SIZE * 0.9;
1459
+ const terrainHeight = this.getTerrainHeightAt(x, z);
1460
+
1461
+ // ๋ฐ”์œ„ ์ƒ์„ฑ
1462
+ const rockSize = 3 + Math.random() * 10;
1463
+ const rockGeometry = new THREE.DodecahedronGeometry(rockSize, 0);
1464
+
1465
+ // ๋ฐ”์œ„ ํ˜•ํƒœ ๋ณ€ํ˜•
1466
+ const vertices = rockGeometry.attributes.position.array;
1467
+ for (let j = 0; j < vertices.length; j += 3) {
1468
+ vertices[j] += (Math.random() - 0.5) * rockSize * 0.3;
1469
+ vertices[j + 1] += (Math.random() - 0.5) * rockSize * 0.3;
1470
+ vertices[j + 2] += (Math.random() - 0.5) * rockSize * 0.3;
1471
+ }
1472
+ rockGeometry.computeVertexNormals();
1473
+
1474
+ const rockMaterial = new THREE.MeshLambertMaterial({
1475
+ color: new THREE.Color(0.4 + Math.random() * 0.2, 0.4 + Math.random() * 0.2, 0.4 + Math.random() * 0.2),
1476
+ flatShading: true
1477
+ });
1478
+
1479
+ const rock = new THREE.Mesh(rockGeometry, rockMaterial);
1480
+ rock.position.set(x, terrainHeight + rockSize * 0.3, z);
1481
+ rock.rotation.set(
1482
+ Math.random() * Math.PI,
1483
+ Math.random() * Math.PI,
1484
+ Math.random() * Math.PI
1485
+ );
1486
+ rock.castShadow = true;
1487
+ rock.receiveShadow = true;
1488
+
1489
+ this.scene.add(rock);
1490
+ }
1491
+ }
1492
+
1493
+ addGrass() {
1494
+ // ํ’€ ํŒจ์น˜ ์ƒ์„ฑ
1495
+ const grassPatchCount = 100;
1496
+
1497
+ for (let i = 0; i < grassPatchCount; i++) {
1498
+ const centerX = (Math.random() - 0.5) * GAME_CONSTANTS.MAP_SIZE * 0.8;
1499
+ const centerZ = (Math.random() - 0.5) * GAME_CONSTANTS.MAP_SIZE * 0.8;
1500
+ const terrainHeight = this.getTerrainHeightAt(centerX, centerZ);
1501
+
1502
+ // ๋‚ฎ์€ ์ง€์—ญ์—๋งŒ ํ’€ ๋ฐฐ์น˜
1503
+ if (terrainHeight > 100) continue;
1504
+
1505
+ const grassGroup = new THREE.Group();
1506
+ const grassCountInPatch = 20 + Math.floor(Math.random() * 30);
1507
+
1508
+ for (let j = 0; j < grassCountInPatch; j++) {
1509
+ const offsetX = (Math.random() - 0.5) * 50;
1510
+ const offsetZ = (Math.random() - 0.5) * 50;
1511
+
1512
+ // ๊ฐ„๋‹จํ•œ ํ’€ ๋ธ”๋ ˆ์ด๋“œ
1513
+ const grassHeight = 2 + Math.random() * 3;
1514
+ const grassGeometry = new THREE.PlaneGeometry(0.5, grassHeight);
1515
+ grassGeometry.translate(0, grassHeight / 2, 0);
1516
+
1517
+ const grassMaterial = new THREE.MeshLambertMaterial({
1518
+ color: new THREE.Color(0.1 + Math.random() * 0.1, 0.4 + Math.random() * 0.2, 0.1),
1519
+ side: THREE.DoubleSide
1520
+ });
1521
+
1522
+ const grass = new THREE.Mesh(grassGeometry, grassMaterial);
1523
+ grass.position.set(offsetX, 0, offsetZ);
1524
+ grass.rotation.y = Math.random() * Math.PI;
1525
+
1526
+ grassGroup.add(grass);
1527
+ }
1528
+
1529
+ grassGroup.position.set(centerX, terrainHeight, centerZ);
1530
+ this.scene.add(grassGroup);
1531
+ }
1532
+ }
1533
+
1534
+ getTerrainHeightAt(x, z) {
1535
+ if (!this.terrainGeometry) return 0;
1536
+
1537
+ // ์ง€ํ˜• ์ขŒํ‘œ๋ฅผ ์ •๊ทœํ™”
1538
+ const normalizedX = (x + GAME_CONSTANTS.MAP_SIZE / 2) / GAME_CONSTANTS.MAP_SIZE;
1539
+ const normalizedZ = (z + GAME_CONSTANTS.MAP_SIZE / 2) / GAME_CONSTANTS.MAP_SIZE;
1540
+
1541
+ // ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ๋ฒ„ํ…์Šค ์ธ๋ฑ์Šค ์ฐพ๊ธฐ
1542
+ const segments = Math.sqrt(this.terrainGeometry.attributes.position.count);
1543
+ const gridX = Math.floor(normalizedX * (segments - 1));
1544
+ const gridZ = Math.floor(normalizedZ * (segments - 1));
1545
+
1546
+ if (gridX < 0 || gridX >= segments - 1 || gridZ < 0 || gridZ >= segments - 1) {
1547
+ return 0;
1548
+ }
1549
+
1550
+ const index = gridZ * segments + gridX;
1551
+ return this.heightMap[index] || 0;
1552
+ }
1553
+
1554
  addAtmosphericEffects() {
1555
  // ๊ฐœ์„ ๋œ ์‚ฐ ์ง€ํ˜• ์‹œ์Šคํ…œ
1556
  this.createMountainRanges();
 
 
 
1557
  }
1558
 
1559
  createMountainRanges() {
 
1671
  // ์•ฝ๊ฐ„์˜ ๋žœ๋ค ํšŒ์ „
1672
  mountainGroup.rotation.y = Math.random() * Math.PI;
1673
 
1674
+ // ๊ฑฐ๋ฆฌ๋ณ„ ์ƒ‰์ƒ: ๋ฉ€๋ฆฌ ์žˆ๋Š” ์‚ฐ์ผ์ˆ˜๋ก ๋” ํ‘ธ๋ฅธ์ƒ‰์œผ๋กœ ๋ณด์ด๋„๋ก ์„ค์ •
1675
  mountainGroup.layers.set(0);
1676
 
1677
  this.scene.add(mountainGroup);
 
1735
  this.scene.add(silhouette);
1736
  }
1737
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1738
 
1739
  addClouds() {
1740
  // ๋‹ค์ธต ๊ตฌ๋ฆ„ ์‹œ์Šคํ…œ