Spaces:
Running
Running
Update game.js
Browse files
game.js
CHANGED
|
@@ -1195,17 +1195,19 @@ class Game {
|
|
| 1195 |
}
|
| 1196 |
|
| 1197 |
setupScene() {
|
| 1198 |
-
|
| 1199 |
-
this.
|
|
|
|
| 1200 |
|
| 1201 |
-
|
|
|
|
| 1202 |
this.scene.add(ambientLight);
|
| 1203 |
|
| 1204 |
-
const directionalLight = new THREE.DirectionalLight(
|
| 1205 |
directionalLight.position.set(8000, 10000, 8000);
|
| 1206 |
directionalLight.castShadow = true;
|
| 1207 |
-
directionalLight.shadow.mapSize.width =
|
| 1208 |
-
directionalLight.shadow.mapSize.height =
|
| 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:
|
| 1220 |
transparent: true,
|
| 1221 |
-
opacity: 0.
|
| 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 |
-
|
| 1233 |
-
const
|
| 1234 |
-
|
| 1235 |
-
|
| 1236 |
-
opacity: 0.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1237 |
});
|
| 1238 |
|
| 1239 |
-
|
| 1240 |
-
|
| 1241 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1242 |
(Math.random() - 0.5) * GAME_CONSTANTS.MAP_SIZE,
|
| 1243 |
-
Math.random() *
|
| 1244 |
(Math.random() - 0.5) * GAME_CONSTANTS.MAP_SIZE
|
| 1245 |
);
|
| 1246 |
-
|
| 1247 |
-
|
| 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 |
|