Spaces:
Running
Running
Update game.js
Browse files
game.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
| 1 |
import * as THREE from 'three';
|
|
|
|
| 2 |
import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js';
|
| 3 |
|
| 4 |
// κ²μ μμ
|
|
@@ -117,7 +118,7 @@ async function init() {
|
|
| 117 |
const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
|
| 118 |
dirLight.position.set(100, 100, 50);
|
| 119 |
dirLight.castShadow = true;
|
| 120 |
-
dirLight.shadow.mapSize.width = 1024;
|
| 121 |
dirLight.shadow.mapSize.height = 1024;
|
| 122 |
scene.add(dirLight);
|
| 123 |
|
|
@@ -127,6 +128,9 @@ async function init() {
|
|
| 127 |
// μ΄λ²€νΈ 리μ€λ
|
| 128 |
setupEventListeners();
|
| 129 |
|
|
|
|
|
|
|
|
|
|
| 130 |
// κ²μ μμ μ΄κΈ°ν
|
| 131 |
await Promise.all([
|
| 132 |
createTerrain(),
|
|
@@ -134,10 +138,15 @@ async function init() {
|
|
| 134 |
]);
|
| 135 |
|
| 136 |
document.getElementById('loading').style.display = 'none';
|
| 137 |
-
|
| 138 |
} catch (error) {
|
| 139 |
console.error('Initialization error:', error);
|
| 140 |
-
document.getElementById('loading').
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 141 |
}
|
| 142 |
}
|
| 143 |
|
|
@@ -148,10 +157,22 @@ function setupEventListeners() {
|
|
| 148 |
window.addEventListener('resize', onWindowResize);
|
| 149 |
}
|
| 150 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 151 |
function createTerrain() {
|
| 152 |
return new Promise((resolve) => {
|
| 153 |
-
|
| 154 |
-
const geometry = new THREE.PlaneGeometry(MAP_SIZE, MAP_SIZE, 100, 100); // ν΄μλ κ°μ
|
| 155 |
const material = new THREE.MeshStandardMaterial({
|
| 156 |
color: 0xD2B48C,
|
| 157 |
roughness: 0.8,
|
|
@@ -171,7 +192,6 @@ function createTerrain() {
|
|
| 171 |
terrain.receiveShadow = true;
|
| 172 |
scene.add(terrain);
|
| 173 |
|
| 174 |
-
// μ΅μ νλ μ₯μ λ¬Ό μΆκ°
|
| 175 |
addObstacles();
|
| 176 |
resolve();
|
| 177 |
});
|
|
@@ -184,7 +204,6 @@ function addObstacles() {
|
|
| 184 |
roughness: 0.9
|
| 185 |
});
|
| 186 |
|
| 187 |
-
// μ₯μ λ¬Ό μ κ°μ
|
| 188 |
for (let i = 0; i < OBSTACLE_COUNT; i++) {
|
| 189 |
const rock = new THREE.Mesh(rockGeometry, rockMaterial);
|
| 190 |
rock.position.set(
|
|
@@ -203,7 +222,9 @@ function addObstacles() {
|
|
| 203 |
}
|
| 204 |
}
|
| 205 |
|
| 206 |
-
function createEnemies() {
|
|
|
|
|
|
|
| 207 |
const enemyCount = Math.min(3 + currentStage, ENEMY_COUNT_MAX);
|
| 208 |
|
| 209 |
for (let i = 0; i < enemyCount; i++) {
|
|
@@ -215,32 +236,59 @@ function createEnemies() {
|
|
| 215 |
Math.sin(angle) * radius
|
| 216 |
);
|
| 217 |
|
| 218 |
-
|
| 219 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 220 |
}
|
| 221 |
}
|
| 222 |
|
| 223 |
-
function
|
| 224 |
-
// κ°λ¨ν μ λͺ¨λΈ μμ±
|
| 225 |
const geometry = new THREE.BoxGeometry(5, 10, 5);
|
| 226 |
const material = new THREE.MeshPhongMaterial({
|
| 227 |
color: 0xff0000,
|
| 228 |
-
|
| 229 |
-
|
| 230 |
});
|
| 231 |
|
| 232 |
const model = new THREE.Mesh(geometry, material);
|
| 233 |
model.position.copy(position);
|
| 234 |
model.castShadow = true;
|
| 235 |
model.receiveShadow = true;
|
| 236 |
-
|
| 237 |
-
// μ μ£Όλ³μ λΉ μΆκ°
|
| 238 |
-
const light = new THREE.PointLight(0xff0000, 1, 10);
|
| 239 |
-
light.position.set(0, 5, 0);
|
| 240 |
-
model.add(light);
|
| 241 |
-
|
| 242 |
-
scene.add(model);
|
| 243 |
-
|
| 244 |
return {
|
| 245 |
model: model,
|
| 246 |
health: 100,
|
|
@@ -277,7 +325,6 @@ function createExplosion(position) {
|
|
| 277 |
explosionLight.position.copy(position);
|
| 278 |
scene.add(explosionLight);
|
| 279 |
|
| 280 |
-
// μ΅μ νλ μ λλ©μ΄μ
|
| 281 |
let opacity = 1;
|
| 282 |
const animate = () => {
|
| 283 |
opacity -= 0.05;
|
|
@@ -351,7 +398,7 @@ function shoot() {
|
|
| 351 |
|
| 352 |
gunSound.createGunshot();
|
| 353 |
|
| 354 |
-
//
|
| 355 |
const muzzleFlash = new THREE.PointLight(0xffff00, 3, 10);
|
| 356 |
muzzleFlash.position.copy(camera.position);
|
| 357 |
scene.add(muzzleFlash);
|
|
@@ -359,7 +406,6 @@ function shoot() {
|
|
| 359 |
}
|
| 360 |
|
| 361 |
function createBullet() {
|
| 362 |
-
// μ΅μ νλ μ΄μ μμ±
|
| 363 |
const bullet = new THREE.Mesh(
|
| 364 |
new THREE.SphereGeometry(0.5),
|
| 365 |
new THREE.MeshBasicMaterial({
|
|
@@ -457,7 +503,6 @@ function updateEnemyBullets() {
|
|
| 457 |
|
| 458 |
enemyBullets[i].position.add(enemyBullets[i].velocity);
|
| 459 |
|
| 460 |
-
// νλ μ΄μ΄μμ μΆ©λ κ²μ¬
|
| 461 |
if (enemyBullets[i].position.distanceTo(camera.position) < 3) {
|
| 462 |
playerHealth -= 10;
|
| 463 |
updateHealthBar();
|
|
@@ -471,7 +516,6 @@ function updateEnemyBullets() {
|
|
| 471 |
continue;
|
| 472 |
}
|
| 473 |
|
| 474 |
-
// λ²μ λ²μ΄λ μ΄μ μ κ±°
|
| 475 |
if (enemyBullets[i].position.distanceTo(camera.position) > 1000) {
|
| 476 |
scene.remove(enemyBullets[i]);
|
| 477 |
enemyBullets.splice(i, 1);
|
|
@@ -510,6 +554,12 @@ function updateEnemies() {
|
|
| 510 |
|
| 511 |
enemyBullets.push(createEnemyBullet(enemy));
|
| 512 |
enemy.lastAttackTime = currentTime;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 513 |
}
|
| 514 |
});
|
| 515 |
}
|
|
@@ -588,7 +638,6 @@ function checkGameStatus() {
|
|
| 588 |
}
|
| 589 |
|
| 590 |
function cleanupResources() {
|
| 591 |
-
// 리μμ€ μ 리
|
| 592 |
bullets.forEach(bullet => scene.remove(bullet));
|
| 593 |
bullets = [];
|
| 594 |
|
|
@@ -634,32 +683,6 @@ function gameLoop(timestamp) {
|
|
| 634 |
renderer.render(scene, camera);
|
| 635 |
}
|
| 636 |
|
| 637 |
-
// λλ²κΉ
μ μν μ μ μ κ·Ό
|
| 638 |
-
window.debugGame = {
|
| 639 |
-
scene,
|
| 640 |
-
camera,
|
| 641 |
-
enemies,
|
| 642 |
-
gunSound,
|
| 643 |
-
reloadEnemies: createEnemies
|
| 644 |
-
};
|
| 645 |
-
|
| 646 |
-
// κ²μ μμ
|
| 647 |
-
window.addEventListener('load', async () => {
|
| 648 |
-
try {
|
| 649 |
-
await init();
|
| 650 |
-
console.log('Game started');
|
| 651 |
-
console.log('Active enemies:', enemies.length);
|
| 652 |
-
gameLoop(performance.now());
|
| 653 |
-
} catch (error) {
|
| 654 |
-
console.error('Game initialization error:', error);
|
| 655 |
-
document.getElementById('loading').innerHTML = `
|
| 656 |
-
<div class="loading-text" style="color: #ff0000;">
|
| 657 |
-
Error loading game. Please refresh.
|
| 658 |
-
</div>
|
| 659 |
-
`;
|
| 660 |
-
}
|
| 661 |
-
});
|
| 662 |
-
|
| 663 |
// μ±λ₯ λͺ¨λν°λ§
|
| 664 |
let lastFpsUpdate = 0;
|
| 665 |
let frameCount = 0;
|
|
@@ -678,4 +701,29 @@ function updateFPS(timestamp) {
|
|
| 678 |
requestAnimationFrame(updateFPS);
|
| 679 |
}
|
| 680 |
|
| 681 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import * as THREE from 'three';
|
| 2 |
+
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
|
| 3 |
import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js';
|
| 4 |
|
| 5 |
// κ²μ μμ
|
|
|
|
| 118 |
const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
|
| 119 |
dirLight.position.set(100, 100, 50);
|
| 120 |
dirLight.castShadow = true;
|
| 121 |
+
dirLight.shadow.mapSize.width = 1024;
|
| 122 |
dirLight.shadow.mapSize.height = 1024;
|
| 123 |
scene.add(dirLight);
|
| 124 |
|
|
|
|
| 128 |
// μ΄λ²€νΈ 리μ€λ
|
| 129 |
setupEventListeners();
|
| 130 |
|
| 131 |
+
// λͺ¨λΈ ν
μ€νΈ λ¨Όμ μ€ν
|
| 132 |
+
await testModelLoading();
|
| 133 |
+
|
| 134 |
// κ²μ μμ μ΄κΈ°ν
|
| 135 |
await Promise.all([
|
| 136 |
createTerrain(),
|
|
|
|
| 138 |
]);
|
| 139 |
|
| 140 |
document.getElementById('loading').style.display = 'none';
|
| 141 |
+
console.log('Game initialized successfully');
|
| 142 |
} catch (error) {
|
| 143 |
console.error('Initialization error:', error);
|
| 144 |
+
document.getElementById('loading').innerHTML = `
|
| 145 |
+
<div class="loading-text" style="color: #ff0000;">
|
| 146 |
+
Error loading models. Please check console and file paths.
|
| 147 |
+
</div>
|
| 148 |
+
`;
|
| 149 |
+
throw error;
|
| 150 |
}
|
| 151 |
}
|
| 152 |
|
|
|
|
| 157 |
window.addEventListener('resize', onWindowResize);
|
| 158 |
}
|
| 159 |
|
| 160 |
+
async function testModelLoading() {
|
| 161 |
+
const loader = new GLTFLoader();
|
| 162 |
+
try {
|
| 163 |
+
const modelPath = 'models/enemy1.glb';
|
| 164 |
+
console.log('Testing model loading:', modelPath);
|
| 165 |
+
const gltf = await loader.loadAsync(modelPath);
|
| 166 |
+
console.log('Test model loaded successfully:', gltf);
|
| 167 |
+
} catch (error) {
|
| 168 |
+
console.error('Test model loading failed:', error);
|
| 169 |
+
throw error;
|
| 170 |
+
}
|
| 171 |
+
}
|
| 172 |
+
|
| 173 |
function createTerrain() {
|
| 174 |
return new Promise((resolve) => {
|
| 175 |
+
const geometry = new THREE.PlaneGeometry(MAP_SIZE, MAP_SIZE, 100, 100);
|
|
|
|
| 176 |
const material = new THREE.MeshStandardMaterial({
|
| 177 |
color: 0xD2B48C,
|
| 178 |
roughness: 0.8,
|
|
|
|
| 192 |
terrain.receiveShadow = true;
|
| 193 |
scene.add(terrain);
|
| 194 |
|
|
|
|
| 195 |
addObstacles();
|
| 196 |
resolve();
|
| 197 |
});
|
|
|
|
| 204 |
roughness: 0.9
|
| 205 |
});
|
| 206 |
|
|
|
|
| 207 |
for (let i = 0; i < OBSTACLE_COUNT; i++) {
|
| 208 |
const rock = new THREE.Mesh(rockGeometry, rockMaterial);
|
| 209 |
rock.position.set(
|
|
|
|
| 222 |
}
|
| 223 |
}
|
| 224 |
|
| 225 |
+
async function createEnemies() {
|
| 226 |
+
console.log('Creating enemies...');
|
| 227 |
+
const loader = new GLTFLoader();
|
| 228 |
const enemyCount = Math.min(3 + currentStage, ENEMY_COUNT_MAX);
|
| 229 |
|
| 230 |
for (let i = 0; i < enemyCount; i++) {
|
|
|
|
| 236 |
Math.sin(angle) * radius
|
| 237 |
);
|
| 238 |
|
| 239 |
+
// μμ μ μμ±
|
| 240 |
+
const tempEnemy = createTemporaryEnemy(position);
|
| 241 |
+
scene.add(tempEnemy.model);
|
| 242 |
+
enemies.push(tempEnemy);
|
| 243 |
+
|
| 244 |
+
// GLB λͺ¨λΈ λ‘λ
|
| 245 |
+
try {
|
| 246 |
+
const modelIndex = i % 4 + 1;
|
| 247 |
+
const modelPath = `models/enemy${modelIndex}.glb`;
|
| 248 |
+
console.log(`Loading model: ${modelPath}`);
|
| 249 |
+
|
| 250 |
+
const gltf = await loader.loadAsync(modelPath);
|
| 251 |
+
const model = gltf.scene;
|
| 252 |
+
|
| 253 |
+
// λͺ¨λΈ μ€μ
|
| 254 |
+
model.scale.set(ENEMY_SCALE, ENEMY_SCALE, ENEMY_SCALE);
|
| 255 |
+
model.position.copy(position);
|
| 256 |
+
|
| 257 |
+
// λͺ¨λΈ μ¬μ§ λ° κ·Έλ¦Όμ μ€μ
|
| 258 |
+
model.traverse((node) => {
|
| 259 |
+
if (node.isMesh) {
|
| 260 |
+
node.castShadow = true;
|
| 261 |
+
node.receiveShadow = true;
|
| 262 |
+
node.material.metalness = 0.2;
|
| 263 |
+
node.material.roughness = 0.8;
|
| 264 |
+
}
|
| 265 |
+
});
|
| 266 |
+
|
| 267 |
+
// μμ λͺ¨λΈ κ΅μ²΄
|
| 268 |
+
scene.remove(tempEnemy.model);
|
| 269 |
+
scene.add(model);
|
| 270 |
+
enemies[enemies.indexOf(tempEnemy)].model = model;
|
| 271 |
+
|
| 272 |
+
console.log(`Successfully loaded enemy model ${modelIndex}`);
|
| 273 |
+
} catch (error) {
|
| 274 |
+
console.error(`Error loading enemy model:`, error);
|
| 275 |
+
}
|
| 276 |
}
|
| 277 |
}
|
| 278 |
|
| 279 |
+
function createTemporaryEnemy(position) {
|
|
|
|
| 280 |
const geometry = new THREE.BoxGeometry(5, 10, 5);
|
| 281 |
const material = new THREE.MeshPhongMaterial({
|
| 282 |
color: 0xff0000,
|
| 283 |
+
transparent: true,
|
| 284 |
+
opacity: 0.8
|
| 285 |
});
|
| 286 |
|
| 287 |
const model = new THREE.Mesh(geometry, material);
|
| 288 |
model.position.copy(position);
|
| 289 |
model.castShadow = true;
|
| 290 |
model.receiveShadow = true;
|
| 291 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 292 |
return {
|
| 293 |
model: model,
|
| 294 |
health: 100,
|
|
|
|
| 325 |
explosionLight.position.copy(position);
|
| 326 |
scene.add(explosionLight);
|
| 327 |
|
|
|
|
| 328 |
let opacity = 1;
|
| 329 |
const animate = () => {
|
| 330 |
opacity -= 0.05;
|
|
|
|
| 398 |
|
| 399 |
gunSound.createGunshot();
|
| 400 |
|
| 401 |
+
// μ΄κ΅¬ νμΌ ν¨κ³Ό
|
| 402 |
const muzzleFlash = new THREE.PointLight(0xffff00, 3, 10);
|
| 403 |
muzzleFlash.position.copy(camera.position);
|
| 404 |
scene.add(muzzleFlash);
|
|
|
|
| 406 |
}
|
| 407 |
|
| 408 |
function createBullet() {
|
|
|
|
| 409 |
const bullet = new THREE.Mesh(
|
| 410 |
new THREE.SphereGeometry(0.5),
|
| 411 |
new THREE.MeshBasicMaterial({
|
|
|
|
| 503 |
|
| 504 |
enemyBullets[i].position.add(enemyBullets[i].velocity);
|
| 505 |
|
|
|
|
| 506 |
if (enemyBullets[i].position.distanceTo(camera.position) < 3) {
|
| 507 |
playerHealth -= 10;
|
| 508 |
updateHealthBar();
|
|
|
|
| 516 |
continue;
|
| 517 |
}
|
| 518 |
|
|
|
|
| 519 |
if (enemyBullets[i].position.distanceTo(camera.position) > 1000) {
|
| 520 |
scene.remove(enemyBullets[i]);
|
| 521 |
enemyBullets.splice(i, 1);
|
|
|
|
| 554 |
|
| 555 |
enemyBullets.push(createEnemyBullet(enemy));
|
| 556 |
enemy.lastAttackTime = currentTime;
|
| 557 |
+
|
| 558 |
+
// 곡격 μ λ°κ΄ ν¨κ³Ό
|
| 559 |
+
const attackFlash = new THREE.PointLight(0xff0000, 2, 20);
|
| 560 |
+
attackFlash.position.copy(enemy.model.position);
|
| 561 |
+
scene.add(attackFlash);
|
| 562 |
+
setTimeout(() => scene.remove(attackFlash), 100);
|
| 563 |
}
|
| 564 |
});
|
| 565 |
}
|
|
|
|
| 638 |
}
|
| 639 |
|
| 640 |
function cleanupResources() {
|
|
|
|
| 641 |
bullets.forEach(bullet => scene.remove(bullet));
|
| 642 |
bullets = [];
|
| 643 |
|
|
|
|
| 683 |
renderer.render(scene, camera);
|
| 684 |
}
|
| 685 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 686 |
// μ±λ₯ λͺ¨λν°λ§
|
| 687 |
let lastFpsUpdate = 0;
|
| 688 |
let frameCount = 0;
|
|
|
|
| 701 |
requestAnimationFrame(updateFPS);
|
| 702 |
}
|
| 703 |
|
| 704 |
+
// κ²μ μμ
|
| 705 |
+
window.addEventListener('load', async () => {
|
| 706 |
+
try {
|
| 707 |
+
await init();
|
| 708 |
+
console.log('Game started');
|
| 709 |
+
console.log('Active enemies:', enemies.length);
|
| 710 |
+
gameLoop(performance.now());
|
| 711 |
+
updateFPS(performance.now());
|
| 712 |
+
} catch (error) {
|
| 713 |
+
console.error('Game initialization error:', error);
|
| 714 |
+
document.getElementById('loading').innerHTML = `
|
| 715 |
+
<div class="loading-text" style="color: #ff0000;">
|
| 716 |
+
Error loading game. Please check console and file paths.
|
| 717 |
+
</div>
|
| 718 |
+
`;
|
| 719 |
+
}
|
| 720 |
+
});
|
| 721 |
+
|
| 722 |
+
// λλ²κΉ
μ μν μ μ μ κ·Ό
|
| 723 |
+
window.debugGame = {
|
| 724 |
+
scene,
|
| 725 |
+
camera,
|
| 726 |
+
enemies,
|
| 727 |
+
gunSound,
|
| 728 |
+
reloadEnemies: createEnemies
|
| 729 |
+
};
|