Spaces:
Sleeping
Sleeping
Upload 11 files
Browse files- README.md +4 -3
- index.html +2 -6
- src/Aircraft.js +38 -12
- src/main.js +10 -0
README.md
CHANGED
|
@@ -34,9 +34,10 @@ This project is pre-configured to be deployed as a **Docker Space** on Hugging F
|
|
| 34 |
## Controls
|
| 35 |
|
| 36 |
### Flight Controls
|
| 37 |
-
- **
|
| 38 |
-
- **
|
| 39 |
-
- **
|
|
|
|
| 40 |
- **Shift**: Afterburn (cyan glowing thruster boost)
|
| 41 |
- **Arrow Up / Down**: Increase / Decrease base cruise throttle
|
| 42 |
|
|
|
|
| 34 |
## Controls
|
| 35 |
|
| 36 |
### Flight Controls
|
| 37 |
+
- **Click**: Lock mouse pointer to the screen to take control
|
| 38 |
+
- **Mouse Up/Down**: Pitch up/down
|
| 39 |
+
- **Mouse Left/Right**: Roll left/right
|
| 40 |
+
- **A/D**: Yaw left/right
|
| 41 |
- **Shift**: Afterburn (cyan glowing thruster boost)
|
| 42 |
- **Arrow Up / Down**: Increase / Decrease base cruise throttle
|
| 43 |
|
index.html
CHANGED
|
@@ -1159,15 +1159,11 @@
|
|
| 1159 |
<div class="controls-tip glass">
|
| 1160 |
<div class="tip-title">Controls</div>
|
| 1161 |
<div class="ctrl-row">
|
| 1162 |
-
<span class="ctrl-keys"><kbd id="key-
|
| 1163 |
-
<span class="ctrl-label">Pitch</span>
|
| 1164 |
</div>
|
| 1165 |
<div class="ctrl-row">
|
| 1166 |
<span class="ctrl-keys"><kbd id="key-a">A</kbd><kbd id="key-d">D</kbd></span>
|
| 1167 |
-
<span class="ctrl-label">Roll</span>
|
| 1168 |
-
</div>
|
| 1169 |
-
<div class="ctrl-row">
|
| 1170 |
-
<span class="ctrl-keys"><kbd id="key-q">Q</kbd><kbd id="key-e">E</kbd></span>
|
| 1171 |
<span class="ctrl-label">Yaw</span>
|
| 1172 |
</div>
|
| 1173 |
<div class="ctrl-row">
|
|
|
|
| 1159 |
<div class="controls-tip glass">
|
| 1160 |
<div class="tip-title">Controls</div>
|
| 1161 |
<div class="ctrl-row">
|
| 1162 |
+
<span class="ctrl-keys"><kbd id="key-mouse">MOUSE</kbd></span>
|
| 1163 |
+
<span class="ctrl-label">Pitch/Roll</span>
|
| 1164 |
</div>
|
| 1165 |
<div class="ctrl-row">
|
| 1166 |
<span class="ctrl-keys"><kbd id="key-a">A</kbd><kbd id="key-d">D</kbd></span>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1167 |
<span class="ctrl-label">Yaw</span>
|
| 1168 |
</div>
|
| 1169 |
<div class="ctrl-row">
|
src/Aircraft.js
CHANGED
|
@@ -247,6 +247,30 @@ export class Aircraft {
|
|
| 247 |
setupControls() {
|
| 248 |
document.addEventListener('keydown', (event) => this.handleKeyDown(event));
|
| 249 |
document.addEventListener('keyup', (event) => this.handleKeyUp(event));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 250 |
}
|
| 251 |
|
| 252 |
handleKeyDown(event) {
|
|
@@ -263,12 +287,8 @@ export class Aircraft {
|
|
| 263 |
return;
|
| 264 |
}
|
| 265 |
switch (event.key.toLowerCase()) {
|
| 266 |
-
case '
|
| 267 |
-
case '
|
| 268 |
-
case 'a': this.roll = -1; break; // Roll left (left wing down)
|
| 269 |
-
case 'd': this.roll = 1; break; // Roll right (right wing down)
|
| 270 |
-
case 'q': this.yaw = 1; break; // Yaw left (turn left)
|
| 271 |
-
case 'e': this.yaw = -1; break; // Yaw right (turn right)
|
| 272 |
}
|
| 273 |
}
|
| 274 |
|
|
@@ -286,9 +306,7 @@ export class Aircraft {
|
|
| 286 |
return;
|
| 287 |
}
|
| 288 |
switch (event.key.toLowerCase()) {
|
| 289 |
-
case '
|
| 290 |
-
case 'a': case 'd': this.roll = 0; break;
|
| 291 |
-
case 'q': case 'e': this.yaw = 0; break;
|
| 292 |
}
|
| 293 |
}
|
| 294 |
|
|
@@ -312,13 +330,21 @@ export class Aircraft {
|
|
| 312 |
// Banked turns: roll naturally induces yaw at speed
|
| 313 |
this.rotation.y -= this.rotation.z * this.turnAssist * speedRatio * deltaTime;
|
| 314 |
|
| 315 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 316 |
this.rotation.x = THREE.MathUtils.clamp(this.rotation.x, -1.1, 1.1);
|
| 317 |
this.rotation.z = THREE.MathUtils.clamp(this.rotation.z, -1.2, 1.2);
|
| 318 |
-
|
|
|
|
|
|
|
| 319 |
this.rotation.z = THREE.MathUtils.lerp(this.rotation.z, 0, deltaTime * 0.8);
|
| 320 |
}
|
| 321 |
-
if (this.pitch
|
| 322 |
this.rotation.x = THREE.MathUtils.lerp(this.rotation.x, 0, deltaTime * 0.3);
|
| 323 |
}
|
| 324 |
|
|
|
|
| 247 |
setupControls() {
|
| 248 |
document.addEventListener('keydown', (event) => this.handleKeyDown(event));
|
| 249 |
document.addEventListener('keyup', (event) => this.handleKeyUp(event));
|
| 250 |
+
|
| 251 |
+
// Set up mouse flight controls
|
| 252 |
+
document.addEventListener('mousemove', (event) => this.handleMouseMove(event));
|
| 253 |
+
|
| 254 |
+
// Request pointer lock on click to capture mouse movement infinitely
|
| 255 |
+
document.addEventListener('click', () => {
|
| 256 |
+
if (document.pointerLockElement !== document.body) {
|
| 257 |
+
document.body.requestPointerLock();
|
| 258 |
+
}
|
| 259 |
+
});
|
| 260 |
+
}
|
| 261 |
+
|
| 262 |
+
handleMouseMove(event) {
|
| 263 |
+
// Only apply mouse controls if pointer is locked (Simulator focused)
|
| 264 |
+
if (document.pointerLockElement === document.body) {
|
| 265 |
+
const mouseSensX = 0.005;
|
| 266 |
+
const mouseSensY = 0.005;
|
| 267 |
+
|
| 268 |
+
// Map mouse Y to Pitch: inverted movementY so mouse Up = plane nose Up
|
| 269 |
+
this.pitch = THREE.MathUtils.clamp(this.pitch - event.movementY * mouseSensY, -1, 1);
|
| 270 |
+
|
| 271 |
+
// Map mouse X to Roll
|
| 272 |
+
this.roll = THREE.MathUtils.clamp(this.roll + event.movementX * mouseSensX, -1, 1);
|
| 273 |
+
}
|
| 274 |
}
|
| 275 |
|
| 276 |
handleKeyDown(event) {
|
|
|
|
| 287 |
return;
|
| 288 |
}
|
| 289 |
switch (event.key.toLowerCase()) {
|
| 290 |
+
case 'a': this.yaw = 1; break; // Yaw left (turn left)
|
| 291 |
+
case 'd': this.yaw = -1; break; // Yaw right (turn right)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 292 |
}
|
| 293 |
}
|
| 294 |
|
|
|
|
| 306 |
return;
|
| 307 |
}
|
| 308 |
switch (event.key.toLowerCase()) {
|
| 309 |
+
case 'a': case 'd': this.yaw = 0; break;
|
|
|
|
|
|
|
| 310 |
}
|
| 311 |
}
|
| 312 |
|
|
|
|
| 330 |
// Banked turns: roll naturally induces yaw at speed
|
| 331 |
this.rotation.y -= this.rotation.z * this.turnAssist * speedRatio * deltaTime;
|
| 332 |
|
| 333 |
+
// Apply drag to the sticks so they recenter automatically if mouse stops moving
|
| 334 |
+
if (document.pointerLockElement === document.body) {
|
| 335 |
+
this.pitch = THREE.MathUtils.lerp(this.pitch, 0, deltaTime * 2.0);
|
| 336 |
+
this.roll = THREE.MathUtils.lerp(this.roll, 0, deltaTime * 2.0);
|
| 337 |
+
}
|
| 338 |
+
|
| 339 |
+
// Clamp pitch/roll rotation limits and gently stabilize when no input
|
| 340 |
this.rotation.x = THREE.MathUtils.clamp(this.rotation.x, -1.1, 1.1);
|
| 341 |
this.rotation.z = THREE.MathUtils.clamp(this.rotation.z, -1.2, 1.2);
|
| 342 |
+
|
| 343 |
+
// Auto-leveling logic
|
| 344 |
+
if (Math.abs(this.roll) < 0.05) {
|
| 345 |
this.rotation.z = THREE.MathUtils.lerp(this.rotation.z, 0, deltaTime * 0.8);
|
| 346 |
}
|
| 347 |
+
if (Math.abs(this.pitch) < 0.05) {
|
| 348 |
this.rotation.x = THREE.MathUtils.lerp(this.rotation.x, 0, deltaTime * 0.3);
|
| 349 |
}
|
| 350 |
|
src/main.js
CHANGED
|
@@ -132,6 +132,12 @@ class FlightSimulator {
|
|
| 132 |
setupInputIndicators() {
|
| 133 |
window.addEventListener('keydown', (event) => this.handleKeyHighlight(event, true));
|
| 134 |
window.addEventListener('keyup', (event) => this.handleKeyHighlight(event, false));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
window.addEventListener('blur', () => {
|
| 136 |
this.activeKeys.clear();
|
| 137 |
this.renderInputHighlights();
|
|
@@ -145,6 +151,10 @@ class FlightSimulator {
|
|
| 145 |
else if (key === 'ArrowUp') key = 'arrowup';
|
| 146 |
else if (key === 'ArrowDown') key = 'arrowdown';
|
| 147 |
else key = key.toLowerCase();
|
|
|
|
|
|
|
|
|
|
|
|
|
| 148 |
if (!Object.prototype.hasOwnProperty.call(this.keyElements, key)) return;
|
| 149 |
if (isPressed) {
|
| 150 |
this.activeKeys.add(key);
|
|
|
|
| 132 |
setupInputIndicators() {
|
| 133 |
window.addEventListener('keydown', (event) => this.handleKeyHighlight(event, true));
|
| 134 |
window.addEventListener('keyup', (event) => this.handleKeyHighlight(event, false));
|
| 135 |
+
window.addEventListener('mousedown', () => {
|
| 136 |
+
if (this.keyElements['mouse']) this.keyElements['mouse'].classList.add('active');
|
| 137 |
+
});
|
| 138 |
+
window.addEventListener('mouseup', () => {
|
| 139 |
+
if (this.keyElements['mouse']) this.keyElements['mouse'].classList.remove('active');
|
| 140 |
+
});
|
| 141 |
window.addEventListener('blur', () => {
|
| 142 |
this.activeKeys.clear();
|
| 143 |
this.renderInputHighlights();
|
|
|
|
| 151 |
else if (key === 'ArrowUp') key = 'arrowup';
|
| 152 |
else if (key === 'ArrowDown') key = 'arrowdown';
|
| 153 |
else key = key.toLowerCase();
|
| 154 |
+
|
| 155 |
+
// Ignore W and S since they are no longer used for flight
|
| 156 |
+
if (key === 'w' || key === 's') return;
|
| 157 |
+
|
| 158 |
if (!Object.prototype.hasOwnProperty.call(this.keyElements, key)) return;
|
| 159 |
if (isPressed) {
|
| 160 |
this.activeKeys.add(key);
|