A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.
This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).
Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
291 lines
8.7 KiB
C#
291 lines
8.7 KiB
C#
// ==============================================
|
|
// AudioManager.cs - Audio-Verwaltung
|
|
// ==============================================
|
|
// Verwaltet Musik, Sound-Effekte und
|
|
// Text-to-Speech fuer das Spiel.
|
|
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
namespace BreakpilotDrive
|
|
{
|
|
[System.Serializable]
|
|
public class SoundEffect
|
|
{
|
|
public string name;
|
|
public AudioClip clip;
|
|
[Range(0f, 1f)]
|
|
public float volume = 1f;
|
|
[Range(0.5f, 1.5f)]
|
|
public float pitch = 1f;
|
|
public bool loop = false;
|
|
}
|
|
|
|
public class AudioManager : MonoBehaviour
|
|
{
|
|
public static AudioManager Instance { get; private set; }
|
|
|
|
[Header("Audio Sources")]
|
|
[SerializeField] private AudioSource musicSource;
|
|
[SerializeField] private AudioSource sfxSource;
|
|
[SerializeField] private AudioSource voiceSource;
|
|
|
|
[Header("Musik")]
|
|
[SerializeField] private AudioClip menuMusic;
|
|
[SerializeField] private AudioClip gameMusic;
|
|
|
|
[Header("Sound-Effekte")]
|
|
[SerializeField] private SoundEffect[] soundEffects;
|
|
|
|
[Header("Lautstaerke")]
|
|
[Range(0f, 1f)]
|
|
[SerializeField] private float masterVolume = 1f;
|
|
[Range(0f, 1f)]
|
|
[SerializeField] private float musicVolume = 0.5f;
|
|
[Range(0f, 1f)]
|
|
[SerializeField] private float sfxVolume = 1f;
|
|
[Range(0f, 1f)]
|
|
[SerializeField] private float voiceVolume = 1f;
|
|
|
|
// Sound-Dictionary fuer schnellen Zugriff
|
|
private Dictionary<string, SoundEffect> soundDict = new Dictionary<string, SoundEffect>();
|
|
|
|
void Awake()
|
|
{
|
|
if (Instance == null)
|
|
{
|
|
Instance = this;
|
|
DontDestroyOnLoad(gameObject);
|
|
InitializeSounds();
|
|
LoadVolumeSettings();
|
|
}
|
|
else
|
|
{
|
|
Destroy(gameObject);
|
|
}
|
|
}
|
|
|
|
private void InitializeSounds()
|
|
{
|
|
soundDict.Clear();
|
|
foreach (var sound in soundEffects)
|
|
{
|
|
if (!string.IsNullOrEmpty(sound.name) && sound.clip != null)
|
|
{
|
|
soundDict[sound.name.ToLower()] = sound;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ==============================================
|
|
// Musik
|
|
// ==============================================
|
|
public void PlayMenuMusic()
|
|
{
|
|
PlayMusic(menuMusic);
|
|
}
|
|
|
|
public void PlayGameMusic()
|
|
{
|
|
PlayMusic(gameMusic);
|
|
}
|
|
|
|
public void PlayMusic(AudioClip clip)
|
|
{
|
|
if (musicSource == null || clip == null) return;
|
|
|
|
if (musicSource.clip == clip && musicSource.isPlaying)
|
|
return;
|
|
|
|
musicSource.clip = clip;
|
|
musicSource.volume = musicVolume * masterVolume;
|
|
musicSource.loop = true;
|
|
musicSource.Play();
|
|
}
|
|
|
|
public void StopMusic()
|
|
{
|
|
if (musicSource != null)
|
|
{
|
|
musicSource.Stop();
|
|
}
|
|
}
|
|
|
|
public void PauseMusic()
|
|
{
|
|
if (musicSource != null)
|
|
{
|
|
musicSource.Pause();
|
|
}
|
|
}
|
|
|
|
public void ResumeMusic()
|
|
{
|
|
if (musicSource != null)
|
|
{
|
|
musicSource.UnPause();
|
|
}
|
|
}
|
|
|
|
// ==============================================
|
|
// Sound-Effekte
|
|
// ==============================================
|
|
public void PlaySound(string soundName)
|
|
{
|
|
if (sfxSource == null) return;
|
|
|
|
string key = soundName.ToLower();
|
|
if (soundDict.TryGetValue(key, out SoundEffect sound))
|
|
{
|
|
sfxSource.pitch = sound.pitch;
|
|
sfxSource.PlayOneShot(sound.clip, sound.volume * sfxVolume * masterVolume);
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning($"Sound '{soundName}' nicht gefunden!");
|
|
}
|
|
}
|
|
|
|
public void PlaySound(AudioClip clip, float volume = 1f)
|
|
{
|
|
if (sfxSource == null || clip == null) return;
|
|
sfxSource.PlayOneShot(clip, volume * sfxVolume * masterVolume);
|
|
}
|
|
|
|
// Vordefinierte Sound-Methoden
|
|
public void PlayCoinSound() => PlaySound("coin");
|
|
public void PlayCrashSound() => PlaySound("crash");
|
|
public void PlayCorrectSound() => PlaySound("correct");
|
|
public void PlayWrongSound() => PlaySound("wrong");
|
|
public void PlayButtonSound() => PlaySound("button");
|
|
|
|
// ==============================================
|
|
// Text-to-Speech (WebGL)
|
|
// ==============================================
|
|
public void Speak(string text)
|
|
{
|
|
if (string.IsNullOrEmpty(text)) return;
|
|
|
|
#if UNITY_WEBGL && !UNITY_EDITOR
|
|
SpeakWebGL(text);
|
|
#else
|
|
Debug.Log($"[TTS] {text}");
|
|
#endif
|
|
}
|
|
|
|
public void SpeakQuestion(QuizQuestion question)
|
|
{
|
|
if (question == null) return;
|
|
|
|
Speak(question.question_text);
|
|
|
|
// Optionen vorlesen (mit Verzoegerung)
|
|
for (int i = 0; i < question.options.Length; i++)
|
|
{
|
|
string optionText = $"Option {i + 1}: {question.options[i]}";
|
|
// In WebGL: Verzoegerung ueber JavaScript
|
|
#if UNITY_WEBGL && !UNITY_EDITOR
|
|
SpeakDelayedWebGL(optionText, 1.5f + (i * 1.5f));
|
|
#else
|
|
Debug.Log($"[TTS] {optionText}");
|
|
#endif
|
|
}
|
|
}
|
|
|
|
public void SpeakFeedback(bool correct)
|
|
{
|
|
string message = correct ?
|
|
"Richtig! Gut gemacht!" :
|
|
"Nicht ganz. Versuch es nochmal!";
|
|
Speak(message);
|
|
}
|
|
|
|
public void StopSpeaking()
|
|
{
|
|
#if UNITY_WEBGL && !UNITY_EDITOR
|
|
StopSpeakingWebGL();
|
|
#endif
|
|
}
|
|
|
|
// WebGL JavaScript Interop
|
|
#if UNITY_WEBGL && !UNITY_EDITOR
|
|
[System.Runtime.InteropServices.DllImport("__Internal")]
|
|
private static extern void SpeakWebGL(string text);
|
|
|
|
[System.Runtime.InteropServices.DllImport("__Internal")]
|
|
private static extern void SpeakDelayedWebGL(string text, float delaySeconds);
|
|
|
|
[System.Runtime.InteropServices.DllImport("__Internal")]
|
|
private static extern void StopSpeakingWebGL();
|
|
#endif
|
|
|
|
// ==============================================
|
|
// Lautstaerke-Einstellungen
|
|
// ==============================================
|
|
public void SetMasterVolume(float volume)
|
|
{
|
|
masterVolume = Mathf.Clamp01(volume);
|
|
UpdateAllVolumes();
|
|
SaveVolumeSettings();
|
|
}
|
|
|
|
public void SetMusicVolume(float volume)
|
|
{
|
|
musicVolume = Mathf.Clamp01(volume);
|
|
if (musicSource != null)
|
|
{
|
|
musicSource.volume = musicVolume * masterVolume;
|
|
}
|
|
SaveVolumeSettings();
|
|
}
|
|
|
|
public void SetSFXVolume(float volume)
|
|
{
|
|
sfxVolume = Mathf.Clamp01(volume);
|
|
SaveVolumeSettings();
|
|
}
|
|
|
|
public void SetVoiceVolume(float volume)
|
|
{
|
|
voiceVolume = Mathf.Clamp01(volume);
|
|
if (voiceSource != null)
|
|
{
|
|
voiceSource.volume = voiceVolume * masterVolume;
|
|
}
|
|
SaveVolumeSettings();
|
|
}
|
|
|
|
private void UpdateAllVolumes()
|
|
{
|
|
if (musicSource != null)
|
|
musicSource.volume = musicVolume * masterVolume;
|
|
if (voiceSource != null)
|
|
voiceSource.volume = voiceVolume * masterVolume;
|
|
}
|
|
|
|
private void SaveVolumeSettings()
|
|
{
|
|
PlayerPrefs.SetFloat("MasterVolume", masterVolume);
|
|
PlayerPrefs.SetFloat("MusicVolume", musicVolume);
|
|
PlayerPrefs.SetFloat("SFXVolume", sfxVolume);
|
|
PlayerPrefs.SetFloat("VoiceVolume", voiceVolume);
|
|
PlayerPrefs.Save();
|
|
}
|
|
|
|
private void LoadVolumeSettings()
|
|
{
|
|
masterVolume = PlayerPrefs.GetFloat("MasterVolume", 1f);
|
|
musicVolume = PlayerPrefs.GetFloat("MusicVolume", 0.5f);
|
|
sfxVolume = PlayerPrefs.GetFloat("SFXVolume", 1f);
|
|
voiceVolume = PlayerPrefs.GetFloat("VoiceVolume", 1f);
|
|
UpdateAllVolumes();
|
|
}
|
|
|
|
// Properties fuer UI-Sliders
|
|
public float MasterVolume => masterVolume;
|
|
public float MusicVolume => musicVolume;
|
|
public float SFXVolume => sfxVolume;
|
|
public float VoiceVolume => voiceVolume;
|
|
}
|
|
}
|