291 lines
9.0 KiB
C#
291 lines
9.0 KiB
C#
// CockpitTransitionController.cs
|
|
// Zweck: Übergang Hangar <-> Cockpit (Fade, Rig umhängen), Modus/Interfaces schalten, Gameplay/Tutorial starten.
|
|
using System.Collections;
|
|
using Unity.XR.CoreUtils;
|
|
using UnityEngine;
|
|
using UnityEngine.XR.Interaction.Toolkit;
|
|
using UnityEngine.XR.Interaction.Toolkit.Locomotion;
|
|
using UnityEngine.XR.Interaction.Toolkit.Locomotion.Movement;
|
|
|
|
public class CockpitTransitionController : MonoBehaviour
|
|
{
|
|
// Rig
|
|
[SerializeField] private XROrigin xrOrigin;
|
|
[SerializeField] private Transform cameraOffset;
|
|
[SerializeField] private ContinuousMoveProvider moveProvider;
|
|
[SerializeField] private CharacterController playerCC;
|
|
[SerializeField] private GameObject xrDeviceSimulator;
|
|
[SerializeField] private XRBodyTransformer bodyTransformer;
|
|
[SerializeField] private UnityEngine.XR.Interaction.Toolkit.Interactors.XRBaseInputInteractor rightHandNearFar;
|
|
|
|
// Schiff
|
|
[SerializeField] private Transform shipRoot;
|
|
[SerializeField] private ShipMovement shipMovement;
|
|
|
|
// Welt-Anker
|
|
[SerializeField] private Transform hangarSpawnAnchor;
|
|
[SerializeField] private Transform cockpitSeatAnchor;
|
|
|
|
// Fade
|
|
[SerializeField] private CanvasGroup fadeCanvas;
|
|
[SerializeField] private float fadeDuration = 0.6f;
|
|
|
|
// UI/Modus
|
|
[SerializeField] private GameObject rightHandRay;
|
|
[SerializeField] private GameObject hangarUI;
|
|
[SerializeField] private GameObject cockpitUI;
|
|
[SerializeField] private CockpitTutorialUI tutorialUI;
|
|
[SerializeField] private CockpitMenuController menuController;
|
|
|
|
// Tutorial
|
|
[SerializeField] private TutorialFlightController flightTutorial;
|
|
|
|
// Gates
|
|
[SerializeField] private GateSpawner gateSpawner;
|
|
|
|
private bool inCockpit;
|
|
private bool gameplayActive;
|
|
private bool isTransitioning;
|
|
|
|
private void Awake()
|
|
{
|
|
if (fadeCanvas) fadeCanvas.alpha = 0f;
|
|
|
|
gameplayActive = false;
|
|
|
|
// Schiffssysteme anfangs aus
|
|
if (shipMovement) shipMovement.SetActive(false);
|
|
|
|
UpdateMode(true);
|
|
}
|
|
|
|
// -> Cockpit
|
|
public void EnterCockpit()
|
|
{
|
|
if (inCockpit || isTransitioning) return;
|
|
StartCoroutine(DoTransition(true));
|
|
}
|
|
|
|
// <- Hangar (optional)
|
|
public void ExitCockpit()
|
|
{
|
|
if (!inCockpit || isTransitioning) return;
|
|
StartCoroutine(DoTransition(false));
|
|
}
|
|
|
|
private IEnumerator DoTransition(bool enter)
|
|
{
|
|
isTransitioning = true;
|
|
|
|
// Pre
|
|
UpdateMode(false);
|
|
|
|
// Fade + Input blocken
|
|
yield return FadeTo(1f, blockInput: true);
|
|
|
|
// CC/Body kurz deaktivieren (Verkanten vermeiden)
|
|
SetCharacterControllerActive(false);
|
|
|
|
if (enter) AttachRigToShip();
|
|
else { DetachRigFromShip(); gameplayActive = false; }
|
|
|
|
inCockpit = enter;
|
|
|
|
// CC/Body nur außerhalb Cockpit
|
|
SetCharacterControllerActive(!inCockpit);
|
|
|
|
// Post
|
|
UpdateMode(true);
|
|
|
|
// Fade zurück
|
|
yield return FadeTo(0f, blockInput: false);
|
|
|
|
isTransitioning = false;
|
|
}
|
|
|
|
private void SetCharacterControllerActive(bool active)
|
|
{
|
|
if (playerCC) playerCC.enabled = active;
|
|
if (bodyTransformer) bodyTransformer.enabled = active;
|
|
}
|
|
|
|
private IEnumerator FadeTo(float targetAlpha, bool blockInput)
|
|
{
|
|
if (!fadeCanvas) yield break;
|
|
|
|
if (!fadeCanvas.gameObject.activeSelf)
|
|
fadeCanvas.gameObject.SetActive(true);
|
|
|
|
fadeCanvas.blocksRaycasts = blockInput;
|
|
fadeCanvas.interactable = blockInput;
|
|
|
|
float start = fadeCanvas.alpha;
|
|
float elapsed = 0f;
|
|
|
|
while (elapsed < fadeDuration)
|
|
{
|
|
elapsed += Time.deltaTime;
|
|
float t = Mathf.Clamp01(elapsed / fadeDuration);
|
|
fadeCanvas.alpha = Mathf.Lerp(start, targetAlpha, t);
|
|
yield return null;
|
|
}
|
|
|
|
fadeCanvas.alpha = targetAlpha;
|
|
|
|
if (Mathf.Approximately(targetAlpha, 0f))
|
|
{
|
|
fadeCanvas.blocksRaycasts = false;
|
|
fadeCanvas.interactable = false;
|
|
fadeCanvas.gameObject.SetActive(false);
|
|
}
|
|
}
|
|
|
|
// UI/Steuerungsmodi schalten
|
|
private void UpdateMode(bool afterTransition)
|
|
{
|
|
bool enableLocomotion = !inCockpit;
|
|
bool enableShipSystems = inCockpit && gameplayActive;
|
|
|
|
if (moveProvider) moveProvider.enabled = enableLocomotion;
|
|
if (shipMovement) shipMovement.SetActive(enableShipSystems);
|
|
if (rightHandRay) rightHandRay.SetActive(inCockpit && !gameplayActive);
|
|
if (hangarUI) hangarUI.SetActive(!inCockpit);
|
|
if (cockpitUI) cockpitUI.SetActive(inCockpit);
|
|
|
|
if (tutorialUI)
|
|
{
|
|
if (inCockpit && !gameplayActive && afterTransition) tutorialUI.BeginTutorial();
|
|
else tutorialUI.CloseTutorial();
|
|
}
|
|
|
|
if (menuController)
|
|
{
|
|
if (!inCockpit || gameplayActive) menuController.HideMenu();
|
|
}
|
|
}
|
|
|
|
public void OnTutorialFinished()
|
|
{
|
|
StartCoroutine(ShowMenuNextFrame());
|
|
}
|
|
|
|
private IEnumerator ShowMenuNextFrame()
|
|
{
|
|
// Einen Frame warten, damit Schließen/Fade fertig ist
|
|
yield return null;
|
|
menuController?.ShowMenu();
|
|
}
|
|
|
|
// Gameplay nach Moduswahl starten
|
|
private void BeginGameplay()
|
|
{
|
|
UpdateMode(true);
|
|
gateSpawner?.BeginGame();
|
|
|
|
gameplayActive = true;
|
|
menuController?.HideMenu();
|
|
|
|
if (rightHandRay) rightHandRay.SetActive(false);
|
|
UpdateMode(true);
|
|
}
|
|
|
|
// Buttons (Passthroughs)
|
|
public void StartSequentialMode() { Debug.Log("Mode A selected"); BeginGameplay(); }
|
|
public void StartParallelMode() { Debug.Log("Mode B selected"); BeginGameplay(); }
|
|
|
|
public void StartTutorialMode()
|
|
{
|
|
Debug.Log("Tutorial practice selected");
|
|
|
|
// Tutorial als Gameplay behandeln (Bewegung an)
|
|
gameplayActive = true;
|
|
|
|
gateSpawner?.ClearAll();
|
|
menuController?.HideMenu();
|
|
UpdateMode(true);
|
|
|
|
if (flightTutorial != null && shipMovement != null && gateSpawner != null)
|
|
flightTutorial.BeginTutorial(this, shipMovement, gateSpawner);
|
|
else
|
|
Debug.LogWarning("[CockpitTransitionController] TutorialFlightController oder Abhängigkeiten fehlen.");
|
|
}
|
|
|
|
// Rig ans Schiff hängen, Sitzposition ausrichten
|
|
private void AttachRigToShip()
|
|
{
|
|
if (!xrOrigin || !shipMovement) return;
|
|
|
|
var mover = shipMovement.MoveRoot;
|
|
if (!mover) return;
|
|
|
|
// 0) Parent unter den Mover (Pose beibehalten)
|
|
xrOrigin.transform.SetParent(mover, worldPositionStays: true);
|
|
|
|
// 1) Tracking-Origin "Device" (seated)
|
|
#if UNITY_XR_CORE_UTILS_2_4_OR_NEWER
|
|
xrOrigin.RequestedTrackingOriginMode = UnityEngine.XR.TrackingOriginModeFlags.Device;
|
|
xrOrigin.CameraYOffset = 0f;
|
|
#else
|
|
xrOrigin.RequestedTrackingOriginMode = XROrigin.TrackingOriginMode.Device;
|
|
if (xrOrigin.CameraFloorOffsetObject)
|
|
{
|
|
var off = xrOrigin.CameraFloorOffsetObject.transform;
|
|
off.localPosition = Vector3.zero;
|
|
off.localRotation = Quaternion.identity;
|
|
}
|
|
#endif
|
|
// 2) Offset zurücksetzen
|
|
if (cameraOffset)
|
|
{
|
|
cameraOffset.localPosition = Vector3.zero;
|
|
cameraOffset.localRotation = Quaternion.identity;
|
|
cameraOffset.localScale = Vector3.one;
|
|
}
|
|
|
|
// 3) Kamera exakt zur Sitz-Augenposition verschieben
|
|
var cam = xrOrigin.Camera;
|
|
if (!cam || !cockpitSeatAnchor) return;
|
|
|
|
const float eyeLift = 0.05f;
|
|
Vector3 targetCamPos = cockpitSeatAnchor.position + cockpitSeatAnchor.up * eyeLift;
|
|
xrOrigin.MoveCameraToWorldLocation(targetCamPos);
|
|
|
|
// 4) Yaw an Sitz ausrichten (um die Kameraposition rotieren)
|
|
Vector3 camF = cam.transform.forward; camF.y = 0f;
|
|
Vector3 seatF = cockpitSeatAnchor.forward; seatF.y = 0f;
|
|
if (camF.sqrMagnitude > 1e-4f && seatF.sqrMagnitude > 1e-4f)
|
|
{
|
|
camF.Normalize(); seatF.Normalize();
|
|
float deltaYaw = Vector3.SignedAngle(camF, seatF, Vector3.up);
|
|
xrOrigin.transform.RotateAround(cam.transform.position, Vector3.up, deltaYaw);
|
|
}
|
|
}
|
|
|
|
public void EndTutorialReturnToMenu()
|
|
{
|
|
// Tutorial vorbei: Schiff stoppen, Menü anzeigen
|
|
gameplayActive = false;
|
|
|
|
if (shipMovement) shipMovement.SetActive(false);
|
|
if (rightHandRay) rightHandRay.SetActive(true);
|
|
if (hangarUI) hangarUI.SetActive(false);
|
|
if (cockpitUI) cockpitUI.SetActive(true);
|
|
|
|
menuController?.ShowMenu();
|
|
}
|
|
|
|
// Rig vom Schiff lösen, in Hangar stellen
|
|
private void DetachRigFromShip()
|
|
{
|
|
if (!xrOrigin) return;
|
|
xrOrigin.transform.SetParent(null, true);
|
|
|
|
if (hangarSpawnAnchor)
|
|
{
|
|
xrOrigin.MoveCameraToWorldLocation(hangarSpawnAnchor.position);
|
|
var e = xrOrigin.transform.eulerAngles; e.y = hangarSpawnAnchor.eulerAngles.y;
|
|
xrOrigin.transform.eulerAngles = e;
|
|
}
|
|
}
|
|
}
|