diff --git a/App/sims/klima/game-3d.html b/App/sims/klima/game-3d.html index 6defffd..f7b63f8 100644 --- a/App/sims/klima/game-3d.html +++ b/App/sims/klima/game-3d.html @@ -4082,55 +4082,57 @@ function updateSingleHeli(v, tNow, airportMesh) { function updateSinglePlane(v, tNow, airportMesh) { const plane = v.plane; const rotY = airportMesh.rotation.y; - const RUNWAY_END = 2.2; // lokal, nach 2x-Scale also 4.4 in Welt - const RUNWAY_Y = 0.50; // auf Plateau-Oberkante (LIFT+Scale²≈0.26) + const RUNWAY_END = 2.2; + const RUNWAY_Y = 0.50; - // Zyklus-Phase — beginnt sofort in Phase 0 (Plane steht auf Runway), - // damit beim Bau des Airports das Flugzeug direkt sichtbar ist. const tPhase = (tNow + v.phaseOffset) % PLANE_CYCLE; plane.visible = true; const prop = plane.userData.propRef; - // Hilfsfunktion: Plane an lokal (lx, ly, lz) mit Rotation-Offset um Y - function place(lx, ly, lz, pitch, extraYaw) { + // place: Nase zeigt IMMER in Airport-lokales +X (= Start- und Lande- + // Richtung, vom Vulkan weg). Banking via roll (rotation.z). + function place(lx, ly, lz, pitch, roll) { const w = airportLocalToWorld(airportMesh, lx, ly, lz); plane.position.set(w.x, w.y, w.z); - // Basis-Yaw = airportRotY + extraYaw (π für Rollen nach -X, 0 für +X) - plane.rotation.set(pitch || 0, rotY + (extraYaw || 0), 0); + plane.rotation.set(pitch || 0, rotY, roll || 0); } if (tPhase < 14) { - // Parken am Runway-Anfang (+X-Ende lokal), Nase zeigt nach Startrichtung (-X) - place(RUNWAY_END - 0.3, RUNWAY_Y, 0, 0, Math.PI); + // Parken am Runway-Anfang (-X-Ende), Nase zeigt +X + place(-(RUNWAY_END - 0.3), RUNWAY_Y, 0, 0, 0); } else if (tPhase < 18) { - // Takeoff-Roll: rollt von +X nach -X, hebt am Ende leicht ab + // Takeoff-Roll: -X → +X, am Ende leicht abheben const t = (tPhase - 14) / 4; - const lx = (RUNWAY_END - 0.3) - t * (2 * RUNWAY_END - 0.6); - const ly = RUNWAY_Y + (t > 0.75 ? (t - 0.75) * 0.6 : 0); - place(lx, ly, 0, -t * 0.2, Math.PI); - } else if (tPhase < 26) { - // Climb-Out: steigt auf, entfernt sich weiter in -X-lokal - const t = (tPhase - 18) / 8; - const lx = -(RUNWAY_END - 0.3) - t * 18; - const ly = RUNWAY_Y + 0.15 + t * 4.0; - place(lx, ly, 0, -0.28, Math.PI); - } else if (tPhase < 80) { - // Unterwegs — unsichtbar (kommt von weit draußen zurück) - plane.visible = false; - } else if (tPhase < 90) { - // Approach: kommt in -X-lokal auf die Landung zu, sinkt - const t = (tPhase - 80) / 10; - const lx = -22 + t * (22 - (RUNWAY_END + 0.3)); - const ly = 4.2 - t * 3.7; // 4.2 → 0.5 - place(lx, ly, 0, 0.15 - t * 0.15, Math.PI); - } else if (tPhase < 94) { - // Landing-Roll: von -X nach +X mit leichtem Pitch-Up am Anfang, dann flach - const t = (tPhase - 90) / 4; const lx = -(RUNWAY_END - 0.3) + t * (2 * RUNWAY_END - 0.6); - place(lx, RUNWAY_Y, 0, 0, Math.PI); + const ly = RUNWAY_Y + (t > 0.75 ? (t - 0.75) * 0.6 : 0); + place(lx, ly, 0, -t * 0.2, 0); + } else if (tPhase < 26) { + // Climb-Out: weiter +X, steigt auf, ab t=0.5 bankt es leicht für die + // Kurve (rotation.z Roll, sichtbar seitwärts kippend vorm Abtauchen) + const t = (tPhase - 18) / 8; + const lx = (RUNWAY_END - 0.3) + t * 18; + const ly = RUNWAY_Y + 0.15 + t * 4.0; + const roll = t > 0.5 ? (t - 0.5) * 0.9 : 0; + place(lx, ly, 0, -0.28, roll); + } else if (tPhase < 76) { + // Unterwegs — unsichtbar (fliegt irgendwo in der Ferne den Bogen) + plane.visible = false; + } else if (tPhase < 84) { + // Approach: von -X-Ferne zurück zum Landeanflug, Banking löst sich + // langsam auf (Kurveneinleitung rückwärts interpretiert) + const t = (tPhase - 76) / 8; + const lx = -22 + t * (22 - (RUNWAY_END + 0.3)); // -22 → -2.5 + const ly = 4.2 - t * 3.7; + const roll = t < 0.5 ? -(0.5 - t) * 0.9 : 0; // Linkskurven-Roll raus + place(lx, ly, 0, 0.15 - t * 0.15, roll); + } else if (tPhase < 88) { + // Landing-Roll: -X-Ende → +X-Ende (selbe Richtung wie Takeoff) + const t = (tPhase - 84) / 4; + const lx = -(RUNWAY_END - 0.3) + t * (2 * RUNWAY_END - 0.6); + place(lx, RUNWAY_Y, 0, 0, 0); } else { - // Taxi + parken (zurück in Startposition) - place(RUNWAY_END - 0.3, RUNWAY_Y, 0, 0, Math.PI); + // Taxi zurück zur Parkposition — unsichtbar (Teleport OK, kurz) + plane.visible = false; } if (prop && plane.visible) prop.rotation.x = tPhase * 22; }