gunship999 commited on
Commit
5326e18
β€’
1 Parent(s): 714950d

Update game.js

Browse files
Files changed (1) hide show
  1. game.js +167 -91
game.js CHANGED
@@ -11,10 +11,10 @@ const ENEMY_SCALE = 3;
11
  const MAX_HEALTH = 1000;
12
  const ENEMY_MOVE_SPEED = 0.1;
13
  const ENEMY_MODELS = [
14
- 'models/enemy1.glb',
15
- 'models/enemy12.glb',
16
- 'models/enemy13.glb',
17
- 'models/enemy14.glb'
18
  ];
19
  const ENEMY_CONFIG = {
20
  ATTACK_RANGE: 100,
@@ -33,44 +33,67 @@ let currentStage = 1;
33
  let isGameOver = false;
34
  let lastTime = performance.now();
35
 
36
- // μ‚¬μš΄λ“œ ν’€ 클래슀
37
- class SoundPool {
38
- constructor(soundUrl, poolSize = 10) {
39
- this.sounds = [];
40
- this.currentIndex = 0;
41
- this.poolSize = poolSize;
42
-
43
- for (let i = 0; i < poolSize; i++) {
44
- const sound = new Audio(soundUrl);
45
- sound.preload = 'auto';
46
- this.sounds.push(sound);
 
 
 
 
 
 
 
47
  }
48
  }
49
 
50
- play() {
51
- const sound = this.sounds[this.currentIndex];
52
- sound.pause();
53
- sound.currentTime = 0;
54
-
55
- const playPromise = sound.play();
56
- if (playPromise !== undefined) {
57
- playPromise.catch(error => {
58
- console.error("Sound play error:", error);
59
- this.sounds[this.currentIndex] = new Audio(sound.src);
60
  });
61
  }
 
 
62
 
63
- this.currentIndex = (this.currentIndex + 1) % this.poolSize;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  }
65
  }
66
 
67
- // μ‚¬μš΄λ“œ μ΄ˆκΈ°ν™”
68
- const sounds = {
69
- bgm: new Audio('Music.wav'),
70
- gunshot: new SoundPool('gun.wav', 20),
71
- explosion: new SoundPool('explosion.wav', 10)
72
- };
73
- sounds.bgm.loop = true;
74
 
75
  // 이동 μƒνƒœ
76
  const moveState = {
@@ -80,9 +103,19 @@ const moveState = {
80
  right: false
81
  };
82
 
 
 
 
 
 
 
 
 
 
 
83
  async function init() {
84
  console.log('Initializing game...');
85
-
86
  // Scene μ΄ˆκΈ°ν™”
87
  scene = new THREE.Scene();
88
  scene.background = new THREE.Color(0x87ceeb);
@@ -122,21 +155,36 @@ async function init() {
122
  document.addEventListener('keyup', onKeyUp);
123
  window.addEventListener('resize', onWindowResize);
124
 
125
- // μ§€ν˜• 생성
126
- await createTerrain();
127
-
128
- // 적 λ‘œλ“œ
129
- await loadEnemies();
 
 
 
 
 
 
 
 
130
 
131
- // λ‘œλ”© ν™”λ©΄ 제거
132
- document.getElementById('loading').style.display = 'none';
133
 
134
- console.log('Scene initialized');
135
- console.log('Camera position:', camera.position);
136
- console.log('Initialization complete');
 
 
 
 
 
 
 
 
 
 
137
  }
138
 
139
-
140
  function createTerrain() {
141
  return new Promise((resolve) => {
142
  const geometry = new THREE.PlaneGeometry(MAP_SIZE, MAP_SIZE, 200, 200);
@@ -189,12 +237,12 @@ function addObstacles() {
189
  }
190
  }
191
 
192
- function loadEnemies() {
193
- console.log('Loading enemies...');
194
  const loader = new GLTFLoader();
195
  const enemyCount = 3 + currentStage;
 
196
 
197
- // μž„μ‹œ 적 생성 (λΉ¨κ°„ λ°•μŠ€)
198
  for (let i = 0; i < enemyCount; i++) {
199
  const angle = (i / enemyCount) * Math.PI * 2;
200
  const radius = 200;
@@ -204,57 +252,72 @@ function loadEnemies() {
204
  Math.sin(angle) * radius
205
  );
206
 
207
- console.log('Creating enemy at position:', position);
208
 
209
  // μž„μ‹œ 적 생성
210
  const tempGeometry = new THREE.BoxGeometry(5, 5, 5);
211
- const tempMaterial = new THREE.MeshPhongMaterial({ color: 0xff0000 });
 
 
 
 
212
  const tempEnemy = new THREE.Mesh(tempGeometry, tempMaterial);
213
  tempEnemy.position.copy(position);
 
 
214
  scene.add(tempEnemy);
215
 
216
- // enemies 배열에 μΆ”κ°€
217
  const enemy = {
218
  model: tempEnemy,
219
  health: 100,
220
  speed: ENEMY_MOVE_SPEED,
221
- lastAttackTime: 0
 
222
  };
223
  enemies.push(enemy);
224
 
225
- // GLB λͺ¨λΈ λ‘œλ“œ μ‹œλ„
226
  const modelPath = ENEMY_MODELS[i % ENEMY_MODELS.length];
227
- console.log('Loading model:', modelPath);
228
-
229
- loader.load(
230
- modelPath,
231
- (gltf) => {
232
- console.log('Successfully loaded:', modelPath);
233
- const model = gltf.scene;
234
- model.scale.set(ENEMY_SCALE, ENEMY_SCALE, ENEMY_SCALE);
235
- model.position.copy(position);
236
-
237
- model.traverse((node) => {
238
- if (node.isMesh) {
239
- node.castShadow = true;
240
- node.receiveShadow = true;
241
- node.material.metalness = 0.2;
242
- node.material.roughness = 0.8;
243
- }
244
- });
245
-
246
- // μž„μ‹œ 적을 μ‹€μ œ λͺ¨λΈλ‘œ ꡐ체
247
- scene.remove(tempEnemy);
248
- scene.add(model);
249
- enemy.model = model;
250
- },
251
- (xhr) => {
252
- console.log(`${modelPath}: ${(xhr.loaded / xhr.total * 100)}% loaded`);
253
- },
254
- (error) => {
255
- console.error('Error loading model:', modelPath, error);
256
- }
257
- );
 
 
 
 
 
 
 
 
 
258
  }
259
  }
260
 
@@ -300,14 +363,15 @@ function createExplosion(position) {
300
  }
301
 
302
  animateExplosion();
303
- sounds.explosion.play();
304
  }
305
 
306
  // 이벀트 ν•Έλ“€λŸ¬
307
  function onClick() {
308
  if (!controls.isLocked) {
309
  controls.lock();
310
- sounds.bgm.play();
 
311
  } else if (ammo > 0) {
312
  shoot();
313
  }
@@ -338,7 +402,6 @@ function onWindowResize() {
338
  renderer.setSize(window.innerWidth, window.innerHeight);
339
  }
340
 
341
- // κ²Œμž„ 메컀닉 ν•¨μˆ˜λ“€
342
  function shoot() {
343
  if (ammo <= 0) return;
344
 
@@ -348,7 +411,7 @@ function shoot() {
348
  const bullet = createBullet();
349
  bullets.push(bullet);
350
 
351
- sounds.gunshot.play();
352
  }
353
 
354
  function createBullet() {
@@ -531,7 +594,10 @@ function updateEnemies() {
531
  const currentTime = Date.now();
532
 
533
  enemies.forEach(enemy => {
534
- if (!enemy || !enemy.model) return;
 
 
 
535
 
536
  // 적의 ν˜„μž¬ μœ„μΉ˜μ—μ„œ ν”Œλ ˆμ΄μ–΄ λ°©ν–₯으둜 ν–₯ν•˜λŠ” 벑터 계산
537
  const direction = new THREE.Vector3();
@@ -579,7 +645,7 @@ function checkGameStatus() {
579
  function gameOver(won) {
580
  isGameOver = true;
581
  controls.unlock();
582
- sounds.bgm.pause();
583
  alert(won ? 'Mission Complete!' : 'Game Over!');
584
  location.reload();
585
  }
@@ -612,5 +678,15 @@ window.addEventListener('load', async () => {
612
  gameLoop();
613
  } catch (error) {
614
  console.error('Game initialization error:', error);
 
615
  }
616
- });
 
 
 
 
 
 
 
 
 
 
11
  const MAX_HEALTH = 1000;
12
  const ENEMY_MOVE_SPEED = 0.1;
13
  const ENEMY_MODELS = [
14
+ '/models/enemy1.glb',
15
+ '/models/enemy12.glb',
16
+ '/models/enemy13.glb',
17
+ '/models/enemy14.glb'
18
  ];
19
  const ENEMY_CONFIG = {
20
  ATTACK_RANGE: 100,
 
33
  let isGameOver = false;
34
  let lastTime = performance.now();
35
 
36
+ // μ‚¬μš΄λ“œ μ‹œμŠ€ν…œ
37
+ class SoundSystem {
38
+ constructor() {
39
+ this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
40
+ this.buffers = new Map();
41
+ this.pools = new Map();
42
+ }
43
+
44
+ async loadSound(name, url) {
45
+ try {
46
+ const response = await fetch(url);
47
+ const arrayBuffer = await response.arrayBuffer();
48
+ const audioBuffer = await this.audioContext.decodeAudioData(arrayBuffer);
49
+ this.buffers.set(name, audioBuffer);
50
+ this.createPool(name, audioBuffer);
51
+ console.log(`Sound loaded: ${name}`);
52
+ } catch (error) {
53
+ console.error(`Error loading sound ${name}:`, error);
54
  }
55
  }
56
 
57
+ createPool(name, buffer, size = 10) {
58
+ const pool = [];
59
+ for (let i = 0; i < size; i++) {
60
+ pool.push({
61
+ source: null,
62
+ gainNode: this.audioContext.createGain(),
63
+ lastPlayed: 0
 
 
 
64
  });
65
  }
66
+ this.pools.set(name, pool);
67
+ }
68
 
69
+ play(name) {
70
+ const pool = this.pools.get(name);
71
+ const buffer = this.buffers.get(name);
72
+ if (!pool || !buffer) {
73
+ console.warn(`Sound not found: ${name}`);
74
+ return;
75
+ }
76
+
77
+ const now = this.audioContext.currentTime;
78
+ const poolItem = pool.find(item => !item.source || item.lastPlayed + buffer.duration <= now);
79
+
80
+ if (poolItem) {
81
+ if (poolItem.source) {
82
+ poolItem.source.disconnect();
83
+ }
84
+
85
+ poolItem.source = this.audioContext.createBufferSource();
86
+ poolItem.source.buffer = buffer;
87
+ poolItem.source.connect(poolItem.gainNode);
88
+ poolItem.gainNode.connect(this.audioContext.destination);
89
+ poolItem.source.start(0);
90
+ poolItem.lastPlayed = now;
91
+ }
92
  }
93
  }
94
 
95
+ // μ‚¬μš΄λ“œ μ‹œμŠ€ν…œ μ΄ˆκΈ°ν™”
96
+ const soundSystem = new SoundSystem();
 
 
 
 
 
97
 
98
  // 이동 μƒνƒœ
99
  const moveState = {
 
103
  right: false
104
  };
105
 
106
+ async function initSounds() {
107
+ try {
108
+ await soundSystem.loadSound('gunshot', 'gun.wav');
109
+ await soundSystem.loadSound('explosion', 'explosion.wav');
110
+ console.log('All sounds loaded successfully');
111
+ } catch (error) {
112
+ console.error('Error initializing sounds:', error);
113
+ }
114
+ }
115
+
116
  async function init() {
117
  console.log('Initializing game...');
118
+
119
  // Scene μ΄ˆκΈ°ν™”
120
  scene = new THREE.Scene();
121
  scene.background = new THREE.Color(0x87ceeb);
 
155
  document.addEventListener('keyup', onKeyUp);
156
  window.addEventListener('resize', onWindowResize);
157
 
158
+ try {
159
+ await initSounds();
160
+ await createTerrain();
161
+ await loadEnemies();
162
+ await testModelLoading();
163
+
164
+ document.getElementById('loading').style.display = 'none';
165
+ console.log('Game initialized successfully');
166
+ console.log('Active enemies:', enemies.length);
167
+ } catch (error) {
168
+ console.error('Initialization error:', error);
169
+ }
170
+ }
171
 
 
 
172
 
173
+ async function testModelLoading() {
174
+ const loader = new GLTFLoader();
175
+ const testPath = ENEMY_MODELS[0];
176
+
177
+ try {
178
+ const gltf = await new Promise((resolve, reject) => {
179
+ loader.load(testPath, resolve, undefined, reject);
180
+ });
181
+ console.log('Test model loaded successfully:', gltf);
182
+ } catch (error) {
183
+ console.error('Test model loading failed:', error);
184
+ console.error('Test path was:', testPath);
185
+ }
186
  }
187
 
 
188
  function createTerrain() {
189
  return new Promise((resolve) => {
190
  const geometry = new THREE.PlaneGeometry(MAP_SIZE, MAP_SIZE, 200, 200);
 
237
  }
238
  }
239
 
240
+ async function loadEnemies() {
241
+ console.log('Starting enemy loading...');
242
  const loader = new GLTFLoader();
243
  const enemyCount = 3 + currentStage;
244
+ const loadPromises = [];
245
 
 
246
  for (let i = 0; i < enemyCount; i++) {
247
  const angle = (i / enemyCount) * Math.PI * 2;
248
  const radius = 200;
 
252
  Math.sin(angle) * radius
253
  );
254
 
255
+ console.log(`Creating enemy ${i} at position:`, position);
256
 
257
  // μž„μ‹œ 적 생성
258
  const tempGeometry = new THREE.BoxGeometry(5, 5, 5);
259
+ const tempMaterial = new THREE.MeshPhongMaterial({
260
+ color: 0xff0000,
261
+ transparent: true,
262
+ opacity: 0.8
263
+ });
264
  const tempEnemy = new THREE.Mesh(tempGeometry, tempMaterial);
265
  tempEnemy.position.copy(position);
266
+ tempEnemy.castShadow = true;
267
+ tempEnemy.receiveShadow = true;
268
  scene.add(tempEnemy);
269
 
 
270
  const enemy = {
271
  model: tempEnemy,
272
  health: 100,
273
  speed: ENEMY_MOVE_SPEED,
274
+ lastAttackTime: 0,
275
+ position: position.clone()
276
  };
277
  enemies.push(enemy);
278
 
279
+ // GLB λͺ¨λΈ λ‘œλ”©
280
  const modelPath = ENEMY_MODELS[i % ENEMY_MODELS.length];
281
+ const loadPromise = new Promise((resolve, reject) => {
282
+ loader.load(
283
+ modelPath,
284
+ (gltf) => {
285
+ console.log(`Successfully loaded model: ${modelPath}`);
286
+ const model = gltf.scene;
287
+ model.scale.set(ENEMY_SCALE, ENEMY_SCALE, ENEMY_SCALE);
288
+ model.position.copy(position);
289
+
290
+ model.traverse((node) => {
291
+ if (node.isMesh) {
292
+ node.castShadow = true;
293
+ node.receiveShadow = true;
294
+ node.material.metalness = 0.2;
295
+ node.material.roughness = 0.8;
296
+ }
297
+ });
298
+
299
+ scene.remove(tempEnemy);
300
+ scene.add(model);
301
+ enemy.model = model;
302
+ resolve();
303
+ },
304
+ (xhr) => {
305
+ console.log(`${modelPath}: ${(xhr.loaded / xhr.total * 100)}% loaded`);
306
+ },
307
+ (error) => {
308
+ console.error(`Error loading model ${modelPath}:`, error);
309
+ reject(error);
310
+ }
311
+ );
312
+ });
313
+ loadPromises.push(loadPromise);
314
+ }
315
+
316
+ try {
317
+ await Promise.all(loadPromises);
318
+ console.log('All enemies loaded successfully');
319
+ } catch (error) {
320
+ console.error('Error loading enemies:', error);
321
  }
322
  }
323
 
 
363
  }
364
 
365
  animateExplosion();
366
+ soundSystem.play('explosion');
367
  }
368
 
369
  // 이벀트 ν•Έλ“€λŸ¬
370
  function onClick() {
371
  if (!controls.isLocked) {
372
  controls.lock();
373
+ // μ‚¬μš΄λ“œ μ»¨ν…μŠ€νŠΈ μ‹œμž‘ (λ§Žμ€ λΈŒλΌμš°μ €μ—μ„œ ν•„μš”)
374
+ soundSystem.audioContext.resume();
375
  } else if (ammo > 0) {
376
  shoot();
377
  }
 
402
  renderer.setSize(window.innerWidth, window.innerHeight);
403
  }
404
 
 
405
  function shoot() {
406
  if (ammo <= 0) return;
407
 
 
411
  const bullet = createBullet();
412
  bullets.push(bullet);
413
 
414
+ soundSystem.play('gunshot');
415
  }
416
 
417
  function createBullet() {
 
594
  const currentTime = Date.now();
595
 
596
  enemies.forEach(enemy => {
597
+ if (!enemy || !enemy.model) {
598
+ console.warn('Invalid enemy detected');
599
+ return;
600
+ }
601
 
602
  // 적의 ν˜„μž¬ μœ„μΉ˜μ—μ„œ ν”Œλ ˆμ΄μ–΄ λ°©ν–₯으둜 ν–₯ν•˜λŠ” 벑터 계산
603
  const direction = new THREE.Vector3();
 
645
  function gameOver(won) {
646
  isGameOver = true;
647
  controls.unlock();
648
+ soundSystem.audioContext.suspend();
649
  alert(won ? 'Mission Complete!' : 'Game Over!');
650
  location.reload();
651
  }
 
678
  gameLoop();
679
  } catch (error) {
680
  console.error('Game initialization error:', error);
681
+ alert('Error loading game. Please check console for details.');
682
  }
683
+ });
684
+
685
+ // 디버깅을 μœ„ν•œ μ „μ—­ μ ‘κ·Ό
686
+ window.debugGame = {
687
+ scene,
688
+ camera,
689
+ enemies,
690
+ soundSystem,
691
+ reloadEnemies: loadEnemies
692
+ };