Update index.html
Browse files- index.html +144 -156
index.html
CHANGED
@@ -1,165 +1,153 @@
|
|
1 |
<!DOCTYPE html>
|
2 |
-
<html
|
3 |
<head>
|
4 |
-
|
5 |
-
|
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 |
-
//
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
// CAMERA
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
window.addEventListener('resize', () => {
|
27 |
-
camera.aspect = window.innerWidth / window.innerHeight;
|
28 |
-
camera.updateProjectionMatrix();
|
29 |
renderer.setSize(window.innerWidth, window.innerHeight);
|
30 |
-
|
31 |
-
|
32 |
-
//
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
scene.add(
|
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 |
-
scene.add(
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
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 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
164 |
</body>
|
165 |
</html>
|
|
|
1 |
<!DOCTYPE html>
|
2 |
+
<html>
|
3 |
<head>
|
4 |
+
<meta charset="UTF-8">
|
5 |
+
<style>body { margin: 0; background-color: #87CEEB; }</style>
|
|
|
|
|
|
|
|
|
|
|
6 |
</head>
|
7 |
<body>
|
8 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
9 |
+
<script>
|
10 |
+
// SETTINGS
|
11 |
+
var planeSpeed = 0.1;
|
12 |
+
var planeRollSpeed = 0.05;
|
13 |
+
var planePitchSpeed = 0.05;
|
14 |
+
var maxAltitude = 1000;
|
15 |
+
var maxSpeed = 5;
|
16 |
+
|
17 |
+
// SETUP SCENE, CAMERA, RENDERER
|
18 |
+
var scene = new THREE.Scene();
|
19 |
+
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000);
|
20 |
+
var renderer = new THREE.WebGLRenderer({ antialias: true });
|
|
|
|
|
|
|
21 |
renderer.setSize(window.innerWidth, window.innerHeight);
|
22 |
+
document.body.appendChild(renderer.domElement);
|
23 |
+
|
24 |
+
// GENERATE SKYBOX (simple gradient)
|
25 |
+
var vertexShader = `
|
26 |
+
varying vec3 vWorldPosition;
|
27 |
+
void main() {
|
28 |
+
vec4 worldPosition = modelMatrix * vec4(position, 1.0);
|
29 |
+
vWorldPosition = worldPosition.xyz;
|
30 |
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
31 |
+
}
|
32 |
+
`;
|
33 |
+
var fragmentShader = `
|
34 |
+
uniform vec3 topColor;
|
35 |
+
uniform vec3 bottomColor;
|
36 |
+
uniform float offset;
|
37 |
+
uniform float exponent;
|
38 |
+
varying vec3 vWorldPosition;
|
39 |
+
void main() {
|
40 |
+
float h = normalize(vWorldPosition + offset).y;
|
41 |
+
gl_FragColor = vec4(mix(bottomColor, topColor, pow(max(h, 0.0), exponent)), 1.0);
|
42 |
+
}
|
43 |
+
`;
|
44 |
+
var skyUniforms = {
|
45 |
+
topColor: { value: new THREE.Color(0x87CEEB) }, // light blue
|
46 |
+
bottomColor: { value: new THREE.Color(0xFFFFFF) }, // white
|
47 |
+
offset: { value: 33 },
|
48 |
+
exponent: { value: 0.6 }
|
49 |
+
};
|
50 |
+
var skyGeo = new THREE.SphereGeometry(5000, 32, 15);
|
51 |
+
var skyMat = new THREE.ShaderMaterial({
|
52 |
+
vertexShader, fragmentShader, uniforms: skyUniforms, side: THREE.BackSide
|
53 |
+
});
|
54 |
+
var sky = new THREE.Mesh(skyGeo, skyMat);
|
55 |
+
scene.add(sky);
|
56 |
+
|
57 |
+
// GENERATE GROUND (simple plane)
|
58 |
+
var groundGeo = new THREE.PlaneGeometry(10000, 10000, 10, 10);
|
59 |
+
var groundMat = new THREE.MeshLambertMaterial({ color: 0x32CD32 }); // lime green
|
60 |
+
var ground = new THREE.Mesh(groundGeo, groundMat);
|
61 |
+
ground.rotation.x = -Math.PI / 2;
|
62 |
+
scene.add(ground);
|
63 |
+
|
64 |
+
// GENERATE AIRPLANE (simple mesh)
|
65 |
+
var planeGeo = new THREE.Group();
|
66 |
+
var fuselageGeo = new THREE.CylinderGeometry(1, 2, 10, 20);
|
67 |
+
var fuselageMat = new THREE.MeshLambertMaterial({ color: 0xFFFFFF }); // white
|
68 |
+
var fuselage = new THREE.Mesh(fuselageGeo, fuselageMat);
|
69 |
+
planeGeo.add(fuselage);
|
70 |
+
var wingGeo = new THREE.BoxGeometry(10, 1, 1);
|
71 |
+
var wingMat = new THREE.MeshLambertMaterial({ color: 0xFF0000 }); // red
|
72 |
+
var leftWing = new THREE.Mesh(wingGeo, wingMat);
|
73 |
+
leftWing.position.x = -5;
|
74 |
+
planeGeo.add(leftWing);
|
75 |
+
var rightWing = new THREE.Mesh(wingGeo, wingMat);
|
76 |
+
rightWing.position.x = 5;
|
77 |
+
planeGeo.add(rightWing);
|
78 |
+
var tailGeo = new THREE.ConeGeometry(1, 2, 10);
|
79 |
+
var tailMat = new THREE.MeshLambertMaterial({ color: 0x0000FF }); // blue
|
80 |
+
var tail = new THREE.Mesh(tailGeo, tailMat);
|
81 |
+
tail.position.z = -5;
|
82 |
+
planeGeo.add(tail);
|
83 |
+
scene.add(planeGeo);
|
84 |
+
var plane = planeGeo; // we'll move this
|
85 |
+
|
86 |
+
// ADD LIGHTING
|
87 |
+
var sun = new THREE.DirectionalLight(0xFFFFFF, 1);
|
88 |
+
sun.position.set(100, 100, 100);
|
89 |
+
scene.add(sun);
|
90 |
+
var ambient = new THREE.AmbientLight(0x444444);
|
91 |
+
scene.add(ambient);
|
92 |
+
|
93 |
+
// CAMERA ATTACH TO PLANE
|
94 |
+
plane.add(camera);
|
95 |
+
camera.position.set(0, 5, -15); // behind the cockpit
|
96 |
+
camera.lookAt(plane.position);
|
97 |
+
|
98 |
+
// USER INPUT (keyboard)
|
99 |
+
var keys = {
|
100 |
+
w: false, s: false, a: false, d: false,
|
101 |
+
q: false, e: false, r: false, f: false
|
102 |
+
};
|
103 |
+
document.addEventListener('keydown', (e) => {
|
104 |
+
if (e.key in keys) keys[e.key] = true;
|
105 |
+
});
|
106 |
+
document.addEventListener('keyup', (e) => {
|
107 |
+
if (e.key in keys) keys[e.key] = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
108 |
});
|
109 |
|
110 |
+
// MAIN LOOP
|
111 |
+
var velocity = new THREE.Vector3();
|
112 |
+
var direction = new THREE.Vector3();
|
113 |
+
var roll = 0, pitch = 0, altitude = 0, speed = 0;
|
114 |
+
function animate() {
|
115 |
+
requestAnimationFrame(animate);
|
116 |
+
|
117 |
+
// PLANE CONTROLS
|
118 |
+
if (keys.w) speed = Math.min(maxSpeed, speed + 0.01);
|
119 |
+
if (keys.s) speed = Math.max(0, speed - 0.01);
|
120 |
+
if (keys.a) roll -= planeRollSpeed;
|
121 |
+
if (keys.d) roll += planeRollSpeed;
|
122 |
+
if (keys.q) pitch -= planePitchSpeed;
|
123 |
+
if (keys.e) pitch += planePitchSpeed;
|
124 |
+
if (keys.r) { /* reset */ roll = pitch = 0; speed = 1; }
|
125 |
+
if (keys.f) { /* full throttle */ speed = maxSpeed; }
|
126 |
+
|
127 |
+
// PHYSICS SIM (very basic)
|
128 |
+
plane.rotation.z = roll;
|
129 |
+
plane.rotation.x = pitch;
|
130 |
+
direction.set(0, 0, -1).applyEuler(plane.rotation);
|
131 |
+
velocity.add(direction.multiplyScalar(speed * planeSpeed));
|
132 |
+
plane.position.add(velocity);
|
133 |
+
altitude = plane.position.y;
|
134 |
+
if (altitude < 0) {
|
135 |
+
plane.position.y = 0; // don't go underground
|
136 |
+
velocity.y = 0;
|
137 |
+
}
|
138 |
+
if (altitude > maxAltitude) {
|
139 |
+
plane.position.y = maxAltitude; // don't go too high
|
140 |
+
velocity.y = 0;
|
141 |
+
}
|
142 |
+
velocity.multiplyScalar(0.99); // friction
|
143 |
+
|
144 |
+
// CAMERA FOLLOW
|
145 |
+
camera.lookAt(plane.position);
|
146 |
+
|
147 |
+
// RENDER
|
148 |
+
renderer.render(scene, camera);
|
149 |
+
}
|
150 |
+
animate();
|
151 |
+
</script>
|
152 |
</body>
|
153 |
</html>
|