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);
}
}
}