Update index.html
Browse files- index.html +165 -19
index.html
CHANGED
@@ -1,19 +1,165 @@
|
|
1 |
-
<!
|
2 |
-
<html>
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 Car Simulator with Three.js</title>
|
7 |
+
<style>
|
8 |
+
body { margin: 0; background-color: #87CEEB; overflow: hidden; }
|
9 |
+
canvas { display: block; }
|
10 |
+
</style>
|
11 |
+
</head>
|
12 |
+
<body>
|
13 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
14 |
+
<script>
|
15 |
+
// SETUP SCENE, CAMERA, RENDERER
|
16 |
+
const scene = new THREE.Scene();
|
17 |
+
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
|
18 |
+
const renderer = new THREE.WebGLRenderer();
|
19 |
+
renderer.setSize(window.innerWidth, window.innerHeight);
|
20 |
+
document.body.appendChild(renderer.domElement);
|
21 |
+
|
22 |
+
// CAMERA initial position
|
23 |
+
camera.position.set(0, 5, 10);
|
24 |
+
|
25 |
+
// RESIZE HANDLING
|
26 |
+
window.addEventListener('resize', () => {
|
27 |
+
camera.aspect = window.innerWidth / window.innerHeight;
|
28 |
+
camera.updateProjectionMatrix();
|
29 |
+
renderer.setSize(window.innerWidth, window.innerHeight);
|
30 |
+
});
|
31 |
+
|
32 |
+
// GROUND (Road)
|
33 |
+
const roadGeom = new THREE.PlaneGeometry(100, 4);
|
34 |
+
const roadMat = new THREE.MeshBasicMaterial({ color: 0x444444 });
|
35 |
+
const road = new THREE.Mesh(roadGeom, roadMat);
|
36 |
+
road.rotation.x = -Math.PI / 2;
|
37 |
+
road.position.z = -2;
|
38 |
+
scene.add(road);
|
39 |
+
|
40 |
+
// ROAD dashed line
|
41 |
+
const dashedLineGeom = new THREE.Geometry();
|
42 |
+
for (let i = -50; i < 50; i += 4) {
|
43 |
+
const point = new THREE.Vector3(i, 0.1, -2);
|
44 |
+
dashedLineGeom.vertices.push(point);
|
45 |
+
}
|
46 |
+
const dashedLineMat = new THREE.LineDashedMaterial({
|
47 |
+
color: 0xffffff,
|
48 |
+
linewidth: 1,
|
49 |
+
scale: 1,
|
50 |
+
dashSize: 1,
|
51 |
+
gapSize: 1
|
52 |
+
});
|
53 |
+
const dashedLine = new THREE.Line(dashedLineGeom, dashedLineMat);
|
54 |
+
dashedLine.computeLineDistances();
|
55 |
+
scene.add(dashedLine);
|
56 |
+
|
57 |
+
// MOUNTAINS (just 3 big blocks)
|
58 |
+
const mountainGeom = new THREE.BoxGeometry(20, 10, 20);
|
59 |
+
const mountainMat = new THREE.MeshLambertMaterial({ color: 0x888888 });
|
60 |
+
for (let i = -1; i <= 1; i++) {
|
61 |
+
const mountain = new THREE.Mesh(mountainGeom, mountainMat);
|
62 |
+
mountain.position.set(i * 25, -5, -50);
|
63 |
+
scene.add(mountain);
|
64 |
+
}
|
65 |
+
|
66 |
+
// CLOUDS
|
67 |
+
function makeCloud(x, z) {
|
68 |
+
const puffGeom = new THREE.SphereGeometry(2, 6, 6);
|
69 |
+
const puffMat = new THREE.MeshBasicMaterial({ color: 0xffffff, transparent: true, opacity: 0.7 });
|
70 |
+
const cloud = new THREE.Group();
|
71 |
+
for (let i = 0; i < 3; i++) {
|
72 |
+
const puff = new THREE.Mesh(puffGeom, puffMat);
|
73 |
+
puff.position.set(i * 2 - 2, Math.random() * 2, Math.random() * 2 - 1);
|
74 |
+
cloud.add(puff);
|
75 |
+
}
|
76 |
+
cloud.position.set(x, 15, z);
|
77 |
+
scene.add(cloud);
|
78 |
+
return cloud;
|
79 |
+
}
|
80 |
+
const clouds = [];
|
81 |
+
for (let i = 0; i < 10; i++) {
|
82 |
+
clouds.push(makeCloud(Math.random() * 100 - 50, Math.random() * 100 - 50));
|
83 |
+
}
|
84 |
+
|
85 |
+
// TREES (simple green cones)
|
86 |
+
function makeTree(x, z) {
|
87 |
+
const treeGeom = new THREE.ConeGeometry(1, 3);
|
88 |
+
const treeMat = new THREE.MeshLambertMaterial({ color: 0x22aa22 });
|
89 |
+
const tree = new THREE.Mesh(treeGeom, treeMat);
|
90 |
+
tree.position.set(x, 1, z);
|
91 |
+
scene.add(tree);
|
92 |
+
}
|
93 |
+
for (let i = -40; i <= 40; i += 8) {
|
94 |
+
makeTree(i + Math.random() * 4 - 2, -4 + Math.random() * 2 - 1);
|
95 |
+
}
|
96 |
+
|
97 |
+
// CAR
|
98 |
+
const carGeom = new THREE.BoxGeometry(3, 1.5, 1.5);
|
99 |
+
const carMat = new THREE.MeshLambertMaterial({ color: 0xff0000 });
|
100 |
+
const car = new THREE.Mesh(carGeom, carMat);
|
101 |
+
car.position.set(0, 1, 0);
|
102 |
+
scene.add(car);
|
103 |
+
let carSpeed = 0;
|
104 |
+
let carDir = 0; // radians
|
105 |
+
const carVel = new THREE.Vector3();
|
106 |
+
|
107 |
+
// TRAIN (just a silly little train going in circles)
|
108 |
+
const trainGeom = new THREE.BoxGeometry(2, 1, 1);
|
109 |
+
const trainMat = new THREE.MeshLambertMaterial({ color: 0x0000ff });
|
110 |
+
const train = new THREE.Mesh(trainGeom, trainMat);
|
111 |
+
train.position.set(20, 1, -4);
|
112 |
+
scene.add(train);
|
113 |
+
let trainAngle = 0;
|
114 |
+
|
115 |
+
// LIGHTING
|
116 |
+
const ambientLight = new THREE.AmbientLight(0x333333);
|
117 |
+
scene.add(ambientLight);
|
118 |
+
const dirLight = new THREE.DirectionalLight(0xffffff, 1);
|
119 |
+
dirLight.position.set(0, 10, 5);
|
120 |
+
scene.add(dirLight);
|
121 |
+
|
122 |
+
// KEYBOARD CONTROLS
|
123 |
+
document.addEventListener('keydown', (e) => {
|
124 |
+
switch(e.key) {
|
125 |
+
case 'ArrowUp': case 'w': carSpeed = Math.min(carSpeed + 0.2, 0.5); break;
|
126 |
+
case 'ArrowDown': case 's': carSpeed = Math.max(carSpeed - 0.2, -0.5); break;
|
127 |
+
case 'ArrowLeft': case 'a': carDir -= 0.1; break;
|
128 |
+
case 'ArrowRight': case 'd': carDir += 0.1; break;
|
129 |
+
}
|
130 |
+
});
|
131 |
+
|
132 |
+
// MAIN LOOP
|
133 |
+
function animate() {
|
134 |
+
requestAnimationFrame(animate);
|
135 |
+
|
136 |
+
// Move CAR
|
137 |
+
carVel.x = Math.sin(carDir) * carSpeed;
|
138 |
+
carVel.z = Math.cos(carDir) * carSpeed;
|
139 |
+
car.position.x += carVel.x;
|
140 |
+
car.position.z += carVel.z;
|
141 |
+
carSpeed *= 0.98; // damping
|
142 |
+
// keep car on road bounds
|
143 |
+
if (car.position.x < -45) car.position.x = -45;
|
144 |
+
if (car.position.x > 45) car.position.x = 45;
|
145 |
+
// Follow car with camera (smoothly)
|
146 |
+
camera.position.x += (car.position.x - camera.position.x) * 0.05;
|
147 |
+
camera.position.z += (car.position.z + 10 - camera.position.z) * 0.05;
|
148 |
+
|
149 |
+
// Move TRAIN in circle
|
150 |
+
trainAngle += 0.01;
|
151 |
+
train.position.x = Math.sin(trainAngle) * 20;
|
152 |
+
train.position.z = Math.cos(trainAngle) * 20 - 4;
|
153 |
+
|
154 |
+
// Move CLOUDS slowly
|
155 |
+
clouds.forEach(cloud => {
|
156 |
+
cloud.position.x += 0.01;
|
157 |
+
if (cloud.position.x > 50) cloud.position.x = -50;
|
158 |
+
});
|
159 |
+
|
160 |
+
renderer.render(scene, camera);
|
161 |
+
}
|
162 |
+
animate();
|
163 |
+
</script>
|
164 |
+
</body>
|
165 |
+
</html>
|