Spaces:
Running
Running
gini1
commited on
Update game.js
Browse files
game.js
CHANGED
@@ -1,4 +1,3 @@
|
|
1 |
-
// game.js
|
2 |
import * as THREE from 'three';
|
3 |
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
|
4 |
import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js';
|
@@ -10,12 +9,11 @@ const HELICOPTER_HEIGHT = 50;
|
|
10 |
const ENEMY_SCALE = 3;
|
11 |
const MAX_HEALTH = 1000;
|
12 |
const ENEMY_MODELS = [
|
13 |
-
'./models/enemy1.glb',
|
14 |
'./models/enemy2.glb',
|
15 |
'./models/enemy3.glb',
|
16 |
'./models/enemy4.glb'
|
17 |
];
|
18 |
-
|
19 |
const ENEMY_CONFIG = {
|
20 |
ATTACK_RANGE: 100,
|
21 |
ATTACK_INTERVAL: 2000,
|
@@ -32,28 +30,46 @@ let ammo = 30;
|
|
32 |
let currentStage = 1;
|
33 |
let isGameOver = false;
|
34 |
|
35 |
-
// μ¬μ΄λ ν
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
41 |
}
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
49 |
}
|
50 |
-
|
51 |
-
|
|
|
|
|
52 |
|
53 |
// μ¬μ΄λ μ΄κΈ°ν
|
54 |
const sounds = {
|
55 |
bgm: new Audio('Music.wav'),
|
56 |
-
gunshot:
|
57 |
};
|
58 |
sounds.bgm.loop = true;
|
59 |
|
@@ -93,6 +109,13 @@ function init() {
|
|
93 |
// Controls μ€μ
|
94 |
controls = new PointerLockControls(camera, document.body);
|
95 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
96 |
// μ΄λ²€νΈ 리μ€λ μ€μ
|
97 |
document.addEventListener('click', onClick);
|
98 |
document.addEventListener('keydown', onKeyDown);
|
@@ -102,7 +125,7 @@ function init() {
|
|
102 |
// μ§ν μμ±
|
103 |
createTerrain();
|
104 |
|
105 |
-
|
106 |
loadEnemies();
|
107 |
}
|
108 |
|
@@ -130,29 +153,12 @@ function createTerrain() {
|
|
130 |
addObstacles();
|
131 |
}
|
132 |
|
133 |
-
function
|
134 |
-
const
|
135 |
-
const
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
for (let i = 0; i < 100; i++) {
|
141 |
-
const rock = new THREE.Mesh(rockGeometry, rockMaterial);
|
142 |
-
rock.position.set(
|
143 |
-
(Math.random() - 0.5) * MAP_SIZE * 0.9,
|
144 |
-
Math.random() * 10,
|
145 |
-
(Math.random() - 0.5) * MAP_SIZE * 0.9
|
146 |
-
);
|
147 |
-
rock.rotation.set(
|
148 |
-
Math.random() * Math.PI,
|
149 |
-
Math.random() * Math.PI,
|
150 |
-
Math.random() * Math.PI
|
151 |
-
);
|
152 |
-
rock.castShadow = true;
|
153 |
-
rock.receiveShadow = true;
|
154 |
-
scene.add(rock);
|
155 |
-
}
|
156 |
}
|
157 |
|
158 |
function loadEnemies() {
|
@@ -160,21 +166,32 @@ function loadEnemies() {
|
|
160 |
const enemyCount = 3 + currentStage;
|
161 |
|
162 |
for (let i = 0; i < enemyCount; i++) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
163 |
const modelPath = ENEMY_MODELS[i % ENEMY_MODELS.length];
|
164 |
-
|
165 |
-
loader.load(modelPath,
|
166 |
(gltf) => {
|
167 |
-
console.log('Enemy model loaded
|
168 |
const enemy = gltf.scene;
|
169 |
enemy.scale.set(ENEMY_SCALE, ENEMY_SCALE, ENEMY_SCALE);
|
170 |
-
|
171 |
-
const angle = (i / enemyCount) * Math.PI * 2;
|
172 |
-
const radius = 200;
|
173 |
-
enemy.position.set(
|
174 |
-
Math.cos(angle) * radius,
|
175 |
-
10,
|
176 |
-
Math.sin(angle) * radius
|
177 |
-
);
|
178 |
|
179 |
enemy.traverse((node) => {
|
180 |
if (node.isMesh) {
|
@@ -185,13 +202,13 @@ function loadEnemies() {
|
|
185 |
}
|
186 |
});
|
187 |
|
|
|
|
|
188 |
scene.add(enemy);
|
189 |
-
enemies.
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
lastAttackTime: 0
|
194 |
-
});
|
195 |
},
|
196 |
(xhr) => {
|
197 |
console.log((xhr.loaded / xhr.total * 100) + '% loaded');
|
@@ -203,6 +220,31 @@ function loadEnemies() {
|
|
203 |
}
|
204 |
}
|
205 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
206 |
function onClick() {
|
207 |
if (!controls.isLocked) {
|
208 |
controls.lock();
|
|
|
|
|
1 |
import * as THREE from 'three';
|
2 |
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
|
3 |
import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js';
|
|
|
9 |
const ENEMY_SCALE = 3;
|
10 |
const MAX_HEALTH = 1000;
|
11 |
const ENEMY_MODELS = [
|
12 |
+
'./models/enemy1.glb',
|
13 |
'./models/enemy2.glb',
|
14 |
'./models/enemy3.glb',
|
15 |
'./models/enemy4.glb'
|
16 |
];
|
|
|
17 |
const ENEMY_CONFIG = {
|
18 |
ATTACK_RANGE: 100,
|
19 |
ATTACK_INTERVAL: 2000,
|
|
|
30 |
let currentStage = 1;
|
31 |
let isGameOver = false;
|
32 |
|
33 |
+
// μ¬μ΄λ ν ν΄λμ€
|
34 |
+
class SoundPool {
|
35 |
+
constructor(soundUrl, poolSize = 10) {
|
36 |
+
this.sounds = [];
|
37 |
+
this.currentIndex = 0;
|
38 |
+
this.poolSize = poolSize;
|
39 |
+
|
40 |
+
for (let i = 0; i < poolSize; i++) {
|
41 |
+
const sound = new Audio(soundUrl);
|
42 |
+
sound.preload = 'auto';
|
43 |
+
this.sounds.push(sound);
|
44 |
+
}
|
45 |
}
|
46 |
+
|
47 |
+
play() {
|
48 |
+
const sound = this.sounds[this.currentIndex];
|
49 |
+
|
50 |
+
// νμ¬ μ¬μ μ€μΈ μ¬μ΄λ μ΄κΈ°ν
|
51 |
+
sound.pause();
|
52 |
+
sound.currentTime = 0;
|
53 |
+
|
54 |
+
// μλ‘μ΄ Promiseλ‘ μ¬μ΄λ μ¬μ
|
55 |
+
const playPromise = sound.play();
|
56 |
+
|
57 |
+
if (playPromise !== undefined) {
|
58 |
+
playPromise.catch(error => {
|
59 |
+
console.error("Sound play error:", error);
|
60 |
+
// μ€λ₯ λ°μ μ μλ‘μ΄ Audio κ°μ²΄λ‘ κ΅μ²΄
|
61 |
+
this.sounds[this.currentIndex] = new Audio(sound.src);
|
62 |
+
});
|
63 |
}
|
64 |
+
|
65 |
+
this.currentIndex = (this.currentIndex + 1) % this.poolSize;
|
66 |
+
}
|
67 |
+
}
|
68 |
|
69 |
// μ¬μ΄λ μ΄κΈ°ν
|
70 |
const sounds = {
|
71 |
bgm: new Audio('Music.wav'),
|
72 |
+
gunshot: new SoundPool('gun.wav', 20)
|
73 |
};
|
74 |
sounds.bgm.loop = true;
|
75 |
|
|
|
109 |
// Controls μ€μ
|
110 |
controls = new PointerLockControls(camera, document.body);
|
111 |
|
112 |
+
// λλ²κ·Έ ν¬νΌ μΆκ°
|
113 |
+
const axesHelper = new THREE.AxesHelper(5);
|
114 |
+
scene.add(axesHelper);
|
115 |
+
|
116 |
+
const gridHelper = new THREE.GridHelper(1000, 100);
|
117 |
+
scene.add(gridHelper);
|
118 |
+
|
119 |
// μ΄λ²€νΈ 리μ€λ μ€μ
|
120 |
document.addEventListener('click', onClick);
|
121 |
document.addEventListener('keydown', onKeyDown);
|
|
|
125 |
// μ§ν μμ±
|
126 |
createTerrain();
|
127 |
|
128 |
+
console.log('Starting to load enemies...');
|
129 |
loadEnemies();
|
130 |
}
|
131 |
|
|
|
153 |
addObstacles();
|
154 |
}
|
155 |
|
156 |
+
function createTemporaryEnemy(position) {
|
157 |
+
const geometry = new THREE.BoxGeometry(5, 5, 5);
|
158 |
+
const material = new THREE.MeshPhongMaterial({ color: 0xff0000 });
|
159 |
+
const cube = new THREE.Mesh(geometry, material);
|
160 |
+
cube.position.copy(position);
|
161 |
+
return cube;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
162 |
}
|
163 |
|
164 |
function loadEnemies() {
|
|
|
166 |
const enemyCount = 3 + currentStage;
|
167 |
|
168 |
for (let i = 0; i < enemyCount; i++) {
|
169 |
+
const angle = (i / enemyCount) * Math.PI * 2;
|
170 |
+
const radius = 200;
|
171 |
+
const position = new THREE.Vector3(
|
172 |
+
Math.cos(angle) * radius,
|
173 |
+
10,
|
174 |
+
Math.sin(angle) * radius
|
175 |
+
);
|
176 |
+
|
177 |
+
// μμ μ μμ±
|
178 |
+
const tempEnemy = createTemporaryEnemy(position);
|
179 |
+
scene.add(tempEnemy);
|
180 |
+
enemies.push({
|
181 |
+
model: tempEnemy,
|
182 |
+
health: 100,
|
183 |
+
speed: 0.3 + (currentStage * 0.1),
|
184 |
+
lastAttackTime: 0
|
185 |
+
});
|
186 |
+
|
187 |
+
// GLB λͺ¨λΈ λ‘λ μλ
|
188 |
const modelPath = ENEMY_MODELS[i % ENEMY_MODELS.length];
|
189 |
+
loader.load(modelPath,
|
|
|
190 |
(gltf) => {
|
191 |
+
console.log('Enemy model loaded:', modelPath);
|
192 |
const enemy = gltf.scene;
|
193 |
enemy.scale.set(ENEMY_SCALE, ENEMY_SCALE, ENEMY_SCALE);
|
194 |
+
enemy.position.copy(position);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
195 |
|
196 |
enemy.traverse((node) => {
|
197 |
if (node.isMesh) {
|
|
|
202 |
}
|
203 |
});
|
204 |
|
205 |
+
// μμ μ μ μ€μ λͺ¨λΈλ‘ κ΅μ²΄
|
206 |
+
scene.remove(tempEnemy);
|
207 |
scene.add(enemy);
|
208 |
+
const index = enemies.findIndex(e => e.model === tempEnemy);
|
209 |
+
if (index !== -1) {
|
210 |
+
enemies[index].model = enemy;
|
211 |
+
}
|
|
|
|
|
212 |
},
|
213 |
(xhr) => {
|
214 |
console.log((xhr.loaded / xhr.total * 100) + '% loaded');
|
|
|
220 |
}
|
221 |
}
|
222 |
|
223 |
+
function addObstacles() {
|
224 |
+
const rockGeometry = new THREE.DodecahedronGeometry(10);
|
225 |
+
const rockMaterial = new THREE.MeshStandardMaterial({
|
226 |
+
color: 0x8B4513,
|
227 |
+
roughness: 0.9
|
228 |
+
});
|
229 |
+
|
230 |
+
for (let i = 0; i < 100; i++) {
|
231 |
+
const rock = new THREE.Mesh(rockGeometry, rockMaterial);
|
232 |
+
rock.position.set(
|
233 |
+
(Math.random() - 0.5) * MAP_SIZE * 0.9,
|
234 |
+
Math.random() * 10,
|
235 |
+
(Math.random() - 0.5) * MAP_SIZE * 0.9
|
236 |
+
);
|
237 |
+
rock.rotation.set(
|
238 |
+
Math.random() * Math.PI,
|
239 |
+
Math.random() * Math.PI,
|
240 |
+
Math.random() * Math.PI
|
241 |
+
);
|
242 |
+
rock.castShadow = true;
|
243 |
+
rock.receiveShadow = true;
|
244 |
+
scene.add(rock);
|
245 |
+
}
|
246 |
+
}
|
247 |
+
|
248 |
function onClick() {
|
249 |
if (!controls.isLocked) {
|
250 |
controls.lock();
|