Initial commit: Retro 90s flight simulator MVP
Loading screen, main menu, 3D flight sim with CRT post-processing, procedural terrain, airport with buildings, low-poly aircraft, flight physics, HUD instruments, and sound. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+219
@@ -0,0 +1,219 @@
|
||||
import * as THREE from 'three';
|
||||
|
||||
function flatMat(color, opts = {}) {
|
||||
return new THREE.MeshLambertMaterial({ color, flatShading: true, ...opts });
|
||||
}
|
||||
|
||||
export class Aircraft {
|
||||
constructor() {
|
||||
this.group = new THREE.Group();
|
||||
this.propeller = null;
|
||||
this.landingGear = [];
|
||||
this.gearExtended = true;
|
||||
this.build();
|
||||
}
|
||||
|
||||
build() {
|
||||
const bodyMat = flatMat(0x6688AA);
|
||||
const whiteMat = flatMat(0xFFFFFF);
|
||||
const redMat = flatMat(0xCC0000);
|
||||
const cockpitMat = flatMat(0x333333, { transparent: true, opacity: 0.6 });
|
||||
const darkMat = flatMat(0x333333);
|
||||
const metalMat = flatMat(0x888888);
|
||||
|
||||
// Fuselage
|
||||
const fuselage = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(0.6, 0.45, 9, 6),
|
||||
bodyMat
|
||||
);
|
||||
fuselage.rotation.x = Math.PI / 2;
|
||||
this.group.add(fuselage);
|
||||
|
||||
// Nose cone
|
||||
const nose = new THREE.Mesh(
|
||||
new THREE.SphereGeometry(0.4, 6, 4, 0, Math.PI * 2, 0, Math.PI / 2),
|
||||
bodyMat
|
||||
);
|
||||
nose.rotation.x = -Math.PI / 2;
|
||||
nose.position.z = -4.5;
|
||||
this.group.add(nose);
|
||||
|
||||
// Tail cone
|
||||
const tailCone = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(0.4, 0.15, 2.5, 6),
|
||||
bodyMat
|
||||
);
|
||||
tailCone.rotation.x = Math.PI / 2;
|
||||
tailCone.position.z = 5.5;
|
||||
this.group.add(tailCone);
|
||||
|
||||
// Cockpit canopy
|
||||
const canopy = new THREE.Mesh(
|
||||
new THREE.SphereGeometry(0.55, 6, 4, 0, Math.PI * 2, 0, Math.PI / 2),
|
||||
cockpitMat
|
||||
);
|
||||
canopy.position.set(0, 0.4, -1);
|
||||
this.group.add(canopy);
|
||||
|
||||
// Wings
|
||||
const wings = new THREE.Mesh(
|
||||
new THREE.BoxGeometry(14, 0.2, 2),
|
||||
bodyMat
|
||||
);
|
||||
wings.position.z = -0.5;
|
||||
this.group.add(wings);
|
||||
|
||||
// Wingtips (red)
|
||||
const leftTip = new THREE.Mesh(new THREE.BoxGeometry(0.8, 0.25, 1.6), redMat);
|
||||
leftTip.position.set(-6.9, 0, -0.5);
|
||||
this.group.add(leftTip);
|
||||
|
||||
const rightTip = leftTip.clone();
|
||||
rightTip.position.x = 6.9;
|
||||
this.group.add(rightTip);
|
||||
|
||||
// Ailerons (white strips on wing trailing edge)
|
||||
const leftAileron = new THREE.Mesh(new THREE.BoxGeometry(2, 0.15, 0.4), whiteMat);
|
||||
leftAileron.position.set(-5, -0.15, 0.3);
|
||||
this.group.add(leftAileron);
|
||||
|
||||
const rightAileron = leftAileron.clone();
|
||||
rightAileron.position.x = 5;
|
||||
this.group.add(rightAileron);
|
||||
|
||||
// Vertical stabilizer
|
||||
const vStab = new THREE.Mesh(
|
||||
new THREE.BoxGeometry(0.15, 2.5, 1.5),
|
||||
bodyMat
|
||||
);
|
||||
vStab.position.set(0, 1.2, 5.5);
|
||||
this.group.add(vStab);
|
||||
|
||||
// Rudder (white)
|
||||
const rudder = new THREE.Mesh(
|
||||
new THREE.BoxGeometry(0.12, 1.8, 0.5),
|
||||
whiteMat
|
||||
);
|
||||
rudder.position.set(0, 1.2, 6.3);
|
||||
this.group.add(rudder);
|
||||
|
||||
// Red tail band
|
||||
const tailBand = new THREE.Mesh(
|
||||
new THREE.BoxGeometry(0.12, 0.6, 1.2),
|
||||
redMat
|
||||
);
|
||||
tailBand.position.set(0, 1.8, 5.5);
|
||||
this.group.add(tailBand);
|
||||
|
||||
// Horizontal stabilizer
|
||||
const hStab = new THREE.Mesh(
|
||||
new THREE.BoxGeometry(5, 0.15, 1.2),
|
||||
bodyMat
|
||||
);
|
||||
hStab.position.set(0, 0.1, 5.5);
|
||||
this.group.add(hStab);
|
||||
|
||||
// Elevators (white)
|
||||
const leftElev = new THREE.Mesh(new THREE.BoxGeometry(1.8, 0.12, 0.4), whiteMat);
|
||||
leftElev.position.set(-2.5, -0.05, 6);
|
||||
this.group.add(leftElev);
|
||||
|
||||
const rightElev = leftElev.clone();
|
||||
rightElev.position.x = 2.5;
|
||||
this.group.add(rightElev);
|
||||
|
||||
// Engine cowling
|
||||
const cowling = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(0.45, 0.55, 1.5, 6),
|
||||
darkMat
|
||||
);
|
||||
cowling.rotation.x = Math.PI / 2;
|
||||
cowling.position.z = -5;
|
||||
this.group.add(cowling);
|
||||
|
||||
// Propeller
|
||||
this.propeller = new THREE.Group();
|
||||
const blade1 = new THREE.Mesh(
|
||||
new THREE.BoxGeometry(0.12, 3, 0.08),
|
||||
metalMat
|
||||
);
|
||||
this.propeller.add(blade1);
|
||||
const blade2 = new THREE.Mesh(
|
||||
new THREE.BoxGeometry(3, 0.12, 0.08),
|
||||
metalMat
|
||||
);
|
||||
this.propeller.add(blade2);
|
||||
// Hub
|
||||
const hub = new THREE.Mesh(
|
||||
new THREE.SphereGeometry(0.15, 4, 4),
|
||||
metalMat
|
||||
);
|
||||
this.propeller.add(hub);
|
||||
this.propeller.position.z = -5.8;
|
||||
this.group.add(this.propeller);
|
||||
|
||||
// Landing gear
|
||||
const gearMat = flatMat(0x222222);
|
||||
const wheelMat = flatMat(0x111111);
|
||||
|
||||
// Nose gear
|
||||
const noseGearStrut = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(0.06, 0.06, 2, 4),
|
||||
gearMat
|
||||
);
|
||||
noseGearStrut.position.set(0, -1.5, -3.5);
|
||||
this.group.add(noseGearStrut);
|
||||
const noseWheel = new THREE.Mesh(
|
||||
new THREE.SphereGeometry(0.2, 4, 4),
|
||||
wheelMat
|
||||
);
|
||||
noseWheel.position.set(0, -2.5, -3.5);
|
||||
this.group.add(noseWheel);
|
||||
this.landingGear.push(noseGearStrut, noseWheel);
|
||||
|
||||
// Left main gear
|
||||
const leftGearStrut = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(0.06, 0.06, 2, 4),
|
||||
gearMat
|
||||
);
|
||||
leftGearStrut.position.set(-2, -1.5, -1);
|
||||
this.group.add(leftGearStrut);
|
||||
const leftWheel = new THREE.Mesh(
|
||||
new THREE.SphereGeometry(0.25, 4, 4),
|
||||
wheelMat
|
||||
);
|
||||
leftWheel.position.set(-2, -2.5, -1);
|
||||
this.group.add(leftWheel);
|
||||
this.landingGear.push(leftGearStrut, leftWheel);
|
||||
|
||||
// Right main gear
|
||||
const rightGearStrut = leftGearStrut.clone();
|
||||
rightGearStrut.position.x = 2;
|
||||
this.group.add(rightGearStrut);
|
||||
const rightWheel = leftWheel.clone();
|
||||
rightWheel.position.x = 2;
|
||||
this.group.add(rightWheel);
|
||||
this.landingGear.push(rightGearStrut, rightWheel);
|
||||
|
||||
this.group.rotation.order = 'YXZ';
|
||||
}
|
||||
|
||||
updatePropeller(thrust, delta) {
|
||||
if (this.propeller) {
|
||||
this.propeller.rotation.z += thrust * delta * 60;
|
||||
}
|
||||
}
|
||||
|
||||
retractGear(onGround) {
|
||||
if (onGround && !this.gearExtended) {
|
||||
this.landingGear.forEach(part => { part.visible = true; });
|
||||
} else if (!onGround && this.gearExtended) {
|
||||
this.landingGear.forEach(part => { part.visible = false; });
|
||||
}
|
||||
this.gearExtended = onGround;
|
||||
}
|
||||
|
||||
getGroup() {
|
||||
return this.group;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user