gini1 commited on
Commit
a6d36b3
โ€ข
1 Parent(s): 0d86eb7

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +260 -225
index.html CHANGED
@@ -2,8 +2,12 @@
2
  <html>
3
  <head>
4
  <title>3D Survival Game</title>
 
5
  <style>
6
- body { margin: 0; }
 
 
 
7
  #info {
8
  position: absolute;
9
  top: 10px;
@@ -15,32 +19,33 @@
15
  font-size: 14px;
16
  z-index: 100;
17
  border-radius: 5px;
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  }
19
  #weaponAlert {
20
  position: absolute;
21
  top: 50%;
22
  left: 50%;
23
  transform: translate(-50%, -50%);
24
- color: white;
25
- background: rgba(255,0,0,0.7);
26
  padding: 20px;
27
  font-family: Arial;
28
  font-size: 24px;
29
- display: none;
30
  z-index: 100;
31
  border-radius: 10px;
32
- }
33
- #safeTimer {
34
- position: absolute;
35
- top: 10px;
36
- right: 10px;
37
- color: white;
38
- background: rgba(0,128,0,0.7);
39
- padding: 10px;
40
- font-family: Arial;
41
- font-size: 18px;
42
- z-index: 100;
43
- border-radius: 5px;
44
  }
45
  </style>
46
  </head>
@@ -52,93 +57,92 @@
52
  Find the weapon to win!<br>
53
  Avoid the enemies!
54
  </div>
 
55
  <div id="weaponAlert">Weapon has appeared!</div>
56
- <div id="safeTimer"></div>
 
 
 
 
 
 
 
 
 
 
57
 
58
  <script type="module">
59
- import * as THREE from 'https://unpkg.com/three@0.157.0/build/three.module.js';
60
- import { GLTFLoader } from 'https://unpkg.com/three@0.157.0/examples/jsm/loaders/GLTFLoader.js';
61
- import { PointerLockControls } from 'https://unpkg.com/three@0.157.0/examples/jsm/controls/PointerLockControls.js';
62
-
63
- // ๊ธฐ๋ณธ ์„ค์ •
64
- const scene = new THREE.Scene();
65
- scene.background = new THREE.Color(0x87ceeb);
66
- scene.fog = new THREE.Fog(0x87ceeb, 0, 500);
67
-
68
- const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
69
- const renderer = new THREE.WebGLRenderer({ antialias: true });
70
- renderer.setSize(window.innerWidth, window.innerHeight);
71
- renderer.shadowMap.enabled = true;
72
- renderer.shadowMap.type = THREE.PCFSoftShadowMap;
73
- document.body.appendChild(renderer.domElement);
74
-
75
- // ๊ฒŒ์ž„ ๋ณ€์ˆ˜
76
- const SAFE_TIME = 15; // 15์ดˆ ์•ˆ์ „์‹œ๊ฐ„
77
- let gameStartTime = 0;
78
- let isSafePeriod = true;
79
- let enemies = [];
80
- let weapon = null;
81
- let playerModel = null;
82
  let isGameOver = false;
 
 
 
83
 
84
- // ํฌ์ธํ„ฐ ๋ฝ ์ปจํŠธ๋กค
85
- const controls = new PointerLockControls(camera, document.body);
86
- controls.addEventListener('lock', () => {
87
- if (!gameStartTime) {
88
- gameStartTime = Date.now();
89
- setTimeout(spawnWeapon, Math.random() * 10000 + 5000); // 5-15์ดˆ ์‚ฌ์ด์— ๋ฌด๊ธฐ ์Šคํฐ
90
- }
91
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
 
93
  // ์ง€ํ˜• ์ƒ์„ฑ
94
- const terrainGeometry = new THREE.PlaneGeometry(500, 500, 100, 100);
95
- const terrainMaterial = new THREE.MeshStandardMaterial({
96
- color: 0x3a8c3a,
97
- roughness: 0.8,
98
- metalness: 0.2
99
- });
100
-
101
- // ์ง€ํ˜• ๋†’๋‚ฎ์ด ์„ค์ •
102
- const vertices = terrainGeometry.attributes.position.array;
103
- for (let i = 0; i < vertices.length; i += 3) {
104
- vertices[i + 2] = Math.sin(vertices[i] * 0.05) * Math.cos(vertices[i + 1] * 0.05) * 5;
 
 
 
 
 
 
 
105
  }
106
- terrainGeometry.attributes.position.needsUpdate = true;
107
- terrainGeometry.computeVertexNormals();
108
-
109
- const terrain = new THREE.Mesh(terrainGeometry, terrainMaterial);
110
- terrain.rotation.x = -Math.PI / 2;
111
- terrain.receiveShadow = true;
112
- scene.add(terrain);
113
-
114
- // ์กฐ๋ช… ์„ค์ •
115
- const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
116
- scene.add(ambientLight);
117
-
118
- const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
119
- directionalLight.position.set(100, 100, 50);
120
- directionalLight.castShadow = true;
121
- directionalLight.shadow.mapSize.width = 2048;
122
- directionalLight.shadow.mapSize.height = 2048;
123
- directionalLight.shadow.camera.near = 0.5;
124
- directionalLight.shadow.camera.far = 500;
125
- directionalLight.shadow.camera.left = -100;
126
- directionalLight.shadow.camera.right = 100;
127
- directionalLight.shadow.camera.top = 100;
128
- directionalLight.shadow.camera.bottom = -100;
129
- scene.add(directionalLight);
130
 
131
  // ๋‚˜๋ฌด ์ƒ์„ฑ
132
  function createTrees() {
133
- const treeGeometry = new THREE.CylinderGeometry(0, 4, 20, 8);
134
- const treeMaterial = new THREE.MeshStandardMaterial({ color: 0x0d5c0d });
135
-
136
- for (let i = 0; i < 200; i++) {
137
- const tree = new THREE.Mesh(treeGeometry, treeMaterial);
138
  tree.position.set(
139
- (Math.random() - 0.5) * 400,
140
- 10,
141
- (Math.random() - 0.5) * 400
142
  );
143
  tree.castShadow = true;
144
  tree.receiveShadow = true;
@@ -147,30 +151,36 @@
147
  }
148
 
149
  // ํ”Œ๋ ˆ์ด์–ด ๋ชจ๋ธ ๋กœ๋“œ
150
- const loader = new GLTFLoader();
151
- loader.load('me.glb', (gltf) => {
152
- playerModel = gltf.scene;
153
- playerModel.scale.set(0.5, 0.5, 0.5);
154
- playerModel.traverse((node) => {
155
- if (node.isMesh) {
156
- node.castShadow = true;
157
- node.receiveShadow = true;
158
- }
 
 
 
 
 
 
 
159
  });
160
- scene.add(playerModel);
161
- });
162
 
163
- // ์  ์ƒ์„ฑ
164
- function createEnemies() {
 
165
  loader.load('enemy.glb', (gltf) => {
166
  const enemyModel = gltf.scene;
167
 
168
- for (let i = 0; i < 8; i++) {
169
  const enemy = enemyModel.clone();
170
  enemy.scale.set(0.5, 0.5, 0.5);
171
 
172
- // ํ”Œ๋ ˆ์ด์–ด๋กœ๋ถ€ํ„ฐ ๋ฉ€๋ฆฌ ์Šคํฐ
173
- const angle = (i / 8) * Math.PI * 2;
174
  const radius = 100;
175
  enemy.position.set(
176
  Math.cos(angle) * radius,
@@ -181,24 +191,28 @@
181
  enemy.traverse((node) => {
182
  if (node.isMesh) {
183
  node.castShadow = true;
184
- node.receiveShadow = true;
185
  }
186
  });
187
 
188
  scene.add(enemy);
189
  enemies.push({
190
  model: enemy,
191
- velocity: new THREE.Vector3(),
192
- speed: 0.2 + Math.random() * 0.2
193
  });
194
  }
 
 
 
 
 
195
  });
196
  }
197
 
198
- // ๋ฌด๊ธฐ ์Šคํฐ
199
  function spawnWeapon() {
200
- const weaponGeometry = new THREE.BoxGeometry(1, 1, 3);
201
- const weaponMaterial = new THREE.MeshStandardMaterial({
202
  color: 0xffd700,
203
  metalness: 0.7,
204
  roughness: 0.3,
@@ -206,7 +220,7 @@
206
  emissiveIntensity: 0.5
207
  });
208
 
209
- weapon = new THREE.Mesh(weaponGeometry, weaponMaterial);
210
  weapon.position.set(
211
  (Math.random() - 0.5) * 200,
212
  2,
@@ -216,139 +230,160 @@
216
  scene.add(weapon);
217
 
218
  // ๋ฌด๊ธฐ ์ถœํ˜„ ์•Œ๋ฆผ
219
- const weaponAlert = document.getElementById('weaponAlert');
220
- weaponAlert.style.display = 'block';
221
  setTimeout(() => {
222
- weaponAlert.style.display = 'none';
223
  }, 3000);
224
  }
225
 
226
- // ์ด๋™ ์ƒํƒœ
227
- const moveState = {
228
- forward: false,
229
- backward: false,
230
- left: false,
231
- right: false
232
- };
233
-
234
- // ํ‚ค๋ณด๋“œ ์ด๋ฒคํŠธ
235
- document.addEventListener('keydown', (event) => {
236
- switch (event.code) {
237
- case 'KeyW': moveState.forward = true; break;
238
- case 'KeyS': moveState.backward = true; break;
239
- case 'KeyA': moveState.left = true; break;
240
- case 'KeyD': moveState.right = true; break;
241
- }
242
- });
243
-
244
- document.addEventListener('keyup', (event) => {
245
- switch (event.code) {
246
- case 'KeyW': moveState.forward = false; break;
247
- case 'KeyS': moveState.backward = false; break;
248
- case 'KeyA': moveState.left = false; break;
249
- case 'KeyD': moveState.right = false; break;
250
- }
251
- });
252
 
253
- // ๊ฒŒ์ž„ ์˜ค๋ฒ„
254
- function gameOver(won = false) {
255
- if (!isGameOver) {
256
- isGameOver = true;
257
- controls.unlock();
258
- alert(won ? 'You found the weapon and won!' : 'Game Over! You were caught!');
259
- location.reload();
260
- }
261
- }
262
 
263
- // ์ดˆ๊ธฐ ์„ค์ •
264
- camera.position.set(0, 5, 0);
265
- createTrees();
266
- createEnemies();
 
 
 
267
 
268
- // ๊ฒŒ์ž„ ๋ฃจํ”„
269
- function animate() {
270
- requestAnimationFrame(animate);
271
 
272
- if (controls.isLocked && !isGameOver) {
273
- // ์•ˆ์ „ ์‹œ๊ฐ„ ์ฒดํฌ
274
- const elapsedTime = Math.floor((Date.now() - gameStartTime) / 1000);
275
- const safeTimer = document.getElementById('safeTimer');
276
-
277
- if (elapsedTime < SAFE_TIME) {
278
- safeTimer.textContent = `Safe Time: ${SAFE_TIME - elapsedTime}s`;
279
- isSafePeriod = true;
280
- } else {
281
- safeTimer.textContent = '';
282
- isSafePeriod = false;
283
- }
284
 
285
- // ์ด๋™ ์ฒ˜๋ฆฌ
286
- const speed = 0.5;
287
- if (moveState.forward) controls.moveForward(speed);
288
- if (moveState.backward) controls.moveForward(-speed);
289
- if (moveState.left) controls.moveRight(-speed);
290
- if (moveState.right) controls.moveRight(speed);
291
-
292
- // ํ”Œ๋ ˆ์ด์–ด ๋ชจ๋ธ ์—…๋ฐ์ดํŠธ
293
- if (playerModel) {
294
- playerModel.position.copy(camera.position);
295
- playerModel.position.y -= 2;
296
-
297
- // ์ด๋™ ๋ฐฉํ–ฅ์— ๋”ฐ๋ฅธ ํšŒ์ „
298
- if (moveState.forward || moveState.backward || moveState.left || moveState.right) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
299
  const direction = new THREE.Vector3();
300
  camera.getWorldDirection(direction);
301
  playerModel.rotation.y = Math.atan2(direction.x, direction.z);
302
  }
303
- }
304
 
305
- // ์  ์—…๋ฐ์ดํŠธ
306
- if (!isSafePeriod) {
307
- enemies.forEach(enemy => {
308
- const direction = new THREE.Vector3();
309
- direction.subVectors(camera.position, enemy.model.position);
310
- direction.y = 0;
311
- direction.normalize();
312
-
313
- enemy.velocity.add(direction.multiplyScalar(enemy.speed));
314
- enemy.velocity.multiplyScalar(0.98);
315
- enemy.model.position.add(enemy.velocity);
316
-
317
- // ์  ํšŒ์ „
318
- enemy.model.lookAt(camera.position);
319
-
320
- // ์ถฉ๋Œ ์ฒดํฌ
321
- if (enemy.model.position.distanceTo(camera.position) < 3) {
322
- gameOver(false);
323
- }
324
- });
325
- }
326
 
327
- // ๋ฌด๊ธฐ ์ฒดํฌ
328
- if (weapon) {
329
- weapon.rotation.y += 0.02;
330
- if (camera.position.distanceTo(weapon.position) < 3) {
331
- gameOver(true);
 
332
  }
333
  }
 
 
334
  }
335
 
336
- renderer.render(scene, camera);
337
- }
 
 
 
 
338
 
339
- // ํ™”๋ฉด ํฌ๊ธฐ ์กฐ์ •
340
- window.addEventListener('resize', () => {
341
- camera.aspect = window.innerWidth / window.innerHeight;
342
- camera.updateProjectionMatrix();
343
- renderer.setSize(window.innerWidth, window.innerHeight);
344
- });
345
 
346
- // ์‹œ์ž‘
347
- document.addEventListener('click', () => {
348
- controls.lock();
349
- });
 
 
 
 
 
 
 
350
 
351
- animate();
 
352
  </script>
353
  </body>
354
  </html>
 
2
  <html>
3
  <head>
4
  <title>3D Survival Game</title>
5
+ <meta charset="utf-8">
6
  <style>
7
+ body {
8
+ margin: 0;
9
+ overflow: hidden;
10
+ }
11
  #info {
12
  position: absolute;
13
  top: 10px;
 
19
  font-size: 14px;
20
  z-index: 100;
21
  border-radius: 5px;
22
+ user-select: none;
23
+ }
24
+ #timer {
25
+ position: absolute;
26
+ top: 10px;
27
+ right: 10px;
28
+ color: white;
29
+ background: rgba(0,0,0,0.7);
30
+ padding: 10px;
31
+ font-family: Arial;
32
+ font-size: 14px;
33
+ z-index: 100;
34
+ border-radius: 5px;
35
  }
36
  #weaponAlert {
37
  position: absolute;
38
  top: 50%;
39
  left: 50%;
40
  transform: translate(-50%, -50%);
41
+ color: yellow;
42
+ background: rgba(0,0,0,0.8);
43
  padding: 20px;
44
  font-family: Arial;
45
  font-size: 24px;
 
46
  z-index: 100;
47
  border-radius: 10px;
48
+ display: none;
 
 
 
 
 
 
 
 
 
 
 
49
  }
50
  </style>
51
  </head>
 
57
  Find the weapon to win!<br>
58
  Avoid the enemies!
59
  </div>
60
+ <div id="timer">Safe Time: 10s</div>
61
  <div id="weaponAlert">Weapon has appeared!</div>
62
+
63
+ <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script>
64
+
65
+ <script type="importmap">
66
+ {
67
+ "imports": {
68
+ "three": "https://unpkg.com/three@0.157.0/build/three.module.js",
69
+ "three/addons/": "https://unpkg.com/three@0.157.0/examples/jsm/"
70
+ }
71
+ }
72
+ </script>
73
 
74
  <script type="module">
75
+ import * as THREE from 'three';
76
+ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
77
+ import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js';
78
+
79
+ // ๊ธฐ๋ณธ ๋ณ€์ˆ˜
80
+ let scene, camera, renderer, controls;
81
+ let playerModel, enemies = [], weapon;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  let isGameOver = false;
83
+ let isSafePeriod = true;
84
+ let startTime = 0;
85
+ const SAFE_TIME = 10; // 10์ดˆ ์•ˆ์ „์‹œ๊ฐ„
86
 
87
+ // Scene ์ดˆ๊ธฐํ™”
88
+ function initScene() {
89
+ scene = new THREE.Scene();
90
+ scene.background = new THREE.Color(0x87ceeb);
91
+ scene.fog = new THREE.Fog(0x87ceeb, 0, 300);
92
+
93
+ camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
94
+ camera.position.set(0, 5, 0);
95
+
96
+ renderer = new THREE.WebGLRenderer({ antialias: true });
97
+ renderer.setSize(window.innerWidth, window.innerHeight);
98
+ renderer.shadowMap.enabled = true;
99
+ document.body.appendChild(renderer.domElement);
100
+
101
+ // ์กฐ๋ช…
102
+ const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
103
+ scene.add(ambientLight);
104
+
105
+ const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
106
+ dirLight.position.set(50, 50, 0);
107
+ dirLight.castShadow = true;
108
+ dirLight.shadow.mapSize.width = 2048;
109
+ dirLight.shadow.mapSize.height = 2048;
110
+ dirLight.shadow.camera.far = 300;
111
+ scene.add(dirLight);
112
+ }
113
 
114
  // ์ง€ํ˜• ์ƒ์„ฑ
115
+ function createTerrain() {
116
+ const geometry = new THREE.PlaneGeometry(300, 300, 50, 50);
117
+ const material = new THREE.MeshStandardMaterial({
118
+ color: 0x3a8c3a,
119
+ roughness: 0.8
120
+ });
121
+
122
+ const vertices = geometry.attributes.position.array;
123
+ for (let i = 0; i < vertices.length; i += 3) {
124
+ vertices[i + 2] = Math.random() * 3;
125
+ }
126
+ geometry.attributes.position.needsUpdate = true;
127
+ geometry.computeVertexNormals();
128
+
129
+ const terrain = new THREE.Mesh(geometry, material);
130
+ terrain.rotation.x = -Math.PI / 2;
131
+ terrain.receiveShadow = true;
132
+ scene.add(terrain);
133
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
 
135
  // ๋‚˜๋ฌด ์ƒ์„ฑ
136
  function createTrees() {
137
+ const treeGeo = new THREE.CylinderGeometry(0, 4, 15, 8);
138
+ const treeMat = new THREE.MeshStandardMaterial({ color: 0x0d5c0d });
139
+
140
+ for (let i = 0; i < 100; i++) {
141
+ const tree = new THREE.Mesh(treeGeo, treeMat);
142
  tree.position.set(
143
+ (Math.random() - 0.5) * 250,
144
+ 7.5,
145
+ (Math.random() - 0.5) * 250
146
  );
147
  tree.castShadow = true;
148
  tree.receiveShadow = true;
 
151
  }
152
 
153
  // ํ”Œ๋ ˆ์ด์–ด ๋ชจ๋ธ ๋กœ๋“œ
154
+ function loadPlayer() {
155
+ const loader = new GLTFLoader();
156
+ loader.load('me.glb', (gltf) => {
157
+ playerModel = gltf.scene;
158
+ playerModel.scale.set(0.5, 0.5, 0.5);
159
+ playerModel.traverse((node) => {
160
+ if (node.isMesh) {
161
+ node.castShadow = true;
162
+ }
163
+ });
164
+ scene.add(playerModel);
165
+ console.log('Player model loaded');
166
+ },
167
+ undefined,
168
+ (error) => {
169
+ console.error('Error loading player model:', error);
170
  });
171
+ }
 
172
 
173
+ // ์  ๋กœ๋“œ
174
+ function loadEnemies() {
175
+ const loader = new GLTFLoader();
176
  loader.load('enemy.glb', (gltf) => {
177
  const enemyModel = gltf.scene;
178
 
179
+ for (let i = 0; i < 5; i++) {
180
  const enemy = enemyModel.clone();
181
  enemy.scale.set(0.5, 0.5, 0.5);
182
 
183
+ const angle = (i / 5) * Math.PI * 2;
 
184
  const radius = 100;
185
  enemy.position.set(
186
  Math.cos(angle) * radius,
 
191
  enemy.traverse((node) => {
192
  if (node.isMesh) {
193
  node.castShadow = true;
 
194
  }
195
  });
196
 
197
  scene.add(enemy);
198
  enemies.push({
199
  model: enemy,
200
+ speed: 0.2,
201
+ velocity: new THREE.Vector3()
202
  });
203
  }
204
+ console.log('Enemies loaded');
205
+ },
206
+ undefined,
207
+ (error) => {
208
+ console.error('Error loading enemy model:', error);
209
  });
210
  }
211
 
212
+ // ๋ฌด๊ธฐ ์ƒ์„ฑ
213
  function spawnWeapon() {
214
+ const geometry = new THREE.BoxGeometry(1, 1, 3);
215
+ const material = new THREE.MeshStandardMaterial({
216
  color: 0xffd700,
217
  metalness: 0.7,
218
  roughness: 0.3,
 
220
  emissiveIntensity: 0.5
221
  });
222
 
223
+ weapon = new THREE.Mesh(geometry, material);
224
  weapon.position.set(
225
  (Math.random() - 0.5) * 200,
226
  2,
 
230
  scene.add(weapon);
231
 
232
  // ๋ฌด๊ธฐ ์ถœํ˜„ ์•Œ๋ฆผ
233
+ document.getElementById('weaponAlert').style.display = 'block';
 
234
  setTimeout(() => {
235
+ document.getElementById('weaponAlert').style.display = 'none';
236
  }, 3000);
237
  }
238
 
239
+ // ์ปจํŠธ๋กค ์ดˆ๊ธฐํ™”
240
+ function initControls() {
241
+ controls = new PointerLockControls(camera, document.body);
242
+
243
+ const moveState = {
244
+ forward: false,
245
+ backward: false,
246
+ left: false,
247
+ right: false
248
+ };
249
+
250
+ document.addEventListener('keydown', (event) => {
251
+ switch(event.code) {
252
+ case 'KeyW': moveState.forward = true; break;
253
+ case 'KeyS': moveState.backward = true; break;
254
+ case 'KeyA': moveState.left = true; break;
255
+ case 'KeyD': moveState.right = true; break;
256
+ }
257
+ });
 
 
 
 
 
 
 
258
 
259
+ document.addEventListener('keyup', (event) => {
260
+ switch(event.code) {
261
+ case 'KeyW': moveState.forward = false; break;
262
+ case 'KeyS': moveState.backward = false; break;
263
+ case 'KeyA': moveState.left = false; break;
264
+ case 'KeyD': moveState.right = false; break;
265
+ }
266
+ });
 
267
 
268
+ document.addEventListener('click', () => {
269
+ if (!startTime) {
270
+ startTime = Date.now();
271
+ setTimeout(spawnWeapon, Math.random() * 5000 + 5000);
272
+ }
273
+ controls.lock();
274
+ });
275
 
276
+ controls.addEventListener('lock', () => {
277
+ document.getElementById('info').style.display = 'none';
278
+ });
279
 
280
+ controls.addEventListener('unlock', () => {
281
+ document.getElementById('info').style.display = 'block';
282
+ });
 
 
 
 
 
 
 
 
 
283
 
284
+ return moveState;
285
+ }
286
+
287
+ // ๊ฒŒ์ž„ ์ดˆ๊ธฐํ™”
288
+ function init() {
289
+ initScene();
290
+ createTerrain();
291
+ createTrees();
292
+ loadPlayer();
293
+ loadEnemies();
294
+ const moveState = initControls();
295
+
296
+ // ๊ฒŒ์ž„ ๋ฃจํ”„
297
+ function animate() {
298
+ requestAnimationFrame(animate);
299
+
300
+ if (controls.isLocked && !isGameOver) {
301
+ // ์•ˆ์ „ ์‹œ๊ฐ„ ์ฒดํฌ
302
+ if (startTime) {
303
+ const elapsed = Math.floor((Date.now() - startTime) / 1000);
304
+ const remaining = SAFE_TIME - elapsed;
305
+ if (remaining > 0) {
306
+ document.getElementById('timer').textContent = `Safe Time: ${remaining}s`;
307
+ } else {
308
+ document.getElementById('timer').textContent = '';
309
+ isSafePeriod = false;
310
+ }
311
+ }
312
+
313
+ // ์ด๋™ ์ฒ˜๋ฆฌ
314
+ const speed = 0.3;
315
+ if (moveState.forward) controls.moveForward(speed);
316
+ if (moveState.backward) controls.moveForward(-speed);
317
+ if (moveState.left) controls.moveRight(-speed);
318
+ if (moveState.right) controls.moveRight(speed);
319
+
320
+ // ํ”Œ๋ ˆ์ด์–ด ๋ชจ๋ธ ์—…๋ฐ์ดํŠธ
321
+ if (playerModel) {
322
+ playerModel.position.copy(camera.position);
323
+ playerModel.position.y -= 2;
324
  const direction = new THREE.Vector3();
325
  camera.getWorldDirection(direction);
326
  playerModel.rotation.y = Math.atan2(direction.x, direction.z);
327
  }
 
328
 
329
+ // ์  ์—…๋ฐ์ดํŠธ
330
+ if (!isSafePeriod) {
331
+ enemies.forEach(enemy => {
332
+ const direction = new THREE.Vector3();
333
+ direction.subVectors(camera.position, enemy.model.position);
334
+ direction.y = 0;
335
+ direction.normalize();
336
+
337
+ enemy.velocity.add(direction.multiplyScalar(enemy.speed));
338
+ enemy.velocity.multiplyScalar(0.98);
339
+ enemy.model.position.add(enemy.velocity);
340
+
341
+ // ์ถฉ๋Œ ์ฒดํฌ
342
+ if (enemy.model.position.distanceTo(camera.position) < 3) {
343
+ gameOver(false);
344
+ }
345
+
346
+ // ์  ํšŒ์ „
347
+ enemy.model.lookAt(camera.position);
348
+ });
349
+ }
350
 
351
+ // ๋ฌด๊ธฐ ์ฒดํฌ
352
+ if (weapon) {
353
+ weapon.rotation.y += 0.02;
354
+ if (camera.position.distanceTo(weapon.position) < 3) {
355
+ gameOver(true);
356
+ }
357
  }
358
  }
359
+
360
+ renderer.render(scene, camera);
361
  }
362
 
363
+ // ์ฐฝ ํฌ๊ธฐ ์กฐ์ ˆ ์ฒ˜๋ฆฌ
364
+ window.addEventListener('resize', () => {
365
+ camera.aspect = window.innerWidth / window.innerHeight;
366
+ camera.updateProjectionMatrix();
367
+ renderer.setSize(window.innerWidth, window.innerHeight);
368
+ });
369
 
370
+ animate();
371
+ }
 
 
 
 
372
 
373
+ // ๊ฒŒ์ž„ ์˜ค๋ฒ„ ์ฒ˜๋ฆฌ
374
+ function gameOver(won) {
375
+ if (!isGameOver) {
376
+ isGameOver = true;
377
+ controls.unlock();
378
+ setTimeout(() => {
379
+ alert(won ? 'You found the weapon and won!' : 'Game Over! You were caught!');
380
+ location.reload();
381
+ }, 100);
382
+ }
383
+ }
384
 
385
+ // ๊ฒŒ์ž„ ์‹œ์ž‘
386
+ init();
387
  </script>
388
  </body>
389
  </html>