SpaceQuestions_MarioKart/Assets/Scripts/ShipFeedbackController.cs
2025-12-11 13:14:43 +01:00

117 lines
3.9 KiB
C#

// ShipFeedbackController.cs
// Zweck: Visuelles/Haptisches Feedback bei richtigen/falschen Antworten (Partikel, Shake, Haptik).
using System.Collections;
using UnityEngine;
using UnityEngine.XR;
public class ShipFeedbackController : MonoBehaviour
{
[Header("Shake target")]
[SerializeField] private Transform shakeRoot; // zu schüttelndes Transform
[Header("Particles")]
[SerializeField] private ParticleSystem correctParticles; // Partikel bei richtig
[SerializeField] private ParticleSystem wrongParticles; // Partikel bei falsch
[Header("Haptics (XR Nodes)")]
[SerializeField] private bool useLeftHand = true;
[SerializeField] private bool useRightHand = true;
[SerializeField] private XRNode leftHandNode = XRNode.LeftHand;
[SerializeField] private XRNode rightHandNode = XRNode.RightHand;
[Header("Shake Settings (wrong answer)")]
[SerializeField] private float shakeDuration = 0.25f;
[SerializeField] private float positionIntensity = 0.25f;
[SerializeField] private float rotationIntensity = 6f;
[Header("Haptic Settings")]
[Range(0f, 1f)] [SerializeField] private float correctAmplitude = 0.3f;
[SerializeField] private float correctDuration = 0.12f;
[Range(0f, 1f)] [SerializeField] private float wrongAmplitude = 0.8f;
[SerializeField] private float wrongDuration = 0.25f;
private Coroutine shakeRoutine;
private void Awake()
{
// Fallback: eigenes Transform
if (!shakeRoot) shakeRoot = transform;
}
// Korrektes Tor
public void PlayCorrectFeedback()
{
if (correctParticles) correctParticles.Play();
PlayHaptics(correctAmplitude, correctDuration);
}
// Falsches Tor
public void PlayWrongFeedback()
{
if (wrongParticles) wrongParticles.Play();
if (shakeRoutine != null) StopCoroutine(shakeRoutine);
shakeRoutine = StartCoroutine(Co_Shake());
PlayHaptics(wrongAmplitude, wrongDuration);
}
// Haptik an beide Hände (optional)
private void PlayHaptics(float amplitude, float duration)
{
if (amplitude <= 0f || duration <= 0f) return;
if (useLeftHand) SendHapticToNode(leftHandNode, amplitude, duration);
if (useRightHand) SendHapticToNode(rightHandNode, amplitude, duration);
}
private void SendHapticToNode(XRNode node, float amplitude, float duration)
{
var device = InputDevices.GetDeviceAtXRNode(node);
if (!device.isValid) return;
if (device.TryGetHapticCapabilities(out var caps) && caps.supportsImpulse)
{
device.SendHapticImpulse(0u, amplitude, duration); // Kanal 0 = Standard
}
}
// Einfacher Shake mit Dämpfung
private IEnumerator Co_Shake()
{
if (!shakeRoot) yield break;
Vector3 originalPos = shakeRoot.localPosition;
Quaternion originalRot = shakeRoot.localRotation;
float t = 0f;
while (t < shakeDuration)
{
t += Time.deltaTime;
float n = Mathf.Clamp01(t / shakeDuration);
float damper = 1f - n;
float posMag = positionIntensity * damper;
float rotMag = rotationIntensity * damper;
Vector3 posOffset = new Vector3(
(Random.value - 0.5f) * 2f * posMag,
(Random.value - 0.5f) * 2f * posMag,
(Random.value - 0.5f) * 2f * posMag
);
Vector3 rotOffset = new Vector3(
(Random.value - 0.5f) * 2f * rotMag,
(Random.value - 0.5f) * 2f * rotMag,
(Random.value - 0.5f) * 2f * rotMag
);
shakeRoot.localPosition = originalPos + posOffset;
shakeRoot.localRotation = originalRot * Quaternion.Euler(rotOffset);
yield return null;
}
shakeRoot.localPosition = originalPos;
shakeRoot.localRotation = originalRot;
shakeRoutine = null;
}
}