Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -8,302 +8,273 @@ with demo:
|
|
8 |
<head>
|
9 |
<meta charset="UTF-8">
|
10 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
11 |
-
<title>
|
12 |
<style>
|
13 |
body { margin: 0; overflow: hidden; }
|
14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
position: absolute;
|
16 |
top: 10px;
|
17 |
left: 10px;
|
|
|
18 |
color: white;
|
19 |
background: rgba(0, 0, 0, 0.7);
|
20 |
-
padding:
|
21 |
font-family: Arial, sans-serif;
|
22 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
</style>
|
24 |
</head>
|
25 |
<body>
|
26 |
-
<div id="
|
27 |
-
<div id="
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
<script>
|
34 |
-
// Scene
|
35 |
const scene = new THREE.Scene();
|
36 |
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
|
37 |
const renderer = new THREE.WebGLRenderer();
|
38 |
renderer.setSize(window.innerWidth, window.innerHeight);
|
39 |
-
document.
|
40 |
-
|
41 |
-
//
|
42 |
-
const
|
43 |
-
|
44 |
-
scene.add(
|
45 |
-
const
|
46 |
-
|
47 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
48 |
|
49 |
-
//
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
74 |
}
|
75 |
|
76 |
-
//
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
91 |
}
|
92 |
|
93 |
-
//
|
94 |
-
const
|
95 |
-
const floorGeometry = new THREE.PlaneGeometry(64, 64);
|
96 |
-
const floorMaterial = new THREE.MeshBasicMaterial({ map: floorTexture });
|
97 |
-
const floor = new THREE.Mesh(floorGeometry, floorMaterial);
|
98 |
-
floor.rotation.x = -Math.PI / 2;
|
99 |
-
scene.add(floor);
|
100 |
|
101 |
-
//
|
102 |
-
const
|
103 |
-
const
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
}
|
115 |
}
|
116 |
-
}
|
117 |
-
|
118 |
-
//
|
119 |
-
const
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
wall.position.set((j - 7.5) * cellSize, cellSize / 2, (i - 7.5) * cellSize);
|
127 |
-
scene.add(wall);
|
128 |
-
}
|
129 |
}
|
130 |
-
}
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
gScore[neighbor] = tentative_gScore;
|
156 |
-
fScore[neighbor] = gScore[neighbor] + heuristic(neighbor, goal);
|
157 |
-
if (!openSet.includes(neighbor)) openSet.push(neighbor);
|
158 |
-
}
|
159 |
-
}
|
160 |
}
|
161 |
-
return [];
|
162 |
-
}
|
163 |
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
}
|
169 |
|
170 |
-
|
171 |
-
const [row, col] = node.split(',').map(Number);
|
172 |
-
const neighbors = [];
|
173 |
-
if (row > 0 && maze[row - 1][col] === 0) neighbors.push(`${row - 1},${col}`);
|
174 |
-
if (row < 15 && maze[row + 1][col] === 0) neighbors.push(`${row + 1},${col}`);
|
175 |
-
if (col > 0 && maze[row][col - 1] === 0) neighbors.push(`${row},${col - 1}`);
|
176 |
-
if (col < 15 && maze[row][col + 1] === 0) neighbors.push(`${row},${col + 1}`);
|
177 |
-
return neighbors;
|
178 |
-
}
|
179 |
-
|
180 |
-
function reconstructPath(cameFrom, current) {
|
181 |
-
const path = [current];
|
182 |
-
while (current in cameFrom) {
|
183 |
-
current = cameFrom[current];
|
184 |
-
path.unshift(current);
|
185 |
-
}
|
186 |
-
return path;
|
187 |
-
}
|
188 |
-
|
189 |
-
// Soldiers
|
190 |
-
const loader = new THREE.GLTFLoader();
|
191 |
-
let soldiers = [];
|
192 |
-
let winner = null;
|
193 |
-
const moveSpeed = 5;
|
194 |
-
|
195 |
-
loader.load(
|
196 |
-
'https://threejs.org/examples/models/gltf/Soldier.glb',
|
197 |
-
(gltf) => {
|
198 |
-
const animations = gltf.animations;
|
199 |
-
|
200 |
-
for (let team = 0; team < 2; team++) {
|
201 |
-
for (let i = 0; i < 3; i++) {
|
202 |
-
const soldierClone = gltf.scene.clone();
|
203 |
-
const mixer = new THREE.AnimationMixer(soldierClone);
|
204 |
-
soldierClone.scale.set(2, 2, 2);
|
205 |
-
|
206 |
-
const soldierObj = {
|
207 |
-
mesh: soldierClone,
|
208 |
-
mixer: mixer,
|
209 |
-
team: team,
|
210 |
-
path: [],
|
211 |
-
idleAction: mixer.clipAction(THREE.AnimationClip.findByName(animations, 'Idle')),
|
212 |
-
runAction: mixer.clipAction(THREE.AnimationClip.findByName(animations, 'Run')),
|
213 |
-
activeAction: null
|
214 |
-
};
|
215 |
-
|
216 |
-
soldierObj.activeAction = soldierObj.idleAction;
|
217 |
-
soldierObj.activeAction.play();
|
218 |
-
|
219 |
-
// Color soldiers
|
220 |
-
soldierClone.traverse((child) => {
|
221 |
-
if (child.isMesh) {
|
222 |
-
child.material = child.material.clone();
|
223 |
-
child.material.color.set(team === 0 ? 0x0000ff : 0xff0000);
|
224 |
-
}
|
225 |
-
});
|
226 |
-
|
227 |
-
// Random starting position
|
228 |
-
let gridX, gridZ;
|
229 |
-
do {
|
230 |
-
gridZ = Math.floor(Math.random() * 16);
|
231 |
-
gridX = team === 0 ? Math.floor(Math.random() * 8) : Math.floor(Math.random() * 8) + 8;
|
232 |
-
} while (maze[gridZ][gridX] === 1);
|
233 |
-
const worldX = (gridX - 7.5) * cellSize;
|
234 |
-
const worldZ = (gridZ - 7.5) * cellSize;
|
235 |
-
soldierClone.position.set(worldX, 0, worldZ);
|
236 |
-
scene.add(soldierClone);
|
237 |
-
|
238 |
-
// Path to flag
|
239 |
-
const start = `${gridZ},${gridX}`;
|
240 |
-
const goal = "8,8";
|
241 |
-
const path = aStar(maze, start, goal);
|
242 |
-
soldierObj.path = path.map(node => {
|
243 |
-
const [row, col] = node.split(',').map(Number);
|
244 |
-
return new THREE.Vector3((col - 7.5) * cellSize, 0, (row - 7.5) * cellSize);
|
245 |
-
});
|
246 |
-
|
247 |
-
soldiers.push(soldierObj);
|
248 |
-
}
|
249 |
-
}
|
250 |
-
},
|
251 |
-
undefined,
|
252 |
-
(error) => console.error('Error loading soldier:', error)
|
253 |
-
);
|
254 |
-
|
255 |
-
// Camera and Controls
|
256 |
-
camera.position.set(0, 20, 30);
|
257 |
-
const controls = new THREE.OrbitControls(camera, renderer.domElement);
|
258 |
-
controls.target.set(0, 0, 0);
|
259 |
-
controls.update();
|
260 |
-
|
261 |
-
// Animation Loop
|
262 |
-
const clock = new THREE.Clock();
|
263 |
function animate() {
|
264 |
requestAnimationFrame(animate);
|
265 |
-
const
|
266 |
-
|
267 |
-
if (!
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
}
|
285 |
-
|
286 |
-
if (soldier.activeAction !== soldier.runAction) {
|
287 |
-
soldier.activeAction.fadeOut(0.2);
|
288 |
-
soldier.runAction.reset().fadeIn(0.2).play();
|
289 |
-
soldier.activeAction = soldier.runAction;
|
290 |
-
}
|
291 |
-
} else {
|
292 |
-
if (soldier.activeAction !== soldier.idleAction) {
|
293 |
-
soldier.activeAction.fadeOut(0.2);
|
294 |
-
soldier.idleAction.reset().fadeIn(0.2).play();
|
295 |
-
soldier.activeAction = soldier.idleAction;
|
296 |
}
|
297 |
}
|
298 |
-
|
299 |
-
}
|
300 |
}
|
301 |
|
|
|
|
|
|
|
|
|
|
|
302 |
renderer.render(scene, camera);
|
303 |
}
|
|
|
304 |
animate();
|
305 |
|
306 |
-
//
|
307 |
window.addEventListener('resize', () => {
|
308 |
camera.aspect = window.innerWidth / window.innerHeight;
|
309 |
camera.updateProjectionMatrix();
|
|
|
8 |
<head>
|
9 |
<meta charset="UTF-8">
|
10 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
11 |
+
<title>Hugging Face Game with Sound</title>
|
12 |
<style>
|
13 |
body { margin: 0; overflow: hidden; }
|
14 |
+
canvas { display: block; }
|
15 |
+
#win-message {
|
16 |
+
position: absolute;
|
17 |
+
top: 50%;
|
18 |
+
left: 50%;
|
19 |
+
transform: translate(-50%, -50%);
|
20 |
+
font-size: 48px;
|
21 |
+
color: white;
|
22 |
+
background: rgba(0, 0, 0, 0.7);
|
23 |
+
padding: 20px;
|
24 |
+
display: none;
|
25 |
+
font-family: Arial, sans-serif;
|
26 |
+
}
|
27 |
+
#gpu-counter {
|
28 |
position: absolute;
|
29 |
top: 10px;
|
30 |
left: 10px;
|
31 |
+
font-size: 24px;
|
32 |
color: white;
|
33 |
background: rgba(0, 0, 0, 0.7);
|
34 |
+
padding: 10px;
|
35 |
font-family: Arial, sans-serif;
|
36 |
}
|
37 |
+
#controls {
|
38 |
+
position: absolute;
|
39 |
+
top: 50%;
|
40 |
+
left: 50%;
|
41 |
+
transform: translate(-50%, -50%);
|
42 |
+
font-size: 20px;
|
43 |
+
color: white;
|
44 |
+
background: rgba(0, 0, 0, 0.8);
|
45 |
+
padding: 20px;
|
46 |
+
font-family: Arial, sans-serif;
|
47 |
+
text-align: center;
|
48 |
+
transition: opacity 1s;
|
49 |
+
}
|
50 |
</style>
|
51 |
</head>
|
52 |
<body>
|
53 |
+
<div id="win-message">You Win!</div>
|
54 |
+
<div id="gpu-counter">GPUs Collected: 0 / 10</div>
|
55 |
+
<div id="controls">
|
56 |
+
<p>Controls:</p>
|
57 |
+
<p>W / Up Arrow: Forward</p>
|
58 |
+
<p>S / Down Arrow: Backward</p>
|
59 |
+
<p>A / Left Arrow: Left</p>
|
60 |
+
<p>D / Right Arrow: Right</p>
|
61 |
+
<p>Spacebar: Up</p>
|
62 |
+
<p>Shift: Down</p>
|
63 |
+
<p>Collect all GPUs to win!</p>
|
64 |
+
</div>
|
65 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"></script>
|
66 |
<script>
|
67 |
+
// Scene setup
|
68 |
const scene = new THREE.Scene();
|
69 |
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
|
70 |
const renderer = new THREE.WebGLRenderer();
|
71 |
renderer.setSize(window.innerWidth, window.innerHeight);
|
72 |
+
document.body.appendChild(renderer.domElement);
|
73 |
+
|
74 |
+
// Morning lighting
|
75 |
+
const directionalLight = new THREE.DirectionalLight(0xffe0b2, 1);
|
76 |
+
directionalLight.position.set(1, 1, 0.5).normalize();
|
77 |
+
scene.add(directionalLight);
|
78 |
+
const ambientLight = new THREE.AmbientLight(0x87ceeb, 0.5);
|
79 |
+
scene.add(ambientLight);
|
80 |
+
|
81 |
+
// Morning background
|
82 |
+
scene.background = new THREE.Color(0x87ceeb);
|
83 |
+
scene.fog = new THREE.Fog(0x87ceeb, 10, 100);
|
84 |
+
|
85 |
+
// Ground
|
86 |
+
const groundGeometry = new THREE.PlaneGeometry(100, 100);
|
87 |
+
const groundMaterial = new THREE.MeshLambertMaterial({ color: 0x8c8c8c });
|
88 |
+
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
|
89 |
+
ground.rotation.x = -Math.PI / 2;
|
90 |
+
scene.add(ground);
|
91 |
+
|
92 |
+
// Realistic Buildings
|
93 |
+
const buildings = [];
|
94 |
+
const numBuildings = 50;
|
95 |
+
for (let i = 0; i < numBuildings; i++) {
|
96 |
+
const width = Math.random() * 5 + 2;
|
97 |
+
const height = Math.random() * 20 + 10;
|
98 |
+
const depth = Math.random() * 5 + 2;
|
99 |
+
const buildingGeometry = new THREE.BoxGeometry(width, height, depth);
|
100 |
+
const buildingMaterial = new THREE.MeshLambertMaterial({ color: 0xb0b0b0 });
|
101 |
+
const building = new THREE.Mesh(buildingGeometry, buildingMaterial);
|
102 |
+
building.position.x = Math.random() * 80 - 40;
|
103 |
+
building.position.y = height / 2;
|
104 |
+
building.position.z = Math.random() * 80 - 40;
|
105 |
+
scene.add(building);
|
106 |
+
buildings.push(building);
|
107 |
+
}
|
108 |
|
109 |
+
// Hugging Face Emoji
|
110 |
+
const emojiCanvas = document.createElement('canvas');
|
111 |
+
emojiCanvas.width = 256;
|
112 |
+
emojiCanvas.height = 256;
|
113 |
+
const emojiCtx = emojiCanvas.getContext('2d');
|
114 |
+
emojiCtx.font = '128px Arial';
|
115 |
+
emojiCtx.textAlign = 'center';
|
116 |
+
emojiCtx.textBaseline = 'middle';
|
117 |
+
emojiCtx.fillText('🤗', 128, 128);
|
118 |
+
const emojiTexture = new THREE.CanvasTexture(emojiCanvas);
|
119 |
+
const faceGeometry = new THREE.SphereGeometry(1, 32, 32);
|
120 |
+
const faceMaterial = new THREE.MeshLambertMaterial({ map: emojiTexture });
|
121 |
+
const face = new THREE.Mesh(faceGeometry, faceMaterial);
|
122 |
+
face.position.set(0, 5, 0);
|
123 |
+
scene.add(face);
|
124 |
+
face.velocity = new THREE.Vector3(0, 0, 0);
|
125 |
+
face.maxSpeed = 25;
|
126 |
+
face.acceleration = new THREE.Vector3(0, 0, 0);
|
127 |
+
face.friction = 0.95;
|
128 |
+
|
129 |
+
// "GPU" Texts
|
130 |
+
const canvas = document.createElement('canvas');
|
131 |
+
canvas.width = 128;
|
132 |
+
canvas.height = 128;
|
133 |
+
const ctx = canvas.getContext('2d');
|
134 |
+
ctx.font = '48px Arial';
|
135 |
+
ctx.fillStyle = 'white';
|
136 |
+
ctx.textAlign = 'center';
|
137 |
+
ctx.textBaseline = 'middle';
|
138 |
+
ctx.fillText('GPU', 64, 64);
|
139 |
+
const texture = new THREE.CanvasTexture(canvas);
|
140 |
+
const gpuSprites = [];
|
141 |
+
const totalGPUs = 10;
|
142 |
+
let collectedGPUs = 0;
|
143 |
+
for (let i = 0; i < totalGPUs; i++) {
|
144 |
+
const spriteMaterial = new THREE.SpriteMaterial({ map: texture });
|
145 |
+
const sprite = new THREE.Sprite(spriteMaterial);
|
146 |
+
sprite.scale.set(2, 2, 1);
|
147 |
+
sprite.position.set(Math.random() * 80 - 40, Math.random() * 20 + 10, Math.random() * 80 - 40);
|
148 |
+
scene.add(sprite);
|
149 |
+
gpuSprites.push(sprite);
|
150 |
}
|
151 |
|
152 |
+
// Sound setup (Web Audio API)
|
153 |
+
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
154 |
+
|
155 |
+
// Background music (simple looping oscillator)
|
156 |
+
const bgOscillator = audioCtx.createOscillator();
|
157 |
+
const bgGain = audioCtx.createGain();
|
158 |
+
bgOscillator.type = 'sine';
|
159 |
+
bgOscillator.frequency.setValueAtTime(220, audioCtx.currentTime); // Low hum
|
160 |
+
bgGain.gain.setValueAtTime(0.1, audioCtx.currentTime); // Quiet volume
|
161 |
+
bgOscillator.connect(bgGain);
|
162 |
+
bgGain.connect(audioCtx.destination);
|
163 |
+
bgOscillator.start();
|
164 |
+
|
165 |
+
// GPU collect sound
|
166 |
+
function playCollectSound() {
|
167 |
+
const oscillator = audioCtx.createOscillator();
|
168 |
+
const gain = audioCtx.createGain();
|
169 |
+
oscillator.type = 'square';
|
170 |
+
oscillator.frequency.setValueAtTime(880, audioCtx.currentTime); // High pitch
|
171 |
+
gain.gain.setValueAtTime(0.3, audioCtx.currentTime);
|
172 |
+
gain.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + 0.2);
|
173 |
+
oscillator.connect(gain);
|
174 |
+
gain.connect(audioCtx.destination);
|
175 |
+
oscillator.start();
|
176 |
+
oscillator.stop(audioCtx.currentTime + 0.2);
|
177 |
}
|
178 |
|
179 |
+
// Clock for smooth animation
|
180 |
+
const clock = new THREE.Clock();
|
|
|
|
|
|
|
|
|
|
|
181 |
|
182 |
+
// Win condition and counter
|
183 |
+
const winMessage = document.getElementById('win-message');
|
184 |
+
const gpuCounter = document.getElementById('gpu-counter');
|
185 |
+
let gameWon = false;
|
186 |
+
|
187 |
+
// Controls instructions
|
188 |
+
const controlsDiv = document.getElementById('controls');
|
189 |
+
let controlsVisible = true;
|
190 |
+
setTimeout(() => {
|
191 |
+
if (controlsVisible) {
|
192 |
+
controlsDiv.style.opacity = '0';
|
193 |
+
setTimeout(() => controlsDiv.style.display = 'none', 1000);
|
194 |
+
controlsVisible = false;
|
|
|
195 |
}
|
196 |
+
}, 5000); // Hide after 5 seconds
|
197 |
+
|
198 |
+
// Keyboard controls
|
199 |
+
const keys = {};
|
200 |
+
window.addEventListener('keydown', (e) => {
|
201 |
+
keys[e.key] = true;
|
202 |
+
if (controlsVisible) {
|
203 |
+
controlsDiv.style.opacity = '0';
|
204 |
+
setTimeout(() => controlsDiv.style.display = 'none', 1000);
|
205 |
+
controlsVisible = false;
|
|
|
|
|
|
|
206 |
}
|
207 |
+
});
|
208 |
+
window.addEventListener('keyup', (e) => { keys[e.key] = false; });
|
209 |
+
|
210 |
+
function handleControls(deltaTime) {
|
211 |
+
const accel = 50;
|
212 |
+
face.acceleration.set(0, 0, 0);
|
213 |
+
|
214 |
+
if (keys['w'] || keys['ArrowUp']) face.acceleration.z = -accel;
|
215 |
+
if (keys['s'] || keys['ArrowDown']) face.acceleration.z = accel;
|
216 |
+
if (keys['a'] || keys['ArrowLeft']) face.acceleration.x = -accel;
|
217 |
+
if (keys['d'] || keys['ArrowRight']) face.acceleration.x = accel;
|
218 |
+
if (keys[' ']) face.acceleration.y = accel;
|
219 |
+
if (keys['Shift']) face.acceleration.y = -accel;
|
220 |
+
|
221 |
+
// Apply acceleration and friction
|
222 |
+
face.velocity.add(face.acceleration.clone().multiplyScalar(deltaTime));
|
223 |
+
face.velocity.multiplyScalar(face.friction);
|
224 |
+
face.velocity.clampLength(0, face.maxSpeed);
|
225 |
+
|
226 |
+
// Update position
|
227 |
+
face.position.add(face.velocity.clone().multiplyScalar(deltaTime));
|
228 |
+
|
229 |
+
// Face direction of movement
|
230 |
+
if (face.velocity.length() > 0.1) {
|
231 |
+
face.lookAt(face.position.clone().add(face.velocity));
|
|
|
|
|
|
|
|
|
|
|
232 |
}
|
|
|
|
|
233 |
|
234 |
+
// Keep within bounds
|
235 |
+
face.position.x = Math.max(-50, Math.min(50, face.position.x));
|
236 |
+
face.position.z = Math.max(-50, Math.min(50, face.position.z));
|
237 |
+
face.position.y = Math.max(1, Math.min(50, face.position.y));
|
238 |
}
|
239 |
|
240 |
+
// Animation loop
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
241 |
function animate() {
|
242 |
requestAnimationFrame(animate);
|
243 |
+
const deltaTime = clock.getDelta();
|
244 |
+
|
245 |
+
if (!gameWon) {
|
246 |
+
// Handle player controls
|
247 |
+
handleControls(deltaTime);
|
248 |
+
|
249 |
+
// Check for "GPU" collisions
|
250 |
+
gpuSprites.forEach((gpu, index) => {
|
251 |
+
if (face.position.distanceTo(gpu.position) < 1) {
|
252 |
+
scene.remove(gpu);
|
253 |
+
gpuSprites.splice(index, 1);
|
254 |
+
collectedGPUs++;
|
255 |
+
gpuCounter.textContent = `GPUs Collected: ${collectedGPUs} / ${totalGPUs}`;
|
256 |
+
playCollectSound(); // Play sound on collection
|
257 |
+
if (collectedGPUs === totalGPUs) {
|
258 |
+
gameWon = true;
|
259 |
+
winMessage.style.display = 'block';
|
260 |
+
face.velocity.set(0, 0, 0);
|
261 |
+
bgOscillator.stop(); // Stop background music
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
262 |
}
|
263 |
}
|
264 |
+
});
|
|
|
265 |
}
|
266 |
|
267 |
+
// Update camera
|
268 |
+
camera.position.set(face.position.x, face.position.y + 5, face.position.z - 10);
|
269 |
+
camera.lookAt(face.position);
|
270 |
+
|
271 |
+
// Render
|
272 |
renderer.render(scene, camera);
|
273 |
}
|
274 |
+
|
275 |
animate();
|
276 |
|
277 |
+
// Handle window resize
|
278 |
window.addEventListener('resize', () => {
|
279 |
camera.aspect = window.innerWidth / window.innerHeight;
|
280 |
camera.updateProjectionMatrix();
|