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); } /// /// Звук сочного плевка слюны изо рта (хлёсткий выброс сжатого воздуха и мокрый хлопок) /// public static void PlayAcidSpitSound() { int sampleRate = 44100; int samples = (int)(sampleRate * 0.28f); float[] data = new float[samples]; System.Random rnd = new System.Random(); for (int i = 0; i < samples; i++) { float t = (float)i / samples; // Фаза 1: резкий хлёсткий хлопок воздуха пасти float popEnv = Mathf.Exp(-t * 22f); float popTone = Mathf.Sin(2f * Mathf.PI * Mathf.Lerp(650f, 160f, t) * ((float)i / sampleRate)); // Фаза 2: свист и шипение вылетающей тягучей жидкости float hissEnv = Mathf.Sin(t * Mathf.PI) * Mathf.Exp(-t * 4.5f); float hissNoise = ((float)rnd.NextDouble() * 2f - 1f) * 0.42f; // Фаза 3: влажное клокотание float wetBubble = Mathf.Sin(2f * Mathf.PI * 90f * ((float)i / sampleRate)) * 0.25f; data[i] = (popTone * 0.7f * popEnv) + (hissNoise + wetBubble) * hissEnv; } AudioClip clip = AudioClip.Create("SalivaSpit", samples, 1, sampleRate, false); clip.SetData(data, 0); PlayOneShot(clip, 0.75f); } /// /// Звук смачного шлепка слюны о поверхность и шипения токсина /// public static void PlayAcidImpact() { int sampleRate = 44100; int samples = (int)(sampleRate * 0.32f); float[] data = new float[samples]; System.Random rnd = new System.Random(); for (int i = 0; i < samples; i++) { float t = (float)i / samples; // Резкий мокрый шлепок в начале float slapEnv = Mathf.Exp(-t * 18f); float slap = ((float)rnd.NextDouble() * 2f - 1f) * 0.65f + Mathf.Sin(2f * Mathf.PI * 130f * ((float)i / sampleRate)) * 0.35f; // Последующее шипение кислоты float sizzleEnv = (1f - t) * Mathf.Exp(-t * 3.5f); float sizzle = ((float)rnd.NextDouble() * 2f - 1f) * 0.35f; data[i] = (slap * slapEnv) + (sizzle * sizzleEnv * 0.6f); } AudioClip clip = AudioClip.Create("SalivaImpact", samples, 1, sampleRate, false); clip.SetData(data, 0); PlayOneShot(clip, 0.8f); } /// /// Звук шипения кислоты на полу /// public static void PlayAcidHiss() { int sampleRate = 44100; int samples = (int)(sampleRate * 0.5f); float[] data = new float[samples]; System.Random rnd = new System.Random(); for (int i = 0; i < samples; i++) { float t = (float)i / samples; float env = 1f - t; float hiss = ((float)rnd.NextDouble() * 2f - 1f) * 0.22f; data[i] = hiss * env; } AudioClip clip = AudioClip.Create("AcidHiss", samples, 1, sampleRate, false); clip.SetData(data, 0); PlayOneShot(clip, 0.35f); } /// /// Оглушительный ультразвуковой крик босса, вызывающий акустическое ослепление /// public static void PlayAcousticScreech() { int sampleRate = 44100; int samples = (int)(sampleRate * 1.5f); float[] data = new float[samples]; System.Random rnd = new System.Random(); for (int i = 0; i < samples; i++) { float t = (float)i / sampleRate; float env = Mathf.Clamp01(t / 0.15f) * Mathf.Pow(1f - (float)i / samples, 0.8f); float lowRumble = Mathf.Sin(2f * Mathf.PI * 65f * t) * 0.5f; float screechFreq = Mathf.Lerp(1600f, 2800f, Mathf.PingPong(t * 3f, 1f)); float screech = Mathf.Sin(2f * Mathf.PI * screechFreq * t) * 0.35f; float distortion = ((float)rnd.NextDouble() * 2f - 1f) * 0.2f; data[i] = Mathf.Clamp((lowRumble + screech + distortion) * env, -1f, 1f) * 0.8f; } AudioClip clip = AudioClip.Create("AcousticScreech", samples, 1, sampleRate, false); clip.SetData(data, 0); PlayOneShot(clip, 0.85f); } /// /// Глухой рев босса при пробуждении /// public static void PlayBossRoar() { int sampleRate = 44100; int samples = (int)(sampleRate * 1.8f); float[] data = new float[samples]; System.Random rnd = new System.Random(); for (int i = 0; i < samples; i++) { float t = (float)i / sampleRate; float env = Mathf.Clamp01(t / 0.2f) * Mathf.Pow(1f - (float)i / samples, 0.7f); float pitch = Mathf.Lerp(120f, 50f, (float)i / samples); float roar = Mathf.Sin(2f * Mathf.PI * pitch * t) * 0.6f; float growl = Mathf.Sin(2f * Mathf.PI * (pitch * 0.5f) * t) * 0.4f; float noise = ((float)rnd.NextDouble() * 2f - 1f) * 0.25f; data[i] = Mathf.Clamp((roar + growl + noise) * env, -1f, 1f) * 0.9f; } AudioClip clip = AudioClip.Create("BossRoar", samples, 1, sampleRate, false); clip.SetData(data, 0); PlayOneShot(clip, 0.9f); } /// /// Сигнал тревоги и карантинной блокировки /// public static void PlayLockdownAlarm() { int sampleRate = 44100; int samples = (int)(sampleRate * 0.9f); float[] data = new float[samples]; for (int i = 0; i < samples; i++) { float t = (float)i / sampleRate; float freq = Mathf.Sin(2f * Mathf.PI * 2.5f * t) > 0f ? 880f : 660f; float env = 1f - ((float)i / samples); data[i] = Mathf.Sin(2f * Mathf.PI * freq * t) * env * 0.5f; } AudioClip clip = AudioClip.Create("LockdownAlarm", samples, 1, sampleRate, false); clip.SetData(data, 0); PlayOneShot(clip, 0.6f); } /// /// Звук гидравлических ворот / разблокировки шлюза /// public static void PlayDoorHydraulic() { int sampleRate = 44100; int samples = (int)(sampleRate * 0.8f); float[] data = new float[samples]; System.Random rnd = new System.Random(); for (int i = 0; i < samples; i++) { float t = (float)i / samples; float env = Mathf.Sin(t * Mathf.PI); float steam = ((float)rnd.NextDouble() * 2f - 1f) * 0.25f; float lowClank = Mathf.Sin(2f * Mathf.PI * 90f * ((float)i / sampleRate)) * 0.35f; data[i] = (steam + lowClank) * env; } AudioClip clip = AudioClip.Create("DoorHydraulic", samples, 1, sampleRate, false); clip.SetData(data, 0); PlayOneShot(clip, 0.65f); } 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); } } }