217 lines
6.6 KiB
C#
217 lines
6.6 KiB
C#
// ThrottleLever.cs
|
|
// Zweck: XR-Hebel entlang definierter Rail bewegen, Output 0..1 als throttle. Stabil gegen XRI/Physik.
|
|
using UnityEngine;
|
|
using UnityEngine.XR.Interaction.Toolkit;
|
|
using UnityEngine.XR.Interaction.Toolkit.Interactables;
|
|
using UnityEngine.XR.Interaction.Toolkit.Interactors;
|
|
|
|
[DefaultExecutionOrder(1000)] // nach den meisten Systemen laufen
|
|
[RequireComponent(typeof(XRGrabInteractable))]
|
|
public class ThrottleLever : MonoBehaviour
|
|
{
|
|
[SerializeField] private bool forceStartAtZero = true;
|
|
|
|
[Header("References")]
|
|
public Transform handle; // visueller Hebel
|
|
public Transform startPoint; // Rail-Anfang
|
|
public Transform endPoint; // Rail-Ende
|
|
public Transform space; // Referenzrahmen der Rail
|
|
[SerializeField] private Transform shipRoot;
|
|
|
|
[Header("Output")]
|
|
[Range(0f, 1f)] public float throttle = 0f;
|
|
|
|
[Header("Diagnostics")]
|
|
public bool selfTestMode = false;
|
|
public float selfTestSpeed = 0.5f;
|
|
|
|
private XRGrabInteractable grab;
|
|
private bool isGrabbed;
|
|
private Transform follow; // Attach des Interactors
|
|
private float cachedT;
|
|
|
|
private void Awake()
|
|
{
|
|
grab = GetComponent<XRGrabInteractable>();
|
|
|
|
if (!handle || !startPoint || !endPoint)
|
|
{
|
|
Debug.LogError("[ThrottleLever] handle/start/end zuweisen.");
|
|
enabled = false; return;
|
|
}
|
|
|
|
if (!space)
|
|
space = startPoint && startPoint.parent ? startPoint.parent : handle.parent;
|
|
|
|
// sicherstellen, dass space mit dem Schiff mitfährt
|
|
if (shipRoot && space && !space.IsChildOf(shipRoot))
|
|
{
|
|
Vector3 wp = space.position;
|
|
Quaternion wq = space.rotation;
|
|
space.SetParent(shipRoot, worldPositionStays: true);
|
|
space.position = wp;
|
|
space.rotation = wq;
|
|
Debug.LogWarning("[ThrottleLever] 'space' unter shipRoot angehängt (fährt nun mit).");
|
|
}
|
|
|
|
// Rail-Validierung
|
|
var a = space.InverseTransformPoint(startPoint.position);
|
|
var b = space.InverseTransformPoint(endPoint.position);
|
|
if ((b - a).sqrMagnitude < 1e-6f)
|
|
{
|
|
Debug.LogError("[ThrottleLever] startPoint und endPoint liegen aufeinander.");
|
|
enabled = false; return;
|
|
}
|
|
|
|
// Physik am Handle (statisch, wir bewegen ihn selbst)
|
|
var rb = handle.GetComponent<Rigidbody>() ?? handle.gameObject.AddComponent<Rigidbody>();
|
|
rb.useGravity = false;
|
|
rb.isKinematic = true;
|
|
|
|
if (!handle.GetComponent<Collider>()) handle.gameObject.AddComponent<BoxCollider>();
|
|
|
|
// XRI soll unsere Pose NICHT direkt treiben
|
|
grab.trackPosition = false;
|
|
grab.trackRotation = false;
|
|
grab.throwOnDetach = false;
|
|
|
|
grab.selectEntered.AddListener(OnGrab);
|
|
grab.selectExited.AddListener(OnRelease);
|
|
|
|
Application.onBeforeRender += OnBeforeRenderPose;
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
if (grab)
|
|
{
|
|
grab.selectEntered.RemoveListener(OnGrab);
|
|
grab.selectExited.RemoveListener(OnRelease);
|
|
}
|
|
Application.onBeforeRender -= OnBeforeRenderPose;
|
|
}
|
|
|
|
private void Start()
|
|
{
|
|
if (forceStartAtZero)
|
|
{
|
|
cachedT = 0f; throttle = 0f;
|
|
SetHandleByT(cachedT);
|
|
}
|
|
else
|
|
{
|
|
ProjectHandleOntoRailFromCurrent();
|
|
}
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (!space) return;
|
|
|
|
Vector3 a = space.InverseTransformPoint(startPoint.position);
|
|
Vector3 b = space.InverseTransformPoint(endPoint.position);
|
|
Vector3 ab = b - a;
|
|
float abLen2 = ab.sqrMagnitude;
|
|
if (abLen2 < 1e-6f) return;
|
|
|
|
if (selfTestMode)
|
|
{
|
|
cachedT = 0.5f + 0.5f * Mathf.Sin(Time.time * selfTestSpeed * Mathf.PI * 2f);
|
|
}
|
|
else if (isGrabbed && follow)
|
|
{
|
|
Vector3 pLocal = space.InverseTransformPoint(follow.position);
|
|
cachedT = Mathf.Clamp01(Vector3.Dot(pLocal - a, ab) / abLen2);
|
|
}
|
|
else
|
|
{
|
|
Vector3 hLocal = space.InverseTransformPoint(handle.position);
|
|
cachedT = Mathf.Clamp01(Vector3.Dot(hLocal - a, ab) / abLen2);
|
|
}
|
|
|
|
throttle = cachedT;
|
|
|
|
// Vorläufig schreiben…
|
|
SetHandleByT(cachedT);
|
|
// …und später noch einmal absichern
|
|
}
|
|
|
|
private void LateUpdate() => SetHandleByT(cachedT);
|
|
|
|
private void OnBeforeRenderPose() => SetHandleByT(cachedT);
|
|
|
|
private void SetHandleByT(float t)
|
|
{
|
|
Vector3 a = space.InverseTransformPoint(startPoint.position);
|
|
Vector3 b = space.InverseTransformPoint(endPoint.position);
|
|
Vector3 targetLocal = Vector3.Lerp(a, b, Mathf.Clamp01(t));
|
|
handle.position = space.TransformPoint(targetLocal);
|
|
}
|
|
|
|
public void SetNormalized(float t)
|
|
{
|
|
t = Mathf.Clamp01(t);
|
|
cachedT = throttle = t;
|
|
SetHandleByT(t);
|
|
}
|
|
|
|
public void ResetToZero() => SetNormalized(0f);
|
|
|
|
private void OnGrab(SelectEnterEventArgs args)
|
|
{
|
|
isGrabbed = true;
|
|
|
|
if (args.interactorObject is XRBaseInteractor xrInteractor)
|
|
follow = xrInteractor.attachTransform;
|
|
else
|
|
follow = args.interactorObject.GetAttachTransform(args.interactableObject);
|
|
|
|
if (!follow)
|
|
Debug.LogWarning("[ThrottleLever] attachTransform nicht gefunden.");
|
|
|
|
if (space && handle.parent != space)
|
|
handle.SetParent(space, true);
|
|
|
|
grab.trackPosition = false;
|
|
grab.trackRotation = false;
|
|
}
|
|
|
|
private void OnRelease(SelectExitEventArgs args)
|
|
{
|
|
isGrabbed = false;
|
|
follow = null;
|
|
ProjectHandleOntoRailFromCurrent();
|
|
|
|
if (space && handle.parent != space)
|
|
handle.SetParent(space, true);
|
|
}
|
|
|
|
private void ProjectHandleOntoRailFromCurrent()
|
|
{
|
|
Vector3 a = space.InverseTransformPoint(startPoint.position);
|
|
Vector3 b = space.InverseTransformPoint(endPoint.position);
|
|
Vector3 ab = b - a; float abLen2 = ab.sqrMagnitude;
|
|
if (abLen2 < 1e-6f) return;
|
|
|
|
Vector3 hLocal = space.InverseTransformPoint(handle.position);
|
|
float t = Mathf.Clamp01(Vector3.Dot(hLocal - a, ab) / abLen2);
|
|
throttle = cachedT = t;
|
|
SetHandleByT(t);
|
|
}
|
|
|
|
public void SetGrabEnabled(bool enabled)
|
|
{
|
|
if (!grab) grab = GetComponent<XRGrabInteractable>();
|
|
if (grab) grab.enabled = enabled;
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
private void OnDrawGizmosSelected()
|
|
{
|
|
if (!startPoint || !endPoint) return;
|
|
Gizmos.color = Color.yellow;
|
|
Gizmos.DrawLine(startPoint.position, endPoint.position);
|
|
}
|
|
#endif
|
|
}
|