zdwalter commited on
Commit
78bd595
·
verified ·
1 Parent(s): 24c839a

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +6 -4
  2. index.html +648 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: 3d Car Game
3
- emoji: 📈
4
- colorFrom: gray
5
  colorTo: blue
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: 3d-car-game
3
+ emoji: 🐳
4
+ colorFrom: blue
5
  colorTo: blue
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,648 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>3D Racing Game</title>
7
+ <style>
8
+ body {
9
+ margin: 0;
10
+ overflow: hidden;
11
+ font-family: 'Arial', sans-serif;
12
+ color: white;
13
+ background-color: #111;
14
+ }
15
+
16
+ #game-container {
17
+ position: relative;
18
+ width: 100vw;
19
+ height: 100vh;
20
+ }
21
+
22
+ #info {
23
+ position: absolute;
24
+ top: 20px;
25
+ left: 20px;
26
+ z-index: 10;
27
+ background-color: rgba(0, 0, 0, 0.5);
28
+ padding: 10px;
29
+ border-radius: 5px;
30
+ }
31
+
32
+ #start-screen {
33
+ position: absolute;
34
+ top: 50%;
35
+ left: 50%;
36
+ transform: translate(-50%, -50%);
37
+ text-align: center;
38
+ background-color: rgba(0, 0, 0, 0.8);
39
+ padding: 30px;
40
+ border-radius: 10px;
41
+ z-index: 100;
42
+ }
43
+
44
+ #game-over {
45
+ position: absolute;
46
+ top: 50%;
47
+ left: 50%;
48
+ transform: translate(-50%, -50%);
49
+ text-align: center;
50
+ background-color: rgba(0, 0, 0, 0.9);
51
+ padding: 30px;
52
+ border-radius: 10px;
53
+ z-index: 100;
54
+ display: none;
55
+ }
56
+
57
+ button {
58
+ background-color: #f1c40f;
59
+ color: #111;
60
+ border: none;
61
+ padding: 10px 20px;
62
+ margin-top: 15px;
63
+ font-size: 18px;
64
+ border-radius: 5px;
65
+ cursor: pointer;
66
+ transition: all 0.3s;
67
+ }
68
+
69
+ button:hover {
70
+ background-color: #f39c12;
71
+ }
72
+
73
+ h1 {
74
+ font-size: 2.5em;
75
+ margin-bottom: 10px;
76
+ color: #f1c40f;
77
+ text-shadow: 0 0 10px rgba(241, 196, 15, 0.5);
78
+ }
79
+
80
+ h2 {
81
+ font-size: 1.8em;
82
+ margin-bottom: 20px;
83
+ }
84
+
85
+ #controls {
86
+ position: absolute;
87
+ bottom: 20px;
88
+ left: 20px;
89
+ z-index: 10;
90
+ background-color: rgba(0, 0, 0, 0.5);
91
+ padding: 10px;
92
+ border-radius: 5px;
93
+ }
94
+ </style>
95
+ </head>
96
+ <body>
97
+ <div id="game-container">
98
+ <div id="info">
99
+ <p>Speed: <span id="speed">0</span> km/h</p>
100
+ <p>Score: <span id="score">0</span></p>
101
+ <p>Time: <span id="time">0</span>s</p>
102
+ </div>
103
+
104
+ <div id="start-screen">
105
+ <h1>3D RACING GAME</h1>
106
+ <p>Race through the city streets and avoid obstacles!</p>
107
+ <p>Collect coins to increase your score.</p>
108
+ <button id="start-btn">START RACE</button>
109
+ </div>
110
+
111
+ <div id="game-over">
112
+ <h1>GAME OVER</h1>
113
+ <p>Final Score: <span id="final-score">0</span></p>
114
+ <p>Time Survived: <span id="final-time">0</span>s</p>
115
+ <button id="restart-btn">PLAY AGAIN</button>
116
+ </div>
117
+
118
+ <div id="controls">
119
+ <p>Controls: WASD or Arrow Keys to drive</p>
120
+ <p>Space to brake</p>
121
+ </div>
122
+ </div>
123
+
124
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
125
+ <script>
126
+ // Game variables
127
+ let scene, camera, renderer;
128
+ let car, roadSegments = [], coins = [], obstacles = [];
129
+ let isGameRunning = false;
130
+ let score = 0;
131
+ let gameTime = 0;
132
+ let carSpeed = 0;
133
+ let maxSpeed = 100;
134
+ let acceleration = 0.5;
135
+ let deceleration = 0.3;
136
+ let rotationSpeed = 0.05;
137
+ let keys = {};
138
+ let distanceTraveled = 0;
139
+
140
+ // Initialize the game
141
+ function init() {
142
+ // Set up scene
143
+ scene = new THREE.Scene();
144
+ scene.background = new THREE.Color(0x87CEEB); // Sky blue
145
+
146
+ // Set up camera
147
+ camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
148
+ camera.position.set(0, 10, 15);
149
+ camera.lookAt(0, 0, 0);
150
+
151
+ // Set up renderer
152
+ renderer = new THREE.WebGLRenderer({ antialias: true });
153
+ renderer.setSize(window.innerWidth, window.innerHeight);
154
+ renderer.shadowMap.enabled = true;
155
+ document.getElementById('game-container').prepend(renderer.domElement);
156
+
157
+ // Add lighting
158
+ const ambientLight = new THREE.AmbientLight(0x404040);
159
+ scene.add(ambientLight);
160
+
161
+ const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
162
+ directionalLight.position.set(1, 1, 1);
163
+ directionalLight.castShadow = true;
164
+ scene.add(directionalLight);
165
+
166
+ // Create initial road segments
167
+ createInitialRoad();
168
+
169
+ // Create car
170
+ createCar();
171
+
172
+ // Add event listeners
173
+ window.addEventListener('resize', onWindowResize);
174
+ window.addEventListener('keydown', onKeyDown);
175
+ window.addEventListener('keyup', onKeyUp);
176
+
177
+ // Start screen buttons
178
+ document.getElementById('start-btn').addEventListener('click', startGame);
179
+ document.getElementById('restart-btn').addEventListener('click', restartGame);
180
+
181
+ animate();
182
+ }
183
+
184
+ function createInitialRoad() {
185
+ const segmentLength = 50;
186
+ const segmentWidth = 20;
187
+
188
+ // Create 10 road segments ahead
189
+ for (let i = 0; i < 10; i++) {
190
+ createRoadSegment(i * segmentLength);
191
+ }
192
+ }
193
+
194
+ function createRoadSegment(zPosition) {
195
+ const segmentLength = 50;
196
+ const segmentWidth = 20;
197
+
198
+ // Road surface
199
+ const roadGeometry = new THREE.PlaneGeometry(segmentWidth, segmentLength);
200
+ roadGeometry.rotateX(-Math.PI / 2);
201
+ const roadMaterial = new THREE.MeshStandardMaterial({ color: 0x333333 });
202
+ const roadSegment = new THREE.Mesh(roadGeometry, roadMaterial);
203
+ roadSegment.position.z = zPosition;
204
+ roadSegment.receiveShadow = true;
205
+ scene.add(roadSegment);
206
+
207
+ // Road markings (center lines)
208
+ const lineLength = 3;
209
+ const lineGap = 3;
210
+ const lineCount = Math.floor(segmentLength / (lineLength + lineGap));
211
+
212
+ for (let i = 0; i < lineCount; i++) {
213
+ const lineGeometry = new THREE.BoxGeometry(0.5, 0.1, lineLength);
214
+ const lineMaterial = new THREE.MeshStandardMaterial({ color: 0xffff00 });
215
+ const line = new THREE.Mesh(lineGeometry, lineMaterial);
216
+ line.position.set(0, 0.1, zPosition - (segmentLength/2) + (i * (lineLength + lineGap)) + lineLength/2);
217
+ line.receiveShadow = true;
218
+ scene.add(line);
219
+ }
220
+
221
+ // Sidewalks
222
+ const sidewalkGeometry = new THREE.BoxGeometry(segmentWidth, 0.2, segmentLength);
223
+ const sidewalkMaterial = new THREE.MeshStandardMaterial({ color: 0x555555 });
224
+
225
+ const leftSidewalk = new THREE.Mesh(sidewalkGeometry, sidewalkMaterial);
226
+ leftSidewalk.position.set(0, 0.2, zPosition);
227
+ leftSidewalk.scale.set(1, 1, 1.02); // Slightly longer to avoid gaps
228
+ scene.add(leftSidewalk);
229
+
230
+ const rightSidewalk = new THREE.Mesh(sidewalkGeometry, sidewalkMaterial);
231
+ rightSidewalk.position.set(0, 0.2, zPosition);
232
+ rightSidewalk.scale.set(1, 1, 1.02); // Slightly longer to avoid gaps
233
+ scene.add(rightSidewalk);
234
+
235
+ // Buildings (randomly placed)
236
+ const buildingCount = 2 + Math.floor(Math.random() * 3);
237
+
238
+ for (let i = 0; i < buildingCount; i++) {
239
+ // Left side buildings
240
+ if (Math.random() > 0.3) {
241
+ const leftBuilding = createBuilding(4 + Math.random() * 4, 15 + Math.random() * 10, false);
242
+ leftBuilding.position.set(
243
+ -15 - Math.random() * 5,
244
+ leftBuilding.geometry.parameters.height / 2,
245
+ zPosition - (segmentLength/2) + Math.random() * segmentLength
246
+ );
247
+ scene.add(leftBuilding);
248
+ }
249
+
250
+ // Right side buildings
251
+ if (Math.random() > 0.3) {
252
+ const rightBuilding = createBuilding(4 + Math.random() * 4, 15 + Math.random() * 10, false);
253
+ rightBuilding.position.set(
254
+ 15 + Math.random() * 5,
255
+ rightBuilding.geometry.parameters.height / 2,
256
+ zPosition - (segmentLength/2) + Math.random() * segmentLength
257
+ );
258
+ scene.add(rightBuilding);
259
+ }
260
+ }
261
+
262
+ roadSegments.push({
263
+ mesh: roadSegment,
264
+ zStart: zPosition - segmentLength/2,
265
+ zEnd: zPosition + segmentLength/2,
266
+ leftSidewalk: leftSidewalk,
267
+ rightSidewalk: rightSidewalk
268
+ });
269
+ }
270
+
271
+ function createBuilding(width, height, hasWindows = true) {
272
+ const buildingGeometry = new THREE.BoxGeometry(width, height, width);
273
+ let buildingMaterial;
274
+
275
+ if (hasWindows) {
276
+ const colors = [0x3498db, 0x2ecc71, 0xe74c3c, 0xf39c12];
277
+ buildingMaterial = new THREE.MeshStandardMaterial({
278
+ color: colors[Math.floor(Math.random() * colors.length)],
279
+ roughness: 0.7,
280
+ metalness: 0.1
281
+ });
282
+ } else {
283
+ buildingMaterial = new THREE.MeshStandardMaterial({ color: 0x7f8c8d });
284
+ }
285
+
286
+ const building = new THREE.Mesh(buildingGeometry, buildingMaterial);
287
+ building.castShadow = true;
288
+ building.receiveShadow = true;
289
+ return building;
290
+ }
291
+
292
+ function createCar() {
293
+ const carGeometry = new THREE.BoxGeometry(2, 1, 3);
294
+ const carMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 });
295
+ car = new THREE.Mesh(carGeometry, carMaterial);
296
+ car.position.set(0, 1, 0);
297
+ car.castShadow = true;
298
+ car.receiveShadow = true;
299
+ scene.add(car);
300
+
301
+ // Add windows
302
+ const windowGeometry = new THREE.BoxGeometry(1.8, 0.8, 2.8);
303
+ const windowMaterial = new THREE.MeshStandardMaterial({
304
+ color: 0x000000,
305
+ transparent: true,
306
+ opacity: 0.5,
307
+ metalness: 0.7,
308
+ roughness: 0.2
309
+ });
310
+ const windows = new THREE.Mesh(windowGeometry, windowMaterial);
311
+ windows.position.y = 0.5;
312
+ car.add(windows);
313
+
314
+ // Add wheel positions (visual only)
315
+ const wheelGeometry = new THREE.CylinderGeometry(0.4, 0.4, 0.2, 16);
316
+ const wheelMaterial = new THREE.MeshStandardMaterial({ color: 0x333333 });
317
+
318
+ // Front wheels
319
+ const frontLeftWheel = new THREE.Mesh(wheelGeometry, wheelMaterial);
320
+ frontLeftWheel.rotation.z = Math.PI / 2;
321
+ frontLeftWheel.position.set(-1.2, 0.4, 1);
322
+ car.add(frontLeftWheel);
323
+
324
+ const frontRightWheel = new THREE.Mesh(wheelGeometry, wheelMaterial);
325
+ frontRightWheel.rotation.z = Math.PI / 2;
326
+ frontRightWheel.position.set(1.2, 0.4, 1);
327
+ car.add(frontRightWheel);
328
+
329
+ // Rear wheels
330
+ const rearLeftWheel = new THREE.Mesh(wheelGeometry, wheelMaterial);
331
+ rearLeftWheel.rotation.z = Math.PI / 2;
332
+ rearLeftWheel.position.set(-1.2, 0.4, -1);
333
+ car.add(rearLeftWheel);
334
+
335
+ const rearRightWheel = new THREE.Mesh(wheelGeometry, wheelMaterial);
336
+ rearRightWheel.rotation.z = Math.PI / 2;
337
+ rearRightWheel.position.set(1.2, 0.4, -1);
338
+ car.add(rearRightWheel);
339
+ }
340
+
341
+ function spawnCoins() {
342
+ if (!isGameRunning) return;
343
+
344
+ // Remove old coins that are behind the car
345
+ coins = coins.filter(coin => {
346
+ if (coin.position.z < car.position.z - 30) {
347
+ scene.remove(coin);
348
+ return false;
349
+ }
350
+ return true;
351
+ });
352
+
353
+ // Spawn new coins
354
+ if (Math.random() < 0.05 && coins.length < 30) {
355
+ const coinGeometry = new THREE.CylinderGeometry(0.5, 0.5, 0.1, 32);
356
+ const coinMaterial = new THREE.MeshStandardMaterial({
357
+ color: 0xf1c40f,
358
+ metalness: 0.9,
359
+ roughness: 0.2,
360
+ emissive: 0xf1c40f,
361
+ emissiveIntensity: 0.3
362
+ });
363
+
364
+ const coin = new THREE.Mesh(coinGeometry, coinMaterial);
365
+ coin.rotation.x = Math.PI / 2;
366
+ coin.position.set(
367
+ (Math.random() - 0.5) * 15, // Random x position (-7.5 to 7.5)
368
+ 1,
369
+ car.position.z + 50 + Math.random() * 100 // Ahead of the car
370
+ );
371
+ coin.castShadow = true;
372
+ coin.receiveShadow = true;
373
+ scene.add(coin);
374
+ coins.push(coin);
375
+ }
376
+ }
377
+
378
+ function spawnObstacles() {
379
+ if (!isGameRunning) return;
380
+
381
+ // Remove old obstacles that are behind the car
382
+ obstacles = obstacles.filter(obstacle => {
383
+ if (obstacle.position.z < car.position.z - 30) {
384
+ scene.remove(obstacle);
385
+ return false;
386
+ }
387
+ return true;
388
+ });
389
+
390
+ // Spawn new obstacles
391
+ if (Math.random() < 0.03 && obstacles.length < 20) {
392
+ const obstacleTypes = ['box', 'cone', 'cylinder'];
393
+ const type = obstacleTypes[Math.floor(Math.random() * obstacleTypes.length)];
394
+ let obstacle;
395
+
396
+ const colors = [0xe74c3c, 0x9b59b6, 0x3498db, 0x2ecc71];
397
+ const color = colors[Math.floor(Math.random() * colors.length)];
398
+
399
+ switch (type) {
400
+ case 'box':
401
+ obstacle = new THREE.Mesh(
402
+ new THREE.BoxGeometry(1.5, 1.5, 1.5),
403
+ new THREE.MeshStandardMaterial({ color })
404
+ );
405
+ break;
406
+ case 'cone':
407
+ obstacle = new THREE.Mesh(
408
+ new THREE.ConeGeometry(0.8, 2, 32),
409
+ new THREE.MeshStandardMaterial({ color })
410
+ );
411
+ break;
412
+ case 'cylinder':
413
+ obstacle = new THREE.Mesh(
414
+ new THREE.CylinderGeometry(0.8, 0.8, 1.5, 32),
415
+ new THREE.MeshStandardMaterial({ color })
416
+ );
417
+ break;
418
+ }
419
+
420
+ obstacle.castShadow = true;
421
+ obstacle.receiveShadow = true;
422
+ obstacle.position.set(
423
+ (Math.random() - 0.5) * 15, // Random x position (-7.5 to 7.5)
424
+ type === 'cone' ? 1 : 0.75,
425
+ car.position.z + 50 + Math.random() * 100 // Ahead of the car
426
+ );
427
+
428
+ scene.add(obstacle);
429
+ obstacles.push(obstacle);
430
+ }
431
+ }
432
+
433
+ function updateRoadSegments() {
434
+ // Remove segments that are behind the car
435
+ const segmentsToRemove = roadSegments.filter(segment => segment.zEnd < car.position.z - 50);
436
+
437
+ segmentsToRemove.forEach(segment => {
438
+ scene.remove(segment.mesh);
439
+ scene.remove(segment.leftSidewalk);
440
+ scene.remove(segment.rightSidewalk);
441
+ });
442
+
443
+ // Keep only segments that are still in use
444
+ roadSegments = roadSegments.filter(segment => segment.zEnd >= car.position.z - 50);
445
+
446
+ // Add new segments if needed
447
+ const lastSegment = roadSegments[roadSegments.length - 1];
448
+ if (!lastSegment || car.position.z + 200 > lastSegment.zEnd) {
449
+ createRoadSegment(lastSegment ? lastSegment.zEnd + 50 : 50);
450
+ }
451
+ }
452
+
453
+ function checkCollisions() {
454
+ if (!isGameRunning) return;
455
+
456
+ // Check coin collisions
457
+ coins.forEach((coin, index) => {
458
+ if (isColliding(car, coin)) {
459
+ scene.remove(coin);
460
+ coins.splice(index, 1);
461
+ score += 10;
462
+ document.getElementById('score').textContent = score;
463
+ }
464
+ });
465
+
466
+ // Check obstacle collisions
467
+ obstacles.forEach((obstacle, index) => {
468
+ if (isColliding(car, obstacle)) {
469
+ gameOver();
470
+ }
471
+ });
472
+
473
+ // Check road boundaries
474
+ if (car.position.x < -9 || car.position.x > 9) {
475
+ gameOver();
476
+ }
477
+ }
478
+
479
+ function isColliding(obj1, obj2) {
480
+ const dx = obj1.position.x - obj2.position.x;
481
+ const dy = obj1.position.y - obj2.position.y;
482
+ const dz = obj1.position.z - obj2.position.z;
483
+ const distance = Math.sqrt(dx * dx + dy * dy + dz * dz);
484
+ return distance < 2;
485
+ }
486
+
487
+ function updateCarMovement() {
488
+ if (!isGameRunning) return;
489
+
490
+ // Acceleration/deceleration
491
+ if (keys['ArrowUp'] || keys['w']) {
492
+ carSpeed += acceleration;
493
+ } else if (keys['ArrowDown'] || keys['s']) {
494
+ carSpeed -= deceleration * 1.5; // Stronger deceleration when reversing
495
+ } else if (keys[' ']) {
496
+ // Braking
497
+ if (carSpeed > 0) {
498
+ carSpeed -= deceleration * 2;
499
+ if (carSpeed < 0) carSpeed = 0;
500
+ } else if (carSpeed < 0) {
501
+ carSpeed += deceleration * 2;
502
+ if (carSpeed > 0) carSpeed = 0;
503
+ }
504
+ } else {
505
+ // Natural deceleration
506
+ if (carSpeed > 0) {
507
+ carSpeed -= deceleration * 0.5;
508
+ if (carSpeed < 0) carSpeed = 0;
509
+ } else if (carSpeed < 0) {
510
+ carSpeed += deceleration * 0.5;
511
+ if (carSpeed > 0) carSpeed = 0;
512
+ }
513
+ }
514
+
515
+ // Limit speed
516
+ carSpeed = Math.max(-maxSpeed / 2, Math.min(carSpeed, maxSpeed));
517
+ document.getElementById('speed').textContent = Math.abs(Math.round(carSpeed));
518
+
519
+ // Update car position based on speed
520
+ const deltaZ = carSpeed * 0.05;
521
+ car.position.z += deltaZ;
522
+ distanceTraveled += deltaZ;
523
+
524
+ // Rotation control
525
+ if ((keys['ArrowLeft'] || keys['a']) && carSpeed !== 0) {
526
+ car.rotation.y += rotationSpeed * (carSpeed > 0 ? 1 : -1) * 0.05;
527
+ }
528
+ if ((keys['ArrowRight'] || keys['d']) && carSpeed !== 0) {
529
+ car.rotation.y -= rotationSpeed * (carSpeed > 0 ? 1 : -1) * 0.05;
530
+ }
531
+
532
+ // Lateral movement based on car rotation
533
+ if (Math.abs(carSpeed) > 0.1) {
534
+ const moveX = Math.sin(car.rotation.y) * (carSpeed * 0.02);
535
+ car.position.x += moveX;
536
+ }
537
+ }
538
+
539
+ function updateCamera() {
540
+ // Chase camera
541
+ const carDirection = new THREE.Vector3(
542
+ Math.sin(car.rotation.y),
543
+ 0,
544
+ Math.cos(car.rotation.y)
545
+ ).normalize();
546
+
547
+ const cameraOffset = new THREE.Vector3(
548
+ -carDirection.x * 5,
549
+ 3 + (carSpeed / maxSpeed * 2), // Camera lifts slightly at high speed
550
+ -carDirection.z * 5 - (carSpeed / maxSpeed * 2) // Camera pulls back slightly at high speed
551
+ );
552
+
553
+ camera.position.set(
554
+ car.position.x + cameraOffset.x,
555
+ car.position.y + cameraOffset.y,
556
+ car.position.z + cameraOffset.z
557
+ );
558
+
559
+ // Smooth camera look-at
560
+ const lookAhead = new THREE.Vector3(
561
+ car.position.x + carDirection.x * 5,
562
+ car.position.y,
563
+ car.position.z + carDirection.z * 5
564
+ );
565
+
566
+ camera.lookAt(lookAhead);
567
+ }
568
+
569
+ function startGame() {
570
+ document.getElementById('start-screen').style.display = 'none';
571
+ isGameRunning = true;
572
+ score = 0;
573
+ gameTime = 0;
574
+ carSpeed = 0;
575
+ car.position.set(0, 1, 0);
576
+ car.rotation.set(0, 0, 0);
577
+ distanceTraveled = 0;
578
+
579
+ // Remove all coins and obstacles
580
+ coins.forEach(coin => scene.remove(coin));
581
+ coins = [];
582
+ obstacles.forEach(obstacle => scene.remove(obstacle));
583
+ obstacles = [];
584
+
585
+ // Reset road segments
586
+ roadSegments.forEach(segment => {
587
+ scene.remove(segment.mesh);
588
+ scene.remove(segment.leftSidewalk);
589
+ scene.remove(segment.rightSidewalk);
590
+ });
591
+ roadSegments = [];
592
+ createInitialRoad();
593
+
594
+ // Update UI
595
+ document.getElementById('score').textContent = score;
596
+ document.getElementById('time').textContent = gameTime.toFixed(1);
597
+ }
598
+
599
+ function restartGame() {
600
+ document.getElementById('game-over').style.display = 'none';
601
+ startGame();
602
+ }
603
+
604
+ function gameOver() {
605
+ isGameRunning = false;
606
+ carSpeed = 0;
607
+ document.getElementById('final-score').textContent = score;
608
+ document.getElementById('final-time').textContent = gameTime.toFixed(1);
609
+ document.getElementById('game-over').style.display = 'block';
610
+ }
611
+
612
+ function onWindowResize() {
613
+ camera.aspect = window.innerWidth / window.innerHeight;
614
+ camera.updateProjectionMatrix();
615
+ renderer.setSize(window.innerWidth, window.innerHeight);
616
+ }
617
+
618
+ function onKeyDown(event) {
619
+ keys[event.key.toLowerCase()] = true;
620
+ }
621
+
622
+ function onKeyUp(event) {
623
+ keys[event.key.toLowerCase()] = false;
624
+ }
625
+
626
+ function animate() {
627
+ requestAnimationFrame(animate);
628
+
629
+ if (isGameRunning) {
630
+ gameTime += 0.1;
631
+ document.getElementById('time').textContent = gameTime.toFixed(1);
632
+
633
+ updateCarMovement();
634
+ updateRoadSegments();
635
+ spawnCoins();
636
+ spawnObstacles();
637
+ checkCollisions();
638
+ updateCamera();
639
+ }
640
+
641
+ renderer.render(scene, camera);
642
+ }
643
+
644
+ // Start the game
645
+ init();
646
+ </script>
647
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <a href="https://enzostvs-deepsite.hf.space" style="color: #fff;" target="_blank" >DeepSite</a> <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;"></p></body>
648
+ </html>