Tim13ekd commited on
Commit
c97ce13
·
verified ·
1 Parent(s): 051d912

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +896 -842
index.html CHANGED
@@ -1,857 +1,911 @@
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, maximum-scale=1.0, user-scalable=no">
6
- <title>Neon Drift: 3D Physics Racer</title>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
- <!-- Import FontAwesome for Icons -->
9
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
10
-
11
- <style>
12
- :root {
13
- --primary-color: #00f3ff;
14
- --secondary-color: #ff00ff;
15
- --bg-dark: #0a0a12;
16
- --glass-bg: rgba(20, 20, 30, 0.7);
17
- --glass-border: rgba(255, 255, 255, 0.1);
18
- --text-color: #ffffff;
19
- --font-main: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
20
- }
21
-
22
- * {
23
- box-sizing: border-box;
24
- user-select: none;
25
- -webkit-user-select: none;
26
- touch-action: none; /* Prevent scrolling on mobile */
27
- }
28
-
29
- body {
30
- margin: 0;
31
- padding: 0;
32
- overflow: hidden;
33
- background-color: var(--bg-dark);
34
- font-family: var(--font-main);
35
- color: var(--text-color);
36
- }
37
-
38
- /* --- Canvas Container --- */
39
- #game-container {
40
- position: absolute;
41
- top: 0;
42
- left: 0;
43
- width: 100vw;
44
- height: 100vh;
45
- z-index: 1;
46
- }
47
-
48
- /* --- UI Overlay Layer --- */
49
- #ui-layer {
50
- position: absolute;
51
- top: 0;
52
- left: 0;
53
- width: 100%;
54
- height: 100%;
55
- z-index: 10;
56
- pointer-events: none; /* Let clicks pass through to controls/canvas */
57
- display: flex;
58
- flex-direction: column;
59
- justify-content: space-between;
60
- }
61
-
62
- /* --- Header / Top Bar --- */
63
- .top-bar {
64
- display: flex;
65
- justify-content: space-between;
66
- align-items: center;
67
- padding: 15px 20px;
68
- background: linear-gradient(to bottom, rgba(0,0,0,0.8), transparent);
69
- pointer-events: auto;
70
- }
71
-
72
- .brand {
73
- font-size: 1.2rem;
74
- font-weight: 800;
75
- text-transform: uppercase;
76
- letter-spacing: 2px;
77
- text-shadow: 0 0 10px var(--primary-color);
78
- }
79
-
80
- .brand a {
81
- color: var(--primary-color);
82
- text-decoration: none;
83
- transition: color 0.3s;
84
- }
85
- .brand a:hover {
86
- color: var(--secondary-color);
87
- }
88
-
89
- .stats {
90
- display: flex;
91
- gap: 20px;
92
- font-family: 'Courier New', monospace;
93
- font-weight: bold;
94
- }
95
-
96
- .stat-box {
97
- background: var(--glass-bg);
98
- border: 1px solid var(--glass-border);
99
- padding: 8px 15px;
100
- border-radius: 8px;
101
- backdrop-filter: blur(5px);
102
- box-shadow: 0 4px 6px rgba(0,0,0,0.3);
103
- }
104
-
105
- .stat-label {
106
- font-size: 0.7rem;
107
- color: #aaa;
108
- display: block;
109
- }
110
-
111
- .stat-value {
112
- font-size: 1.2rem;
113
- color: var(--primary-color);
114
- }
115
-
116
- /* --- Main Menu (Start Screen) --- */
117
- #start-screen {
118
- position: absolute;
119
- top: 0;
120
- left: 0;
121
- width: 100%;
122
- height: 100%;
123
- background: rgba(10, 10, 18, 0.85);
124
- backdrop-filter: blur(10px);
125
- display: flex;
126
- flex-direction: column;
127
- justify-content: center;
128
- align-items: center;
129
- z-index: 20;
130
- pointer-events: auto;
131
- transition: opacity 0.5s ease;
132
- }
133
-
134
- #start-screen.hidden {
135
- opacity: 0;
136
- pointer-events: none;
137
- }
138
-
139
- h1 {
140
- font-size: 4rem;
141
- margin-bottom: 0.5rem;
142
- background: linear-gradient(45deg, var(--primary-color), var(--secondary-color));
143
- -webkit-background-clip: text;
144
- -webkit-text-fill-color: transparent;
145
- text-shadow: 0 0 20px rgba(0, 243, 255, 0.3);
146
- text-align: center;
147
- }
148
-
149
- p.subtitle {
150
- font-size: 1.2rem;
151
- color: #ccc;
152
- margin-bottom: 3rem;
153
- text-align: center;
154
- max-width: 600px;
155
- line-height: 1.5;
156
- }
157
-
158
- .btn {
159
- background: linear-gradient(90deg, var(--primary-color), #00a8ff);
160
- border: none;
161
- padding: 15px 40px;
162
- color: #000;
163
- font-size: 1.2rem;
164
- font-weight: bold;
165
- border-radius: 50px;
166
- cursor: pointer;
167
- transition: transform 0.2s, box-shadow 0.2s;
168
- box-shadow: 0 0 15px var(--primary-color);
169
- text-transform: uppercase;
170
- }
171
-
172
- .btn:hover {
173
- transform: scale(1.05);
174
- box-shadow: 0 0 25px var(--secondary-color);
175
- }
176
-
177
- .controls-info {
178
- margin-top: 40px;
179
- display: grid;
180
- grid-template-columns: repeat(3, 1fr);
181
- gap: 20px;
182
- text-align: center;
183
- }
184
-
185
- .control-item {
186
- display: flex;
187
- flex-direction: column;
188
- align-items: center;
189
- color: #888;
190
- }
191
-
192
- .control-item i {
193
- font-size: 1.5rem;
194
- margin-bottom: 10px;
195
- color: var(--text-color);
196
- }
197
-
198
- /* --- Mobile Controls --- */
199
- #mobile-controls {
200
- display: none; /* Hidden on desktop by default */
201
- width: 100%;
202
- height: 100%;
203
- position: absolute;
204
- top: 0;
205
- left: 0;
206
- pointer-events: none;
207
- padding: 20px;
208
- padding-bottom: 40px;
209
- }
210
-
211
- .touch-zone {
212
- pointer-events: auto;
213
- position: absolute;
214
- bottom: 20px;
215
- display: flex;
216
- gap: 15px;
217
- }
218
-
219
- .d-pad {
220
- left: 20px;
221
- display: flex;
222
- flex-direction: column;
223
- align-items: center;
224
- gap: 10px;
225
- }
226
-
227
- .pedals {
228
- right: 20px;
229
- display: flex;
230
- flex-direction: row-reverse; /* Brake on left, Gas on right relative to hand */
231
- gap: 15px;
232
- }
233
-
234
- .touch-btn {
235
- width: 70px;
236
- height: 70px;
237
- background: rgba(255, 255, 255, 0.1);
238
- border: 2px solid rgba(255, 255, 255, 0.2);
239
- border-radius: 50%;
240
- color: white;
241
- font-size: 1.5rem;
242
- display: flex;
243
- justify-content: center;
244
- align-items: center;
245
- backdrop-filter: blur(4px);
246
- transition: background 0.1s, transform 0.1s;
247
- }
248
-
249
- .touch-btn:active, .touch-btn.active {
250
- background: rgba(0, 243, 255, 0.3);
251
- transform: scale(0.95);
252
- border-color: var(--primary-color);
253
- }
254
-
255
- .touch-btn.brake:active, .touch-btn.brake.active {
256
- background: rgba(255, 0, 100, 0.3);
257
- border-color: var(--secondary-color);
258
- }
259
-
260
- /* --- Reset Button --- */
261
- #reset-btn {
262
- position: absolute;
263
- bottom: 20px;
264
- left: 50%;
265
- transform: translateX(-50%);
266
- background: rgba(0,0,0,0.6);
267
- border: 1px solid white;
268
- color: white;
269
- padding: 10px 20px;
270
- border-radius: 20px;
271
- pointer-events: auto;
272
- cursor: pointer;
273
- font-size: 0.9rem;
274
- display: none;
275
- }
276
-
277
- #reset-btn:hover {
278
- background: var(--secondary-color);
279
- border-color: var(--secondary-color);
280
- }
281
-
282
- /* --- Media Queries --- */
283
- @media (max-width: 768px) {
284
- h1 { font-size: 2.5rem; }
285
- .controls-info { display: none; } /* Hide keyboard info on mobile */
286
- #mobile-controls { display: block; }
287
- #reset-btn { display: block; }
288
- }
289
- </style>
290
 
291
- <!-- Three.js & Cannon-es via Import Map -->
292
- <script type="importmap">
293
- {
294
- "imports": {
295
- "three": "https://unpkg.com/three@0.160.0/build/three.module.js",
296
- "three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/",
297
- "cannon-es": "https://unpkg.com/cannon-es@0.20.0/dist/cannon-es.js"
298
- }
299
- }
300
- </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
301
  </head>
 
302
  <body>
303
 
304
- <!-- Game Canvas -->
305
- <div id="game-container"></div>
306
-
307
- <!-- UI Overlay -->
308
- <div id="ui-layer">
309
- <div class="top-bar">
310
- <div class="brand">
311
- <i class="fa-solid fa-gamepad"></i> Neon Drift
312
- <span style="font-size: 0.7em; opacity: 0.7; margin-left: 10px;">| Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank">anycoder</a></span>
313
- </div>
314
- <div class="stats">
315
- <div class="stat-box">
316
- <span class="stat-label">SPEED</span>
317
- <span class="stat-value" id="speed-display">0 <small>km/h</small></span>
318
- </div>
319
- </div>
320
  </div>
321
-
322
- <div id="reset-btn" onclick="resetCar()">
323
- <i class="fa-solid fa-rotate-right"></i> Flip Car
324
  </div>
 
 
325
 
326
- <!-- Mobile Controls -->
327
- <div id="mobile-controls">
328
- <div class="touch-zone d-pad">
329
- <div class="touch-btn" id="btn-left"><i class="fa-solid fa-arrow-left"></i></div>
330
- <div class="touch-btn" id="btn-right"><i class="fa-solid fa-arrow-right"></i></div>
331
- </div>
332
- <div class="touch-zone pedals">
333
- <div class="touch-btn" id="btn-brake" style="border-color: #ff6666;"><i class="fa-solid fa-stop"></i></div>
334
- <div class="touch-btn" id="btn-gas"><i class="fa-solid fa-gas-pump"></i></div>
335
- </div>
336
- </div>
337
  </div>
338
 
339
- <!-- Start Screen -->
340
- <div id="start-screen">
341
- <h1>NEON DRIFT</h1>
342
- <p class="subtitle">Physics-based driving experience. Jump ramps, drift, and explore the cyber-grid.</p>
343
-
344
- <button class="btn" id="start-btn">Start Engine</button>
345
-
346
- <div class="controls-info">
347
- <div class="control-item">
348
- <i class="fa-solid fa-keyboard"></i>
349
- <span>WASD / Arrows</span>
350
- </div>
351
- <div class="control-item">
352
- <i class="fa-solid fa-gamepad"></i>
353
- <span>Controller</span>
354
- </div>
355
- <div class="control-item">
356
- <i class="fa-solid fa-mobile-screen"></i>
357
- <span>Touch</span>
358
- </div>
359
- </div>
360
  </div>
361
 
362
- <script type="module">
363
- import * as THREE from 'three';
364
- import * as CANNON from 'cannon-es';
365
-
366
- // --- Configuration ---
367
- const CONFIG = {
368
- shadows: true,
369
- maxSteerVal: 0.5,
370
- maxForce: 1000,
371
- brakeForce: 20,
372
- colors: {
373
- bg: 0x0a0a12,
374
- grid: 0x00f3ff,
375
- car: 0xff00ff,
376
- obstacle: 0x222222
377
- }
378
- };
379
-
380
- // --- Globals ---
381
- let scene, camera, renderer, world;
382
- let vehicle, chassisBody, wheelBodies = [];
383
- let meshHelper;
384
- let lastTime;
385
- let isGameActive = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
386
 
387
- // Input State
388
- const input = {
389
- forward: false,
390
- backward: false,
391
- left: false,
392
- right: false,
393
- brake: false
394
- };
395
-
396
- // --- Initialization ---
397
- function init() {
398
- // 1. Setup Three.js Scene
399
- scene = new THREE.Scene();
400
- scene.background = new THREE.Color(CONFIG.colors.bg);
401
- scene.fog = new THREE.FogExp2(CONFIG.colors.bg, 0.015);
402
-
403
- // 2. Camera
404
- camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
405
-
406
- // 3. Renderer
407
- renderer = new THREE.WebGLRenderer({ antialias: true });
408
- renderer.setSize(window.innerWidth, window.innerHeight);
409
- renderer.shadowMap.enabled = true;
410
- renderer.shadowMap.type = THREE.PCFSoftShadowMap;
411
- document.getElementById('game-container').appendChild(renderer.domElement);
412
-
413
- // 4. Lights
414
- const ambientLight = new THREE.AmbientLight(0x404040, 1.5); // Soft white light
415
- scene.add(ambientLight);
416
-
417
- const dirLight = new THREE.DirectionalLight(0xffffff, 1);
418
- dirLight.position.set(50, 100, 50);
419
- dirLight.castShadow = true;
420
- dirLight.shadow.mapSize.width = 2048;
421
- dirLight.shadow.mapSize.height = 2048;
422
- dirLight.shadow.camera.near = 0.5;
423
- dirLight.shadow.camera.far = 500;
424
- dirLight.shadow.camera.left = -50;
425
- dirLight.shadow.camera.right = 50;
426
- dirLight.shadow.camera.top = 50;
427
- dirLight.shadow.camera.bottom = -50;
428
- scene.add(dirLight);
429
-
430
- // Neon Point Lights
431
- const pLight1 = new THREE.PointLight(CONFIG.colors.grid, 2, 50);
432
- pLight1.position.set(0, 5, 0);
433
- scene.add(pLight1);
434
-
435
- // 5. Physics World
436
- world = new CANNON.World();
437
- world.gravity.set(0, -9.82, 0);
438
- world.broadphase = new CANNON.SAPBroadphase(world);
439
-
440
- // Materials
441
- const groundMat = new CANNON.Material();
442
- const wheelMat = new CANNON.Material();
443
- const wheelGroundContact = new CANNON.ContactMaterial(wheelMat, groundMat, {
444
- friction: 0.3,
445
- restitution: 0,
446
- contactEquationStiffness: 1000
447
- });
448
- world.addContactMaterial(wheelGroundContact);
449
-
450
- // 6. Create Environment
451
- createEnvironment(groundMat);
452
-
453
- // 7. Create Car
454
- createCar(wheelMat);
455
-
456
- // 8. Event Listeners
457
- window.addEventListener('resize', onWindowResize);
458
- setupInputs();
459
-
460
- // Start Loop
461
- lastTime = performance.now();
462
- requestAnimationFrame(animate);
463
- }
464
-
465
- // --- Environment Generation ---
466
- function createEnvironment(material) {
467
- // Floor
468
- const groundGeo = new THREE.PlaneGeometry(500, 500);
469
- const groundTex = createGridTexture();
470
- groundTex.wrapS = THREE.RepeatWrapping;
471
- groundTex.wrapT = THREE.RepeatWrapping;
472
- groundTex.repeat.set(100, 100);
473
-
474
- const groundMesh = new THREE.Mesh(
475
- groundGeo,
476
- new THREE.MeshStandardMaterial({
477
- map: groundTex,
478
- roughness: 0.8,
479
- metalness: 0.2
480
- })
481
- );
482
- groundMesh.rotation.x = -Math.PI / 2;
483
- groundMesh.receiveShadow = true;
484
- scene.add(groundMesh);
485
-
486
- // Physics Ground
487
- const groundBody = new CANNON.Body({
488
- mass: 0, // static
489
- shape: new CANNON.Plane(),
490
- material: material
491
- });
492
- groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0);
493
- world.addBody(groundBody);
494
-
495
- // Obstacles & Ramps
496
- createObstacle(10, 1, 10, -20, 0.5, -20); // Box
497
- createObstacle(2, 1, 8, 15, 0.5, -30); // Wall
498
- createRamp(8, 2, 10, -15, 1, 15); // Ramp
499
- createRamp(8, 2, 10, 20, 1, 30); // Ramp 2
500
- }
501
-
502
- function createGridTexture() {
503
- const canvas = document.createElement('canvas');
504
- canvas.width = 512;
505
- canvas.height = 512;
506
- const ctx = canvas.getContext('2d');
507
-
508
- // Background
509
- ctx.fillStyle = '#0a0a12';
510
- ctx.fillRect(0, 0, 512, 512);
511
-
512
- // Grid Lines
513
- ctx.strokeStyle = '#00f3ff';
514
- ctx.lineWidth = 2;
515
-
516
- // Glow effect
517
- ctx.shadowBlur = 10;
518
- ctx.shadowColor = '#00f3ff';
519
-
520
- ctx.beginPath();
521
- // Vertical lines
522
- for(let i=0; i<=512; i+=64) {
523
- ctx.moveTo(i, 0);
524
- ctx.lineTo(i, 512);
525
- }
526
- // Horizontal lines
527
- for(let i=0; i<=512; i+=64) {
528
- ctx.moveTo(0, i);
529
- ctx.lineTo(512, i);
530
- }
531
- ctx.stroke();
532
-
533
- const tex = new THREE.CanvasTexture(canvas);
534
- tex.anisotropy = 16;
535
- return tex;
536
- }
537
-
538
- function createObstacle(w, h, d, x, y, z) {
539
- // Visual
540
- const geo = new THREE.BoxGeometry(w, h, d);
541
- const mat = new THREE.MeshStandardMaterial({ color: 0x333333, roughness: 0.1 });
542
- const mesh = new THREE.Mesh(geo, mat);
543
- mesh.position.set(x, y, z);
544
- mesh.castShadow = true;
545
- mesh.receiveShadow = true;
546
- scene.add(mesh);
547
-
548
- // Neon Edges
549
- const edges = new THREE.EdgesGeometry(geo);
550
- const line = new THREE.LineSegments(edges, new THREE.LineBasicMaterial({ color: 0xff00ff }));
551
- mesh.add(line);
552
-
553
- // Physics
554
- const shape = new CANNON.Box(new CANNON.Vec3(w/2, h/2, d/2));
555
- const body = new CANNON.Body({ mass: 0 });
556
- body.addShape(shape);
557
- body.position.set(x, y, z);
558
- world.addBody(body);
559
- }
560
-
561
- function createRamp(w, h, d, x, y, z) {
562
- // Visual
563
- const geo = new THREE.BoxGeometry(w, h, d);
564
- const mat = new THREE.MeshStandardMaterial({ color: 0x222222 });
565
- const mesh = new THREE.Mesh(geo, mat);
566
- mesh.position.set(x, y, z);
567
- mesh.rotation.x = -0.2; // Tilt
568
- mesh.castShadow = true;
569
- mesh.receiveShadow = true;
570
- scene.add(mesh);
571
-
572
- // Physics
573
- const shape = new CANNON.Box(new CANNON.Vec3(w/2, h/2, d/2));
574
- const body = new CANNON.Body({ mass: 0 });
575
- body.addShape(shape);
576
- body.position.set(x, y, z);
577
- body.quaternion.setFromEuler(-0.2, 0, 0);
578
- world.addBody(body);
579
- }
580
-
581
- // --- Car Creation ---
582
- function createCar(material) {
583
- // Chassis Dimensions
584
- const chassisWidth = 1.8;
585
- const chassisHeight = 0.8;
586
- const chassisDepth = 4;
587
- const mass = 150;
588
-
589
- // Visual Chassis
590
- const chassisGeo = new THREE.BoxGeometry(chassisWidth, chassisHeight, chassisDepth);
591
- const chassisMat = new THREE.MeshStandardMaterial({ color: 0x111111, roughness: 0.2, metalness: 0.8 });
592
- const chassisMesh = new THREE.Mesh(chassisGeo, chassisMat);
593
- chassisMesh.castShadow = true;
594
-
595
- // Add glowing details to car
596
- const cabinGeo = new THREE.BoxGeometry(1.4, 0.6, 2);
597
- const cabinMat = new THREE.MeshStandardMaterial({ color: CONFIG.colors.car, emissive: CONFIG.colors.secondary, emissiveIntensity: 0.5 });
598
- const cabinMesh = new THREE.Mesh(cabinGeo, cabinMat);
599
- cabinMesh.position.y = 0.5;
600
- cabinMesh.position.z = -0.2;
601
- chassisMesh.add(cabinMesh);
602
-
603
- // Tail lights
604
- const tailGeo = new THREE.BoxGeometry(0.6, 0.2, 0.1);
605
- const tailMat = new THREE.MeshBasicMaterial({ color: 0xff0000 });
606
- const tl = new THREE.Mesh(tailGeo, tailMat);
607
- tl.position.set(-0.5, 0, 2);
608
- const tr = new THREE.Mesh(tailGeo, tailMat);
609
- tr.position.set(0.5, 0, 2);
610
- chassisMesh.add(tl);
611
- chassisMesh.add(tr);
612
-
613
- scene.add(chassisMesh);
614
-
615
- // Physics Chassis
616
- const chassisShape = new CANNON.Box(new CANNON.Vec3(chassisWidth/2, chassisHeight/2, chassisDepth/2));
617
- chassisBody = new CANNON.Body({ mass: mass, material: material });
618
- chassisBody.addShape(chassisShape);
619
- chassisBody.position.set(0, 4, 0); // Drop from sky
620
- chassisBody.angularDamping = 0.5;
621
- world.addBody(chassisBody);
622
-
623
- // Vehicle Setup
624
- vehicle = new CANNON.RaycastVehicle({
625
- chassisBody: chassisBody,
626
- });
627
-
628
- const wheelOptions = {
629
- radius: 0.5,
630
- directionLocal: new CANNON.Vec3(0, -1, 0),
631
- suspensionStiffness: 30,
632
- suspensionRestLength: 0.3,
633
- frictionSlip: 1.4,
634
- dampingRelaxation: 2.3,
635
- dampingCompression: 4.4,
636
- maxSuspensionForce: 100000,
637
- rollInfluence: 0.01,
638
- axleLocal: new CANNON.Vec3(-1, 0, 0),
639
- chassisConnectionPointLocal: new CANNON.Vec3(1, 1, 0),
640
- maxSuspensionTravel: 0.3,
641
- customSlidingRotationalSpeed: -30,
642
- useCustomSlidingRotationalSpeed: true
643
- };
644
-
645
- // Add wheels
646
- const axleWidth = 1.3;
647
- const frontPos = 1.3;
648
- const rearPos = -1.2;
649
- const wheelHeight = -0.5;
650
-
651
- // Front Left
652
- wheelOptions.chassisConnectionPointLocal.set(axleWidth, wheelHeight, frontPos);
653
- vehicle.addWheel(wheelOptions);
654
-
655
- // Front Right
656
- wheelOptions.chassisConnectionPointLocal.set(-axleWidth, wheelHeight, frontPos);
657
- vehicle.addWheel(wheelOptions);
658
-
659
- // Rear Left
660
- wheelOptions.chassisConnectionPointLocal.set(axleWidth, wheelHeight, rearPos);
661
- vehicle.addWheel(wheelOptions);
662
-
663
- // Rear Right
664
- wheelOptions.chassisConnectionPointLocal.set(-axleWidth, wheelHeight, rearPos);
665
- vehicle.addWheel(wheelOptions);
666
-
667
- vehicle.addToWorld(world);
668
-
669
- // Wheel Visuals
670
- const wheelGeo = new THREE.CylinderGeometry(wheelOptions.radius, wheelOptions.radius, wheelOptions.radius / 2, 24);
671
- wheelGeo.rotateZ(Math.PI / 2);
672
- const wheelMeshMat = new THREE.MeshStandardMaterial({ color: 0x333333 });
673
- const rimMat = new THREE.MeshBasicMaterial({ color: CONFIG.colors.primary });
674
-
675
- vehicle.wheelInfos.forEach((wheel) => {
676
- const cylinder = new THREE.Mesh(wheelGeo, wheelMeshMat);
677
- cylinder.castShadow = true;
678
-
679
- // Add Rim visual
680
- const rim = new THREE.Mesh(new THREE.BoxGeometry(0.1, 0.8, 0.8), rimMat);
681
- rim.rotation.x = Math.PI/2;
682
- cylinder.add(rim);
683
-
684
- scene.add(cylinder);
685
- wheelBodies.push(cylinder);
686
- });
687
-
688
- // Sync Chassis Mesh with Body
689
- chassisBody.visualMesh = chassisMesh;
690
- }
691
-
692
- // --- Inputs Handling ---
693
- function setupInputs() {
694
- // Keyboard
695
- document.addEventListener('keydown', (e) => {
696
- switch(e.key.toLowerCase()) {
697
- case 'w': case 'arrowup': input.forward = true; break;
698
- case 's': case 'arrowdown': input.backward = true; break;
699
- case 'a': case 'arrowleft': input.left = true; break;
700
- case 'd': case 'arrowright': input.right = true; break;
701
- case ' ': input.brake = true; break;
702
- }
703
- });
704
-
705
- document.addEventListener('keyup', (e) => {
706
- switch(e.key.toLowerCase()) {
707
- case 'w': case 'arrowup': input.forward = false; break;
708
- case 's': case 'arrowdown': input.backward = false; break;
709
- case 'a': case 'arrowleft': input.left = false; break;
710
- case 'd': case 'arrowright': input.right = false; break;
711
- case ' ': input.brake = false; break;
712
- }
713
- });
714
-
715
- // Touch (Mobile)
716
- const addTouch = (id, key) => {
717
- const el = document.getElementById(id);
718
- el.addEventListener('touchstart', (e) => { e.preventDefault(); input[key] = true; el.classList.add('active'); });
719
- el.addEventListener('touchend', (e) => { e.preventDefault(); input[key] = false; el.classList.remove('active'); });
720
- };
721
-
722
- addTouch('btn-gas', 'forward');
723
- addTouch('btn-brake', 'backward'); // Using backward for brake/reverse logic
724
- addTouch('btn-left', 'left');
725
- addTouch('btn-right', 'right');
726
-
727
- // Start Button
728
- document.getElementById('start-btn').addEventListener('click', () => {
729
- document.getElementById('start-screen').classList.add('hidden');
730
- isGameActive = true;
731
- });
732
- }
733
-
734
- function handleGamepad() {
735
- const gamepads = navigator.getGamepads();
736
- if (gamepads[0]) {
737
- const gp = gamepads[0];
738
-
739
- // Deadzone check
740
- const deadzone = 0.1;
741
-
742
- // Left Stick Axis 0 (Steering) or D-Pad Axis 0
743
- let steerAxis = gp.axes[0];
744
- if (Math.abs(steerAxis) < deadzone) steerAxis = 0;
745
-
746
- if (steerAxis < -0.1) { input.left = true; input.right = false; }
747
- else if (steerAxis > 0.1) { input.left = false; input.right = true; }
748
- else { input.left = false; input.right = false; }
749
-
750
- // Triggers or Buttons for Gas/Brake
751
- // Button 0 (A/Cross), Button 1 (B/Circle)
752
- input.forward = gp.buttons[0].pressed;
753
- input.backward = gp.buttons[1].pressed || gp.buttons[2].pressed; // B or X
754
- }
755
- }
756
-
757
- // --- Game Logic ---
758
- function updatePhysics() {
759
- if (!vehicle) return;
760
-
761
- // Steering
762
- const steer = input.left ? CONFIG.maxSteerVal : (input.right ? -CONFIG.maxSteerVal : 0);
763
- vehicle.setSteeringValue(steer, 0);
764
- vehicle.setSteeringValue(steer, 1);
765
-
766
- // Engine Force
767
- let force = 0;
768
- if (input.forward) force = -CONFIG.maxForce;
769
- if (input.backward) force = CONFIG.maxForce / 2; // Reverse is slower
770
-
771
- vehicle.applyEngineForce(force, 2);
772
- vehicle.applyEngineForce(force, 3);
773
-
774
- // Brakes
775
- const brakeForce = input.brake ? CONFIG.brakeForce : 0;
776
- vehicle.setBrake(brakeForce, 0);
777
- vehicle.setBrake(brakeForce, 1);
778
- vehicle.setBrake(brakeForce, 2);
779
- vehicle.setBrake(brakeForce, 3);
780
-
781
- // Update Physics World
782
- world.step(1 / 60);
783
-
784
- // Sync Visuals
785
- for (let i = 0; i < vehicle.wheelInfos.length; i++) {
786
- vehicle.updateWheelTransform(i);
787
- const t = vehicle.wheelInfos[i].worldTransform;
788
- const wheelMesh = wheelBodies[i];
789
- wheelMesh.position.copy(t.position);
790
- wheelMesh.quaternion.copy(t.quaternion);
791
- }
792
-
793
- if (chassisBody && chassisBody.visualMesh) {
794
- chassisBody.visualMesh.position.copy(chassisBody.position);
795
- chassisBody.visualMesh.quaternion.copy(chassisBody.quaternion);
796
- }
797
- }
798
-
799
- function updateCamera() {
800
- if (!chassisBody) return;
801
-
802
- // Camera Follow Logic (Smooth Lerp)
803
- const relativeCameraOffset = new THREE.Vector3(0, 5, 10);
804
- const cameraOffset = relativeCameraOffset.applyMatrix4(chassisBody.visualMesh.matrixWorld);
805
-
806
- // Simple lerp
807
- camera.position.lerp(cameraOffset, 0.1);
808
-
809
- // Look slightly ahead of the car
810
- const carPos = chassisBody.visualMesh.position;
811
- const lookTarget = new THREE.Vector3(carPos.x, carPos.y, carPos.z - 10);
812
- // Apply rotation of car to look target? No, just look at car generally
813
- camera.lookAt(carPos);
814
- }
815
-
816
- function updateUI() {
817
- if (!chassisBody) return;
818
- // Speed calculation (approximate)
819
- const velocity = chassisBody.velocity;
820
- const speed = Math.sqrt(velocity.x**2 + velocity.z**2) * 3.6; // m/s to km/h
821
- document.getElementById('speed-display').innerHTML = Math.round(speed) + ' <small>km/h</small>';
822
- }
823
-
824
- // Global Reset Function
825
- window.resetCar = function() {
826
- if (!chassisBody) return;
827
- chassisBody.position.set(0, 2, 0);
828
- chassisBody.quaternion.set(0, 0, 0, 1);
829
- chassisBody.velocity.set(0, 0, 0);
830
- chassisBody.angularVelocity.set(0, 0, 0);
831
- };
832
-
833
- function animate() {
834
- requestAnimationFrame(animate);
835
-
836
- if (isGameActive) {
837
- handleGamepad();
838
- updatePhysics();
839
- updateCamera();
840
- updateUI();
841
- }
842
-
843
- renderer.render(scene, camera);
844
- }
845
-
846
- function onWindowResize() {
847
- camera.aspect = window.innerWidth / window.innerHeight;
848
- camera.updateProjectionMatrix();
849
- renderer.setSize(window.innerWidth, window.innerHeight);
850
- }
851
-
852
- // Start
853
- init();
854
-
855
- </script>
856
- </body>
857
- </html>
 
1
  <!DOCTYPE html>
2
+ <html lang="de">
3
+
4
  <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
7
+ <title>Neon Drift: Overdrive</title>
8
+
9
+ <!-- Import FontAwesome for Icons -->
10
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
11
+
12
+ <style>
13
+ :root {
14
+ --primary-color: #00f3ff;
15
+ --secondary-color: #ff00ff;
16
+ --accent-color: #ffe600;
17
+ --danger-color: #ff2a2a;
18
+ --bg-dark: #050510;
19
+ --glass-bg: rgba(10, 10, 25, 0.65);
20
+ --glass-border: rgba(255, 255, 255, 0.15);
21
+ --text-color: #ffffff;
22
+ --font-main: 'Orbitron', 'Segoe UI', Roboto, sans-serif;
23
+ }
24
+
25
+ /* Import Google Font for Sci-Fi look */
26
+ @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&display=swap');
27
+
28
+ * {
29
+ box-sizing: border-box;
30
+ user-select: none;
31
+ -webkit-user-select: none;
32
+ touch-action: none;
33
+ }
34
+
35
+ body {
36
+ margin: 0;
37
+ padding: 0;
38
+ overflow: hidden;
39
+ background-color: var(--bg-dark);
40
+ font-family: var(--font-main);
41
+ color: var(--text-color);
42
+ }
43
+
44
+ /* --- Canvas Container --- */
45
+ #game-container {
46
+ position: absolute;
47
+ top: 0;
48
+ left: 0;
49
+ width: 100vw;
50
+ height: 100vh;
51
+ z-index: 1;
52
+ }
53
+
54
+ /* --- UI Overlay Layer --- */
55
+ #ui-layer {
56
+ position: absolute;
57
+ top: 0;
58
+ left: 0;
59
+ width: 100%;
60
+ height: 100%;
61
+ z-index: 10;
62
+ pointer-events: none;
63
+ display: flex;
64
+ flex-direction: column;
65
+ justify-content: space-between;
66
+ }
67
+
68
+ /* --- Header / Top Bar --- */
69
+ .top-bar {
70
+ display: flex;
71
+ justify-content: space-between;
72
+ align-items: center;
73
+ padding: 15px 25px;
74
+ background: linear-gradient(to bottom, rgba(0, 0, 0, 0.8), transparent);
75
+ pointer-events: auto;
76
+ }
77
+
78
+ .brand {
79
+ font-size: 1.2rem;
80
+ font-weight: 900;
81
+ text-transform: uppercase;
82
+ letter-spacing: 2px;
83
+ text-shadow: 0 0 10px var(--primary-color);
84
+ display: flex;
85
+ align-items: center;
86
+ gap: 10px;
87
+ }
88
+
89
+ .brand a {
90
+ color: var(--primary-color);
91
+ text-decoration: none;
92
+ transition: color 0.3s;
93
+ font-size: 0.8em;
94
+ opacity: 0.8;
95
+ }
96
+
97
+ .brand a:hover {
98
+ color: var(--secondary-color);
99
+ opacity: 1;
100
+ }
101
+
102
+ /* --- HUD Stats --- */
103
+ .hud-stats {
104
+ display: flex;
105
+ gap: 20px;
106
+ }
107
+
108
+ .stat-box {
109
+ background: var(--glass-bg);
110
+ border: 1px solid var(--glass-border);
111
+ padding: 8px 20px;
112
+ border-radius: 4px;
113
+ backdrop-filter: blur(8px);
114
+ box-shadow: 0 0 15px rgba(0, 243, 255, 0.1);
115
+ position: relative;
116
+ overflow: hidden;
117
+ }
118
+
119
+ .stat-box::after {
120
+ content: '';
121
+ position: absolute;
122
+ top: 0;
123
+ left: 0;
124
+ width: 2px;
125
+ height: 100%;
126
+ background: var(--primary-color);
127
+ }
128
+
129
+ .stat-box.score-box::after { background: var(--accent-color); }
130
+
131
+ .stat-label {
132
+ font-size: 0.6rem;
133
+ color: #aaa;
134
+ display: block;
135
+ letter-spacing: 1px;
136
+ }
137
+
138
+ .stat-value {
139
+ font-size: 1.4rem;
140
+ font-weight: 700;
141
+ color: var(--text-color);
142
+ text-shadow: 0 0 5px rgba(255,255,255,0.5);
143
+ }
144
+
145
+ /* --- Nitro Bar --- */
146
+ .nitro-container {
147
+ position: absolute;
148
+ bottom: 30px;
149
+ right: 30px;
150
+ width: 200px;
151
+ pointer-events: none;
152
+ }
153
+
154
+ .nitro-label {
155
+ font-size: 0.7rem;
156
+ margin-bottom: 5px;
157
+ color: var(--secondary-color);
158
+ text-transform: uppercase;
159
+ text-align: right;
160
+ text-shadow: 0 0 5px var(--secondary-color);
161
+ }
162
+
163
+ .nitro-bar-bg {
164
+ width: 100%;
165
+ height: 10px;
166
+ background: rgba(0,0,0,0.5);
167
+ border: 1px solid var(--secondary-color);
168
+ transform: skewX(-20deg);
169
+ overflow: hidden;
170
+ }
171
+
172
+ .nitro-bar-fill {
173
+ height: 100%;
174
+ width: 100%;
175
+ background: linear-gradient(90deg, var(--secondary-color), #ff66aa);
176
+ box-shadow: 0 0 10px var(--secondary-color);
177
+ transform-origin: left;
178
+ transition: transform 0.1s linear;
179
+ }
180
+
181
+ /* --- Main Menu (Start Screen) --- */
182
+ #start-screen {
183
+ position: absolute;
184
+ top: 0;
185
+ left: 0;
186
+ width: 100%;
187
+ height: 100%;
188
+ background: radial-gradient(circle at center, rgba(20, 20, 40, 0.9), #000000);
189
+ backdrop-filter: blur(15px);
190
+ display: flex;
191
+ flex-direction: column;
192
+ justify-content: center;
193
+ align-items: center;
194
+ z-index: 20;
195
+ pointer-events: auto;
196
+ transition: opacity 0.6s ease;
197
+ }
198
+
199
+ #start-screen.hidden {
200
+ opacity: 0;
201
+ pointer-events: none;
202
+ }
203
+
204
+ h1 {
205
+ font-size: 4.5rem;
206
+ margin-bottom: 0.5rem;
207
+ font-weight: 900;
208
+ font-style: italic;
209
+ background: linear-gradient(180deg, #fff, var(--primary-color));
210
+ -webkit-background-clip: text;
211
+ -webkit-text-fill-color: transparent;
212
+ text-shadow: 0 0 30px var(--primary-color);
213
+ text-align: center;
214
+ letter-spacing: -2px;
215
+ }
216
+
217
+ p.subtitle {
218
+ font-size: 1.1rem;
219
+ color: #ccc;
220
+ margin-bottom: 3rem;
221
+ text-align: center;
222
+ max-width: 500px;
223
+ line-height: 1.6;
224
+ text-transform: uppercase;
225
+ letter-spacing: 2px;
226
+ }
227
+
228
+ .btn {
229
+ background: transparent;
230
+ border: 2px solid var(--primary-color);
231
+ padding: 15px 50px;
232
+ color: var(--primary-color);
233
+ font-size: 1.2rem;
234
+ font-weight: bold;
235
+ font-family: var(--font-main);
236
+ border-radius: 2px;
237
+ cursor: pointer;
238
+ transition: all 0.3s;
239
+ text-transform: uppercase;
240
+ letter-spacing: 3px;
241
+ position: relative;
242
+ overflow: hidden;
243
+ box-shadow: 0 0 15px rgba(0, 243, 255, 0.2);
244
+ }
245
+
246
+ .btn::before {
247
+ content: '';
248
+ position: absolute;
249
+ top: 0;
250
+ left: -100%;
251
+ width: 100%;
252
+ height: 100%;
253
+ background: var(--primary-color);
254
+ transition: left 0.3s ease;
255
+ z-index: -1;
256
+ }
257
+
258
+ .btn:hover {
259
+ color: #000;
260
+ box-shadow: 0 0 30px var(--primary-color);
261
+ }
262
+
263
+ .btn:hover::before {
264
+ left: 0;
265
+ }
266
+
267
+ .controls-info {
268
+ margin-top: 50px;
269
+ display: grid;
270
+ grid-template-columns: repeat(3, 1fr);
271
+ gap: 30px;
272
+ text-align: center;
273
+ }
274
+
275
+ .control-item {
276
+ display: flex;
277
+ flex-direction: column;
278
+ align-items: center;
279
+ color: #888;
280
+ transition: transform 0.3s;
281
+ }
282
+
283
+ .control-item:hover {
284
+ transform: translateY(-5px);
285
+ color: #fff;
286
+ }
287
+
288
+ .control-item i {
289
+ font-size: 1.8rem;
290
+ margin-bottom: 10px;
291
+ color: var(--primary-color);
292
+ text-shadow: 0 0 10px var(--primary-color);
293
+ }
294
+
295
+ /* --- Mobile Controls --- */
296
+ #mobile-controls {
297
+ display: none;
298
+ width: 100%;
299
+ height: 100%;
300
+ position: absolute;
301
+ top: 0;
302
+ left: 0;
303
+ pointer-events: none;
304
+ padding: 20px;
305
+ }
306
+
307
+ .touch-zone {
308
+ pointer-events: auto;
309
+ position: absolute;
310
+ bottom: 30px;
311
+ display: flex;
312
+ gap: 15px;
313
+ }
314
+
315
+ .d-pad {
316
+ left: 20px;
317
+ display: flex;
318
+ flex-direction: column;
319
+ align-items: center;
320
+ gap: 15px;
321
+ }
322
+
323
+ .pedals {
324
+ right: 20px;
325
+ display: flex;
326
+ flex-direction: row-reverse;
327
+ gap: 15px;
328
+ align-items: flex-end;
329
+ }
330
+
331
+ .touch-btn {
332
+ width: 70px;
333
+ height: 70px;
334
+ background: rgba(255, 255, 255, 0.05);
335
+ border: 2px solid rgba(255, 255, 255, 0.2);
336
+ border-radius: 50%;
337
+ color: white;
338
+ font-size: 1.5rem;
339
+ display: flex;
340
+ justify-content: center;
341
+ align-items: center;
342
+ backdrop-filter: blur(4px);
343
+ transition: all 0.1s;
344
+ }
345
+
346
+ .touch-btn:active,
347
+ .touch-btn.active {
348
+ background: rgba(0, 243, 255, 0.3);
349
+ transform: scale(0.9);
350
+ border-color: var(--primary-color);
351
+ box-shadow: 0 0 15px var(--primary-color);
352
+ }
353
+
354
+ .touch-btn.brake:active,
355
+ .touch-btn.brake.active {
356
+ background: rgba(255, 0, 100, 0.3);
357
+ border-color: var(--secondary-color);
358
+ box-shadow: 0 0 15px var(--secondary-color);
359
+ }
360
 
361
+ .touch-btn.boost {
362
+ border-color: var(--accent-color);
363
+ color: var(--accent-color);
364
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
365
 
366
+ .touch-btn.boost:active,
367
+ .touch-btn.boost.active {
368
+ background: rgba(255, 230, 0, 0.3);
369
+ border-color: var(--accent-color);
370
+ box-shadow: 0 0 15px var(--accent-color);
371
+ }
372
+
373
+ /* --- Reset Button --- */
374
+ #reset-btn {
375
+ position: absolute;
376
+ bottom: 30px;
377
+ left: 50%;
378
+ transform: translateX(-50%);
379
+ background: rgba(0, 0, 0, 0.6);
380
+ border: 1px solid rgba(255,255,255,0.3);
381
+ color: #ccc;
382
+ padding: 10px 25px;
383
+ border-radius: 30px;
384
+ pointer-events: auto;
385
+ cursor: pointer;
386
+ font-size: 0.8rem;
387
+ text-transform: uppercase;
388
+ letter-spacing: 1px;
389
+ backdrop-filter: blur(5px);
390
+ display: none;
391
+ }
392
+
393
+ #reset-btn:hover {
394
+ background: var(--secondary-color);
395
+ border-color: var(--secondary-color);
396
+ color: white;
397
+ }
398
+
399
+ /* --- Media Queries --- */
400
+ @media (max-width: 768px) {
401
+ h1 { font-size: 2.8rem; }
402
+ .controls-info { display: none; }
403
+ #mobile-controls { display: block; }
404
+ #reset-btn { display: block; }
405
+ .nitro-container {
406
+ bottom: 100px; /* Move up to avoid finger overlap */
407
+ right: 20px;
408
+ width: 150px;
409
+ }
410
+ }
411
+ </style>
412
+
413
+ <!-- Three.js & Cannon-es via Import Map -->
414
+ <script type="importmap">
415
+ {
416
+ "imports": {
417
+ "three": "https://unpkg.com/three@0.160.0/build/three.module.js",
418
+ "three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/",
419
+ "cannon-es": "https://unpkg.com/cannon-es@0.20.0/dist/cannon-es.js"
420
+ }
421
+ }
422
+ </script>
423
  </head>
424
+
425
  <body>
426
 
427
+ <!-- Game Canvas -->
428
+ <div id="game-container"></div>
429
+
430
+ <!-- UI Overlay -->
431
+ <div id="ui-layer">
432
+ <div class="top-bar">
433
+ <div class="brand">
434
+ <i class="fa-solid fa-bolt"></i> NEON DRIFT
435
+ <span style="font-size: 0.6em; margin-left: 15px; opacity: 0.6; font-weight: 400;">
436
+ Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank">anycoder</a>
437
+ </span>
438
+ </div>
439
+ <div class="hud-stats">
440
+ <div class="stat-box score-box">
441
+ <span class="stat-label">SCORE</span>
442
+ <span class="stat-value" id="score-display">0</span>
443
  </div>
444
+ <div class="stat-box">
445
+ <span class="stat-label">SPEED</span>
446
+ <span class="stat-value" id="speed-display">0 <small style="font-size:0.6em">km/h</small></span>
447
  </div>
448
+ </div>
449
+ </div>
450
 
451
+ <!-- Nitro Bar -->
452
+ <div class="nitro-container">
453
+ <div class="nitro-label">Nitro System</div>
454
+ <div class="nitro-bar-bg">
455
+ <div class="nitro-bar-fill" id="nitro-fill"></div>
456
+ </div>
 
 
 
 
 
457
  </div>
458
 
459
+ <div id="reset-btn" onclick="resetCar()">
460
+ <i class="fa-solid fa-rotate-right"></i> Flip Car
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
461
  </div>
462
 
463
+ <!-- Mobile Controls -->
464
+ <div id="mobile-controls">
465
+ <div class="touch-zone d-pad">
466
+ <div class="touch-btn" id="btn-left"><i class="fa-solid fa-arrow-left"></i></div>
467
+ <div class="touch-btn" id="btn-right"><i class="fa-solid fa-arrow-right"></i></div>
468
+ </div>
469
+ <div class="touch-zone pedals">
470
+ <div class="touch-btn boost" id="btn-boost"><i class="fa-solid fa-fire"></i></div>
471
+ <div class="touch-btn brake" id="btn-brake" style="margin-right: 10px;"><i class="fa-solid fa-stop"></i></div>
472
+ <div class="touch-btn" id="btn-gas"><i class="fa-solid fa-gas-pump"></i></div>
473
+ </div>
474
+ </div>
475
+ </div>
476
+
477
+ <!-- Start Screen -->
478
+ <div id="start-screen">
479
+ <h1>NEON DRIFT<br><span style="font-size: 0.5em; color: var(--accent-color); text-shadow: 0 0 20px var(--accent-color);">OVERDRIVE</span></h1>
480
+ <p class="subtitle">Sammle Energiekerne. Nutze Nitro. Überlebe die Unendlichkeit.</p>
481
+
482
+ <button class="btn" id="start-btn">Start Engine</button>
483
+
484
+ <div class="controls-info">
485
+ <div class="control-item">
486
+ <i class="fa-solid fa-keyboard"></i>
487
+ <span>WASD / Shift</span>
488
+ </div>
489
+ <div class="control-item">
490
+ <i class="fa-solid fa-gamepad"></i>
491
+ <span>Controller</span>
492
+ </div>
493
+ <div class="control-item">
494
+ <i class="fa-solid fa-mobile-screen"></i>
495
+ <span>Touch</span>
496
+ </div>
497
+ </div>
498
+ </div>
499
+
500
+ <script type="module">
501
+ import * as THREE from 'three';
502
+ import * as CANNON from 'cannon-es';
503
+ // Post Processing Imports
504
+ import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
505
+ import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
506
+ import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';
507
+
508
+ // --- Configuration ---
509
+ const CONFIG = {
510
+ shadows: true,
511
+ maxSteerVal: 0.5,
512
+ maxForce: 1200,
513
+ boostForce: 2500,
514
+ brakeForce: 30,
515
+ nitroMax: 100,
516
+ nitroDepletion: 0.5, // per frame
517
+ nitroRegen: 0.1, // per frame
518
+ colors: {
519
+ bg: 0x050510,
520
+ grid: 0x00f3ff,
521
+ car: 0x111111,
522
+ neon: 0xff00ff,
523
+ coin: 0xffaa00
524
+ }
525
+ };
526
+
527
+ // --- Globals ---
528
+ let scene, camera, renderer, composer;
529
+ let world, vehicle, chassisBody, wheelBodies = [];
530
+ let particles = [];
531
+ let collectibles = [];
532
+ let lastTime;
533
+ let isGameActive = false;
534
+
535
+ // Game State
536
+ let score = 0;
537
+ let nitro = 100;
538
+ let isBoosting = false;
539
+
540
+ // Input State
541
+ const input = {
542
+ forward: false,
543
+ backward: false,
544
+ left: false,
545
+ right: false,
546
+ brake: false,
547
+ boost: false
548
+ };
549
+
550
+ // --- Initialization ---
551
+ function init() {
552
+ // 1. Setup Three.js Scene
553
+ scene = new THREE.Scene();
554
+ scene.background = new THREE.Color(CONFIG.colors.bg);
555
+ scene.fog = new THREE.FogExp2(CONFIG.colors.bg, 0.006);
556
+
557
+ // 2. Camera
558
+ camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
559
+
560
+ // 3. Renderer
561
+ renderer = new THREE.WebGLRenderer({ antialias: true, powerPreference: "high-performance" });
562
+ renderer.setSize(window.innerWidth, window.innerHeight);
563
+ renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
564
+ renderer.shadowMap.enabled = true;
565
+ renderer.shadowMap.type = THREE.PCFSoftShadowMap;
566
+ renderer.toneMapping = THREE.ReinhardToneMapping;
567
+ document.getElementById('game-container').appendChild(renderer.domElement);
568
+
569
+ // 4. Post-Processing (BLOOM)
570
+ const renderScene = new RenderPass(scene, camera);
571
+ const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85);
572
+ bloomPass.threshold = 0.2;
573
+ bloomPass.strength = 1.2; // Intensity of glow
574
+ bloomPass.radius = 0.5;
575
+
576
+ composer = new EffectComposer(renderer);
577
+ composer.addPass(renderScene);
578
+ composer.addPass(bloomPass);
579
+
580
+ // 5. Lights
581
+ const ambientLight = new THREE.AmbientLight(0x404040, 0.5);
582
+ scene.add(ambientLight);
583
+
584
+ const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
585
+ dirLight.position.set(50, 100, 50);
586
+ dirLight.castShadow = true;
587
+ dirLight.shadow.mapSize.width = 2048;
588
+ dirLight.shadow.mapSize.height = 2048;
589
+ dirLight.shadow.camera.near = 0.5;
590
+ dirLight.shadow.camera.far = 500;
591
+ dirLight.shadow.camera.left = -100;
592
+ dirLight.shadow.camera.right = 100;
593
+ dirLight.shadow.camera.top = 100;
594
+ dirLight.shadow.camera.bottom = -100;
595
+ scene.add(dirLight);
596
+
597
+ // 6. Physics World
598
+ world = new CANNON.World();
599
+ world.gravity.set(0, -9.82, 0);
600
+ world.broadphase = new CANNON.SAPBroadphase(world);
601
+
602
+ // Materials
603
+ const groundMat = new CANNON.Material();
604
+ const wheelMat = new CANNON.Material();
605
+ const wheelGroundContact = new CANNON.ContactMaterial(wheelMat, groundMat, {
606
+ friction: 0.3,
607
+ restitution: 0,
608
+ contactEquationStiffness: 1000
609
+ });
610
+ world.addContactMaterial(wheelGroundContact);
611
+
612
+ // 7. Create Environment
613
+ createEnvironment(groundMat);
614
+
615
+ // 8. Create Car
616
+ createCar(wheelMat);
617
+
618
+ // 9. Event Listeners
619
+ window.addEventListener('resize', onWindowResize);
620
+ setupInputs();
621
+
622
+ // Start Loop
623
+ lastTime = performance.now();
624
+ requestAnimationFrame(animate);
625
+ }
626
+
627
+ // --- Environment Generation ---
628
+ function createEnvironment(material) {
629
+ // Floor
630
+ const groundGeo = new THREE.PlaneGeometry(1000, 1000);
631
+ const groundTex = createGridTexture();
632
+ groundTex.wrapS = THREE.RepeatWrapping;
633
+ groundTex.wrapT = THREE.RepeatWrapping;
634
+ groundTex.repeat.set(200, 200);
635
+
636
+ const groundMesh = new THREE.Mesh(
637
+ groundGeo,
638
+ new THREE.MeshStandardMaterial({
639
+ map: groundTex,
640
+ roughness: 0.4,
641
+ metalness: 0.6,
642
+ emissive: 0x001133,
643
+ emissiveIntensity: 0.2
644
+ })
645
+ );
646
+ groundMesh.rotation.x = -Math.PI / 2;
647
+ groundMesh.receiveShadow = true;
648
+ scene.add(groundMesh);
649
+
650
+ // Physics Ground
651
+ const groundBody = new CANNON.Body({
652
+ mass: 0,
653
+ shape: new CANNON.Plane(),
654
+ material: material
655
+ });
656
+ groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0);
657
+ world.addBody(groundBody);
658
+
659
+ // Sun (Background)
660
+ createSun();
661
+
662
+ // Mountains (Wireframe)
663
+ createMountains();
664
+
665
+ // Obstacles
666
+ createObstacle(10, 2, 10, -20, 1, -30);
667
+ createRamp(10, 1, 15, 20, 0.5, -20);
668
+
669
+ // Initial Collectibles
670
+ for(let i=0; i<20; i++) {
671
+ spawnCollectible();
672
+ }
673
+ }
674
+
675
+ function createSun() {
676
+ const sunGeo = new THREE.CircleGeometry(40, 64);
677
+ const sunMat = new THREE.ShaderMaterial({
678
+ uniforms: {
679
+ time: { value: 0 },
680
+ color1: { value: new THREE.Color(0xff00cc) },
681
+ color2: { value: new THREE.Color(0x3300cc) }
682
+ },
683
+ vertexShader: `
684
+ varying vec2 vUv;
685
+ void main() {
686
+ vUv = uv;
687
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
688
+ }
689
+ `,
690
+ fragmentShader: `
691
+ uniform float time;
692
+ uniform vec3 color1;
693
+ uniform vec3 color2;
694
+ varying vec2 vUv;
695
+ void main() {
696
+ // Create gradient stripes
697
+ float stripes = sin(vUv.y * 40.0 + time * 0.5);
698
+ float gradient = smoothstep(0.4, 0.6, vUv.y);
699
+ vec3 color = mix(color1, color2, vUv.y + stripes * 0.05);
700
+ float alpha = smoothstep(0.0, 0.5, vUv.y) * (1.0 - smoothstep(0.5, 1.0, vUv.y));
701
+ gl_FragColor = vec4(color, alpha * 0.8);
702
+ }
703
+ `,
704
+ transparent: true,
705
+ side: THREE.DoubleSide
706
+ });
707
+
708
+ const sun = new THREE.Mesh(sunGeo, sunMat);
709
+ sun.position.set(0, 20, -300);
710
+ scene.add(sun);
711
+
712
+ // Sun Glow (Behind)
713
+ const glowGeo = new THREE.CircleGeometry(60, 32);
714
+ const glowMat = new THREE.MeshBasicMaterial({ color: 0xff00aa, transparent: true, opacity: 0.3 });
715
+ const glow = new THREE.Mesh(glowGeo, glowMat);
716
+ glow.position.set(0, 20, -310);
717
+ scene.add(glow);
718
+
719
+ // Update shader time
720
+ sun.userData.update = (t) => { sunMat.uniforms.time.value = t * 0.001; };
721
+ scene.userData.sun = sun;
722
+ }
723
+
724
+ function createMountains() {
725
+ const mountainGeo = new THREE.ConeGeometry(1, 1, 4);
726
+ const mountainMat = new THREE.MeshBasicMaterial({
727
+ color: 0xaa00ff,
728
+ wireframe: true,
729
+ transparent: true,
730
+ opacity: 0.3
731
+ });
732
+
733
+ const group = new THREE.Group();
734
+
735
+ for(let i=0; i<60; i++) {
736
+ const mesh = new THREE.Mesh(mountainGeo, mountainMat);
737
+ const scaleY = 20 + Math.random() * 60;
738
+ const scaleXZ = 30 + Math.random() * 50;
739
 
740
+ mesh.scale.set(scaleXZ, scaleY, scaleXZ);
741
+ mesh.position.set(
742
+ (Math.random() - 0.5) * 800,
743
+ scaleY / 2 - 10,
744
+ -200 - Math.random() * 400
745
+ );
746
+ mesh.rotation.y = Math.random() * Math.PI;
747
+ group.add(mesh);
748
+ }
749
+ scene.add(group);
750
+ }
751
+
752
+ function createGridTexture() {
753
+ const canvas = document.createElement('canvas');
754
+ canvas.width = 512;
755
+ canvas.height = 512;
756
+ const ctx = canvas.getContext('2d');
757
+
758
+ ctx.fillStyle = '#050510';
759
+ ctx.fillRect(0, 0, 512, 512);
760
+
761
+ ctx.strokeStyle = '#00f3ff';
762
+ ctx.lineWidth = 2;
763
+ ctx.shadowBlur = 4;
764
+ ctx.shadowColor = '#00f3ff';
765
+
766
+ ctx.beginPath();
767
+ for(let i=0; i<=512; i+=64) {
768
+ ctx.moveTo(i, 0);
769
+ ctx.lineTo(i, 512);
770
+ ctx.moveTo(0, i);
771
+ ctx.lineTo(512, i);
772
+ }
773
+ ctx.stroke();
774
+
775
+ const tex = new THREE.CanvasTexture(canvas);
776
+ tex.anisotropy = 16;
777
+ return tex;
778
+ }
779
+
780
+ function createObstacle(w, h, d, x, y, z) {
781
+ const geo = new THREE.BoxGeometry(w, h, d);
782
+ const mat = new THREE.MeshStandardMaterial({ color: 0x222222, roughness: 0.1, metalness: 0.8 });
783
+ const mesh = new THREE.Mesh(geo, mat);
784
+ mesh.position.set(x, y, z);
785
+ mesh.castShadow = true;
786
+ mesh.receiveShadow = true;
787
+ scene.add(mesh);
788
+
789
+ const edges = new THREE.EdgesGeometry(geo);
790
+ const line = new THREE.LineSegments(edges, new THREE.LineBasicMaterial({ color: 0xff00ff }));
791
+ mesh.add(line);
792
+
793
+ const shape = new CANNON.Box(new CANNON.Vec3(w/2, h/2, d/2));
794
+ const body = new CANNON.Body({ mass: 0 });
795
+ body.addShape(shape);
796
+ body.position.set(x, y, z);
797
+ world.addBody(body);
798
+ }
799
+
800
+ function createRamp(w, h, d, x, y, z) {
801
+ const geo = new THREE.BoxGeometry(w, h, d);
802
+ const mat = new THREE.MeshStandardMaterial({ color: 0x222222, metalness: 0.5 });
803
+ const mesh = new THREE.Mesh(geo, mat);
804
+ mesh.position.set(x, y, z);
805
+ mesh.rotation.x = -0.3;
806
+ mesh.castShadow = true;
807
+ mesh.receiveShadow = true;
808
+ scene.add(mesh);
809
+
810
+ const edges = new THREE.EdgesGeometry(geo);
811
+ const line = new THREE.LineSegments(edges, new THREE.LineBasicMaterial({ color: 0x00f3ff }));
812
+ mesh.add(line);
813
+
814
+ const shape = new CANNON.Box(new CANNON.Vec3(w/2, h/2, d/2));
815
+ const body = new CANNON.Body({ mass: 0 });
816
+ body.addShape(shape);
817
+ body.position.set(x, y, z);
818
+ body.quaternion.setFromEuler(-0.3, 0, 0);
819
+ world.addBody(body);
820
+ }
821
+
822
+ function spawnCollectible() {
823
+ const geo = new THREE.OctahedronGeometry(0.8);
824
+ const mat = new THREE.MeshBasicMaterial({ color: CONFIG.colors.coin });
825
+ const mesh = new THREE.Mesh(geo, mat);
826
+
827
+ // Random position around center
828
+ const x = (Math.random() - 0.5) * 150;
829
+ const z = (Math.random() - 0.5) * 150;
830
+ const y = 2 + Math.random() * 3;
831
+
832
+ mesh.position.set(x, y, z);
833
+
834
+ // Add PointLight
835
+ const light = new THREE.PointLight(CONFIG.colors.coin, 2, 10);
836
+ mesh.add(light);
837
+
838
+ scene.add(mesh);
839
+ collectibles.push({ mesh: mesh, active: true });
840
+ }
841
+
842
+ // --- Car Creation ---
843
+ function createCar(material) {
844
+ const chassisWidth = 1.8;
845
+ const chassisHeight = 0.8;
846
+ const chassisDepth = 4;
847
+ const mass = 150;
848
+
849
+ // Visual Chassis
850
+ const chassisGeo = new THREE.BoxGeometry(chassisWidth, chassisHeight, chassisDepth);
851
+ const chassisMat = new THREE.MeshStandardMaterial({ color: CONFIG.colors.car, roughness: 0.2, metalness: 0.8 });
852
+ const chassisMesh = new THREE.Mesh(chassisGeo, chassisMat);
853
+ chassisMesh.castShadow = true;
854
+
855
+ // Neon Strips
856
+ const stripGeo = new THREE.BoxGeometry(1.82, 0.1, 4.02);
857
+ const stripMat = new THREE.MeshBasicMaterial({ color: CONFIG.colors.neon });
858
+ const strip = new THREE.Mesh(stripGeo, stripMat);
859
+ strip.position.y = -0.3;
860
+ chassisMesh.add(strip);
861
+
862
+ // Cabin
863
+ const cabinGeo = new THREE.BoxGeometry(1.4, 0.6, 2);
864
+ const cabinMat = new THREE.MeshStandardMaterial({ color: 0x111111, roughness: 0.0, metalness: 1.0 });
865
+ const cabinMesh = new THREE.Mesh(cabinGeo, cabinMat);
866
+ cabinMesh.position.y = 0.5;
867
+ cabinMesh.position.z = -0.2;
868
+ chassisMesh.add(cabinMesh);
869
+
870
+ // Engine Glow (Rear)
871
+ const engineGeo = new THREE.BoxGeometry(1.2, 0.2, 0.1);
872
+ const engineMat = new THREE.MeshBasicMaterial({ color: 0x00ffff });
873
+ const engine = new THREE.Mesh(engineGeo, engineMat);
874
+ engine.position.set(0, 0, 2.05);
875
+ chassisMesh.add(engine);
876
+
877
+ // Headlights
878
+ const spotLightL = new THREE.SpotLight(0xffffff, 10);
879
+ spotLightL.position.set(-0.6, 0, -1.8);
880
+ spotLightL.target.position.set(-0.6, -1, -10);
881
+ spotLightL.angle = 0.5;
882
+ spotLightL.penumbra = 0.5;
883
+ spotLightL.castShadow = true;
884
+ chassisMesh.add(spotLightL);
885
+ chassisMesh.add(spotLightL.target);
886
+
887
+ const spotLightR = spotLightL.clone();
888
+ spotLightR.position.set(0.6, 0, -1.8);
889
+ spotLightR.target.position.set(0.6, -1, -10);
890
+ chassisMesh.add(spotLightR);
891
+ chassisMesh.add(spotLightR.target);
892
+
893
+ scene.add(chassisMesh);
894
+
895
+ // Physics Chassis
896
+ const chassisShape = new CANNON.Box(new CANNON.Vec3(chassisWidth/2, chassisHeight/2, chassisDepth/2));
897
+ chassisBody = new CANNON.Body({ mass: mass, material: material });
898
+ chassisBody.addShape(chassisShape);
899
+ chassisBody.position.set(0, 4, 0);
900
+ chassisBody.angularDamping = 0.5;
901
+ world.addBody(chassisBody);
902
+
903
+ // Vehicle Setup
904
+ vehicle = new CANNON.RaycastVehicle({ chassisBody: chassisBody });
905
+
906
+ const wheelOptions = {
907
+ radius: 0.5,
908
+ directionLocal: new CANNON.Vec3(0, -1, 0),
909
+ suspensionStiffness: 30,
910
+ suspensionRestLength: 0.3,
911
+ frictionSlip: 2.0, // More