using UnityEngine; /// /// Процедурный синтезатор звуковых эффектов для интерфейса терминала, кнопок и записок. /// Создает чистые звуки прямо в оперативной памяти (AudioClip.Create) без внешних файлов. /// public static class GameAudioSynthesizer { private static AudioSource globalAudioSource; private static AudioSource GetAudioSource() { if (globalAudioSource == null) { var go = new GameObject("ProceduralUIAudioSource"); if (Application.isPlaying) { Object.DontDestroyOnLoad(go); } globalAudioSource = go.AddComponent(); globalAudioSource.playOnAwake = false; globalAudioSource.spatialBlend = 0f; // 2D звук интерфейса globalAudioSource.ignoreListenerPause = true; // Работает даже при Time.timeScale = 0 } return globalAudioSource; } /// /// Короткий высокий бип нажатия кнопки терминала (1200 Hz) /// public static void PlayKeypadBeep() { AudioClip clip = CreateToneClip(1200f, 0.05f, 0.25f); PlayOneShot(clip, 0.35f); } /// /// Звук сброса/стирания цифры (700 Hz) /// public static void PlayKeypadClear() { AudioClip clip = CreateToneClip(700f, 0.08f, 0.3f); PlayOneShot(clip, 0.3f); } /// /// Звук ошибки ввода (низкий двойной зуммер 160Hz) /// public static void PlayErrorCode() { int sampleRate = 44100; int samples = (int)(sampleRate * 0.25f); float[] data = new float[samples]; for (int i = 0; i < samples; i++) { float t = (float)i / sampleRate; float freq = 160f; float pulse = Mathf.Sin(2f * Mathf.PI * 18f * t) > 0f ? 1f : 0f; data[i] = Mathf.Sin(2f * Mathf.PI * freq * t) * pulse * (1f - (float)i / samples) * 0.4f; } AudioClip clip = AudioClip.Create("ErrorCodeTone", samples, 1, sampleRate, false); clip.SetData(data, 0); PlayOneShot(clip, 0.45f); } /// /// Торжественный трезвук успеха (C-E-G мажорный перелив) /// public static void PlaySuccessChime() { int sampleRate = 44100; int samples = (int)(sampleRate * 0.6f); float[] data = new float[samples]; float[] freqs = new float[] { 523.25f, 659.25f, 783.99f, 1046.50f }; for (int i = 0; i < samples; i++) { float t = (float)i / sampleRate; float sampleVal = 0f; for (int f = 0; f < freqs.Length; f++) { float startTime = f * 0.1f; if (t >= startTime) { float noteTime = t - startTime; float env = Mathf.Exp(-noteTime * 4.5f); sampleVal += Mathf.Sin(2f * Mathf.PI * freqs[f] * noteTime) * env * 0.2f; } } data[i] = Mathf.Clamp(sampleVal, -0.9f, 0.9f); } AudioClip clip = AudioClip.Create("SuccessChime", samples, 1, sampleRate, false); clip.SetData(data, 0); PlayOneShot(clip, 0.5f); } /// /// Звук разворачивания/чтения секретной записки (шум бумаги + легкий звон) /// public static void PlayPaperRustle() { int sampleRate = 44100; int samples = (int)(sampleRate * 0.18f); float[] data = new float[samples]; System.Random rnd = new System.Random(42); for (int i = 0; i < samples; i++) { float env = 1f - (float)i / samples; float noise = ((float)rnd.NextDouble() * 2f - 1f) * 0.15f; float chime = Mathf.Sin(2f * Mathf.PI * 1400f * ((float)i / sampleRate)) * 0.12f; data[i] = (noise + chime) * env; } AudioClip clip = AudioClip.Create("PaperRustle", samples, 1, sampleRate, false); clip.SetData(data, 0); PlayOneShot(clip, 0.4f); } private static AudioClip CreateToneClip(float frequency, float duration, float volume) { int sampleRate = 44100; int samples = (int)(sampleRate * duration); float[] data = new float[samples]; for (int i = 0; i < samples; i++) { float t = (float)i / sampleRate; float env = 1f - ((float)i / samples); data[i] = Mathf.Sin(2f * Mathf.PI * frequency * t) * env * volume; } AudioClip clip = AudioClip.Create($"Tone_{frequency}", samples, 1, sampleRate, false); clip.SetData(data, 0); return clip; } private static void PlayOneShot(AudioClip clip, float volumeScale) { if (clip == null) return; var src = GetAudioSource(); if (src != null) { src.PlayOneShot(clip, volumeScale); } } }