Kursach_StealthAction/Assets/Scripts/GameAudioSynthesizer.cs

151 lines
5.2 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using UnityEngine;
/// <summary>
/// Процедурный синтезатор звуковых эффектов для интерфейса терминала, кнопок и записок.
/// Создает чистые звуки прямо в оперативной памяти (AudioClip.Create) без внешних файлов.
/// </summary>
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<AudioSource>();
globalAudioSource.playOnAwake = false;
globalAudioSource.spatialBlend = 0f; // 2D звук интерфейса
globalAudioSource.ignoreListenerPause = true; // Работает даже при Time.timeScale = 0
}
return globalAudioSource;
}
/// <summary>
/// Короткий высокий бип нажатия кнопки терминала (1200 Hz)
/// </summary>
public static void PlayKeypadBeep()
{
AudioClip clip = CreateToneClip(1200f, 0.05f, 0.25f);
PlayOneShot(clip, 0.35f);
}
/// <summary>
/// Звук сброса/стирания цифры (700 Hz)
/// </summary>
public static void PlayKeypadClear()
{
AudioClip clip = CreateToneClip(700f, 0.08f, 0.3f);
PlayOneShot(clip, 0.3f);
}
/// <summary>
/// Звук ошибки ввода (низкий двойной зуммер 160Hz)
/// </summary>
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);
}
/// <summary>
/// Торжественный трезвук успеха (C-E-G мажорный перелив)
/// </summary>
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);
}
/// <summary>
/// Звук разворачивания/чтения секретной записки (шум бумаги + легкий звон)
/// </summary>
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);
}
}
}