117 lines
3.9 KiB
C#
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;
|
|
}
|
|
}
|