cutechicken commited on
Commit
be285e6
·
verified ·
1 Parent(s): 94974c6

Update game.js

Browse files
Files changed (1) hide show
  1. game.js +275 -23
game.js CHANGED
@@ -1195,17 +1195,19 @@ class Game {
1195
  }
1196
 
1197
  setupScene() {
1198
- this.scene.background = new THREE.Color(0x87CEEB);
1199
- this.scene.fog = new THREE.Fog(0x87CEEB, 1000, 30000);
 
1200
 
1201
- const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
 
1202
  this.scene.add(ambientLight);
1203
 
1204
- const directionalLight = new THREE.DirectionalLight(0xffffff, 1.0);
1205
  directionalLight.position.set(8000, 10000, 8000);
1206
  directionalLight.castShadow = true;
1207
- directionalLight.shadow.mapSize.width = 2048;
1208
- directionalLight.shadow.mapSize.height = 2048;
1209
  directionalLight.shadow.camera.near = 0.5;
1210
  directionalLight.shadow.camera.far = 20000;
1211
  directionalLight.shadow.camera.left = -10000;
@@ -1214,41 +1216,274 @@ class Game {
1214
  directionalLight.shadow.camera.bottom = -10000;
1215
  this.scene.add(directionalLight);
1216
 
 
 
 
 
 
 
1217
  const groundGeometry = new THREE.PlaneGeometry(GAME_CONSTANTS.MAP_SIZE, GAME_CONSTANTS.MAP_SIZE);
1218
  const groundMaterial = new THREE.MeshLambertMaterial({
1219
- color: 0x8FBC8F,
1220
  transparent: true,
1221
- opacity: 0.8
1222
  });
1223
  const ground = new THREE.Mesh(groundGeometry, groundMaterial);
1224
  ground.rotation.x = -Math.PI / 2;
1225
  ground.receiveShadow = true;
1226
  this.scene.add(ground);
1227
 
 
 
 
 
1228
  this.addClouds();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1229
  }
1230
 
1231
  addClouds() {
1232
- const cloudGeometry = new THREE.SphereGeometry(100, 8, 6);
1233
- const cloudMaterial = new THREE.MeshLambertMaterial({
1234
- color: 0xffffff,
1235
- transparent: true,
1236
- opacity: 0.5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1237
  });
1238
 
1239
- for (let i = 0; i < 100; i++) {
1240
- const cloud = new THREE.Mesh(cloudGeometry, cloudMaterial);
1241
- cloud.position.set(
 
 
 
 
 
 
 
 
1242
  (Math.random() - 0.5) * GAME_CONSTANTS.MAP_SIZE,
1243
- Math.random() * 4000 + 1000,
1244
  (Math.random() - 0.5) * GAME_CONSTANTS.MAP_SIZE
1245
  );
1246
- cloud.scale.set(
1247
- Math.random() * 3 + 1,
1248
- Math.random() * 2 + 0.5,
1249
- Math.random() * 3 + 1
1250
- );
1251
- this.scene.add(cloud);
1252
  }
1253
  }
1254
 
@@ -2042,6 +2277,23 @@ class Game {
2042
  }
2043
  }
2044
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2045
  const targetCameraPos = this.fighter.getCameraPosition();
2046
  const targetCameraTarget = this.fighter.getCameraTarget();
2047
 
 
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;
 
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();
1241
+
1242
+ // 대기 효과 추가
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
+ const fieldTileSize = 1000; // 각 field 타일의 크기
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
+
1307
+ // 모델의 실제 크기 계산
1308
+ const box = new THREE.Box3().setFromObject(fieldModel);
1309
+ const size = box.getSize(new THREE.Vector3());
1310
+
1311
+ // 맵 전체를 채우기 위해 타일 배치
1312
+ for (let x = 0; x < tilesPerSide; x++) {
1313
+ for (let z = 0; z < tilesPerSide; z++) {
1314
+ const fieldClone = fieldModel.clone();
1315
+
1316
+ // 위치 설정
1317
+ const posX = (x - tilesPerSide / 2) * fieldTileSize;
1318
+ const posZ = (z - tilesPerSide / 2) * fieldTileSize;
1319
+ fieldClone.position.set(posX, 0, posZ);
1320
+
1321
+ // 크기 조정 (필요한 경우)
1322
+ const scale = fieldTileSize / Math.max(size.x, size.z);
1323
+ fieldClone.scale.set(scale, scale, scale);
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
+ this.scene.add(fieldClone);
1337
+ }
1338
+ }
1339
+
1340
+ console.log(`Field terrain loaded: ${tilesPerSide}x${tilesPerSide} tiles`);
1341
+
1342
+ } catch (error) {
1343
+ console.error('Field terrain loading failed:', error);
1344
+ // 폴백: 프로시저럴 지형 생성
1345
+ this.createProceduralTerrain();
1346
+ }
1347
+ }
1348
+
1349
+ createProceduralTerrain() {
1350
+ // field.glb 로딩 실패 시 프로시저럴 지형 생성
1351
+ const segments = 50;
1352
+ const terrainGeometry = new THREE.PlaneGeometry(
1353
+ GAME_CONSTANTS.MAP_SIZE,
1354
+ GAME_CONSTANTS.MAP_SIZE,
1355
+ segments,
1356
+ segments
1357
+ );
1358
+
1359
+ // 버텍스에 노이즈 추가하여 언덕 효과
1360
+ const vertices = terrainGeometry.attributes.position.array;
1361
+ for (let i = 0; i < vertices.length; i += 3) {
1362
+ const x = vertices[i];
1363
+ const y = vertices[i + 1];
1364
+ const distance = Math.sqrt(x * x + y * y);
1365
+ vertices[i + 2] = Math.sin(distance * 0.0003) * 50 + Math.random() * 20;
1366
+ }
1367
+
1368
+ terrainGeometry.computeVertexNormals();
1369
+
1370
+ const terrainMaterial = new THREE.MeshLambertMaterial({
1371
+ color: 0x3a5f3a,
1372
+ flatShading: true
1373
+ });
1374
+
1375
+ const terrain = new THREE.Mesh(terrainGeometry, terrainMaterial);
1376
+ terrain.rotation.x = -Math.PI / 2;
1377
+ terrain.receiveShadow = true;
1378
+ this.scene.add(terrain);
1379
+ }
1380
+
1381
+ addAtmosphericEffects() {
1382
+ // 먼 산 실루엣 추가
1383
+ const mountainCount = 20;
1384
+ for (let i = 0; i < mountainCount; i++) {
1385
+ const angle = (i / mountainCount) * Math.PI * 2;
1386
+ const distance = GAME_CONSTANTS.MAP_SIZE * 0.7;
1387
+
1388
+ const mountainGeometry = new THREE.ConeGeometry(2000 + Math.random() * 1000, 3000 + Math.random() * 2000, 8);
1389
+ const mountainMaterial = new THREE.MeshLambertMaterial({
1390
+ color: 0x4a5d6a,
1391
+ fog: true
1392
+ });
1393
+
1394
+ const mountain = new THREE.Mesh(mountainGeometry, mountainMaterial);
1395
+ mountain.position.set(
1396
+ Math.cos(angle) * distance,
1397
+ 1500 + Math.random() * 500,
1398
+ Math.sin(angle) * distance
1399
+ );
1400
+ mountain.rotation.y = Math.random() * Math.PI;
1401
+
1402
+ this.scene.add(mountain);
1403
+ }
1404
  }
1405
 
1406
  addClouds() {
1407
+ // 다층 구름 시스템
1408
+ const cloudLayers = [
1409
+ { count: 50, minHeight: 800, maxHeight: 1500, opacity: 0.7, scale: 1.5 }, // 낮은 구름
1410
+ { count: 80, minHeight: 2000, maxHeight: 4000, opacity: 0.5, scale: 2.0 }, // 중간 구름
1411
+ { count: 40, minHeight: 5000, maxHeight: 8000, opacity: 0.3, scale: 3.0 } // 높은 구름
1412
+ ];
1413
+
1414
+ cloudLayers.forEach(layer => {
1415
+ for (let i = 0; i < layer.count; i++) {
1416
+ // 구름 그룹 생성 (여러 구체로 구성)
1417
+ const cloudGroup = new THREE.Group();
1418
+
1419
+ // 구름을 구성하는 구체 개수
1420
+ const sphereCount = 5 + Math.floor(Math.random() * 8);
1421
+
1422
+ for (let j = 0; j < sphereCount; j++) {
1423
+ const radius = 50 + Math.random() * 100;
1424
+ const cloudGeometry = new THREE.SphereGeometry(radius, 8, 6);
1425
+ const cloudMaterial = new THREE.MeshLambertMaterial({
1426
+ color: 0xffffff,
1427
+ transparent: true,
1428
+ opacity: layer.opacity * (0.7 + Math.random() * 0.3)
1429
+ });
1430
+
1431
+ const cloudPart = new THREE.Mesh(cloudGeometry, cloudMaterial);
1432
+
1433
+ // 구름 내 구체들의 위치를 랜덤하게 배치
1434
+ cloudPart.position.set(
1435
+ (Math.random() - 0.5) * 200,
1436
+ (Math.random() - 0.5) * 50,
1437
+ (Math.random() - 0.5) * 200
1438
+ );
1439
+
1440
+ cloudGroup.add(cloudPart);
1441
+ }
1442
+
1443
+ // 구름 그룹 위치 설정
1444
+ cloudGroup.position.set(
1445
+ (Math.random() - 0.5) * GAME_CONSTANTS.MAP_SIZE,
1446
+ layer.minHeight + Math.random() * (layer.maxHeight - layer.minHeight),
1447
+ (Math.random() - 0.5) * GAME_CONSTANTS.MAP_SIZE
1448
+ );
1449
+
1450
+ // 구름 크기 변화
1451
+ const scale = layer.scale * (0.8 + Math.random() * 0.4);
1452
+ cloudGroup.scale.set(scale, scale * 0.5, scale);
1453
+
1454
+ // 구름 애니메이션을 위한 속성 추가
1455
+ cloudGroup.userData = {
1456
+ driftSpeed: (Math.random() - 0.5) * 0.1,
1457
+ floatSpeed: Math.random() * 0.5 + 0.5,
1458
+ initialY: cloudGroup.position.y,
1459
+ time: Math.random() * Math.PI * 2
1460
+ };
1461
+
1462
+ this.scene.add(cloudGroup);
1463
+
1464
+ // 구름 배열에 추가 (애니메이션용)
1465
+ if (!this.clouds) this.clouds = [];
1466
+ this.clouds.push(cloudGroup);
1467
+ }
1468
  });
1469
 
1470
+ // 안개 효과를 위한 낮은 구름층 추가
1471
+ for (let i = 0; i < 30; i++) {
1472
+ const fogGeometry = new THREE.BoxGeometry(500, 100, 500);
1473
+ const fogMaterial = new THREE.MeshLambertMaterial({
1474
+ color: 0xffffff,
1475
+ transparent: true,
1476
+ opacity: 0.2
1477
+ });
1478
+
1479
+ const fog = new THREE.Mesh(fogGeometry, fogMaterial);
1480
+ fog.position.set(
1481
  (Math.random() - 0.5) * GAME_CONSTANTS.MAP_SIZE,
1482
+ 50 + Math.random() * 200,
1483
  (Math.random() - 0.5) * GAME_CONSTANTS.MAP_SIZE
1484
  );
1485
+
1486
+ this.scene.add(fog);
 
 
 
 
1487
  }
1488
  }
1489
 
 
2277
  }
2278
  }
2279
 
2280
+ // 구름 애니메이션
2281
+ if (this.clouds) {
2282
+ this.clouds.forEach(cloud => {
2283
+ cloud.userData.time += deltaTime;
2284
+ // 좌우 드리프트
2285
+ cloud.position.x += cloud.userData.driftSpeed;
2286
+ // 상하 부유
2287
+ cloud.position.y = cloud.userData.initialY +
2288
+ Math.sin(cloud.userData.time * cloud.userData.floatSpeed) * 20;
2289
+
2290
+ // 맵 경계 처리
2291
+ const mapLimit = GAME_CONSTANTS.MAP_SIZE / 2;
2292
+ if (cloud.position.x > mapLimit) cloud.position.x = -mapLimit;
2293
+ if (cloud.position.x < -mapLimit) cloud.position.x = mapLimit;
2294
+ });
2295
+ }
2296
+
2297
  const targetCameraPos = this.fighter.getCameraPosition();
2298
  const targetCameraTarget = this.fighter.getCameraTarget();
2299