Files
AudioSystem/Assets/Scripts/OCES/Audio/HandWritten/AudioSystem.cs
T
Oliver e774581bfe 增加低通效果开关
- 引入DoTween
- 实现功能
2026-03-27 17:56:49 +08:00

174 lines
6.8 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Audio;
using DG.Tweening;
namespace OCES.Audio
{
public class AudioSystem : MonoBehaviour
{
public static AudioSystem Instance { get; private set; }
const string k_audioConfigPath = "AudioData";
const string k_audioResourcePath = "Audios";
AudioScheduler m_scheduler;
MusicSystem m_musicSystem;
AudioObjectConfig m_audioObjects;
AudioGroupConfig m_groups;
AudioMixer m_mixer;
Tween m_lowpassTween;
// ─────────────────────────────────────────────
// 公开接口
// ─────────────────────────────────────────────
public void Play(AudioObject audioObject)
{
this.m_scheduler.TryPlay(audioObject);
}
public void Play(int audioId)
{
AudioObject obj = this.m_audioObjects.QueryById((uint)audioId);
if (obj != null)
this.m_scheduler.TryPlay(obj);
}
public void Play(string audioName)
{
// TODO: 按文件名播放
}
public void SetLowpass(bool enable)
{
float target = enable ? 440f : 22000f;
// Kill existing tween to avoid stacking
if (this.m_lowpassTween != null && this.m_lowpassTween.IsActive())
this.m_lowpassTween.Kill();
// Get current value as start
if (!this.m_mixer.GetFloat("LowpassFreq", out float current))
current = target;
float startLog = Mathf.Log10(current);
float endLog = Mathf.Log10(target);
this.m_lowpassTween = DOTween.To(
() => startLog,
x =>
{
startLog = x;
float value = Mathf.Pow(10f, startLog);
this.m_mixer.SetFloat("LowpassFreq", value);
},
endLog,
0.2f // duration in seconds
).SetEase(Ease.OutCubic);
}
/// <summary>
/// 更新游戏状态,驱动音乐与环境音系统切换。
/// 调用示例:AudioSystem.Instance.SetState(GameState.Game);
/// </summary>
public void SetState<TEnum>(TEnum state) where TEnum : Enum
{
this.m_musicSystem.OnStateChanged(state);
}
// ─────────────────────────────────────────────
// 初始化
// ─────────────────────────────────────────────
void Awake()
{
if ((bool)Instance && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
DontDestroyOnLoad(gameObject);
this.m_mixer = Resources.Load<AudioMixer>("Audios/Master");
// ── SFX 调度器 ──
this.m_scheduler = gameObject.AddComponent<AudioScheduler>();
this.m_audioObjects = AudioConfigLoader.Load<AudioObjectConfig>($"{k_audioConfigPath}/AudioObject");
this.m_groups = AudioConfigLoader.Load<AudioGroupConfig>($"{k_audioConfigPath}/AudioGroup");
this.m_scheduler.Initialize(this.m_groups, this.m_mixer);
// ── 音乐与环境音系统 ──
var segments = AudioConfigLoader.Load<MusicSegmentConfig>($"{k_audioConfigPath}/MusicSegment");
var containers = AudioConfigLoader.Load<MusicContainerConfig>($"{k_audioConfigPath}/MusicContainer");
var musicPaths = AudioConfigLoader.Load<MusicPathConfig>($"{k_audioConfigPath}/MusicPath");
var ambiencePaths = AudioConfigLoader.Load<AmbiencePathConfig>($"{k_audioConfigPath}/AmbiencePath");
var musicTransitions = AudioConfigLoader.Load<MusicTransitionConfig>($"{k_audioConfigPath}/MusicTransition");
var ambienceTransitions = AudioConfigLoader.Load<AmbienceTransitionConfig>($"{k_audioConfigPath}/AmbienceTransition");
// MusicSystem 需要运行协程,作为 MonoBehaviour 挂载在同一 GameObject 上
this.m_musicSystem = gameObject.AddComponent<MusicSystem>();
// AudioSourcePool 由 MusicSystem 独占一个子节点,与 SFX pool 隔离
GameObject musicPoolRoot = new("MusicSourcePool");
musicPoolRoot.transform.SetParent(transform, false);
AudioMixerGroup musicGroup = this.m_mixer.FindMatchingGroups("Master/Music")[0];
AudioSourcePool musicPool = new(musicPoolRoot.transform, musicGroup);
GameObject ambiencePoolRoot = new("AmbienceSourcePool");
musicPoolRoot.transform.SetParent(transform, false);
AudioMixerGroup ambienceGroup = this.m_mixer.FindMatchingGroups("Master/SFX/Ambience")[0];
AudioSourcePool ambiencePool = new(ambiencePoolRoot.transform, ambienceGroup);
this.m_musicSystem.Initialize(
segments,
containers,
musicPaths,
ambiencePaths,
musicTransitions,
ambienceTransitions,
musicPool,
ambiencePool);
// ── 注册 StateGroup ──
EnumIds.RegisterAllGameState();
// ── 启动默认音乐与环境音 ──
// 触发一次初始状态,让音乐系统从默认状态开始匹配
SetState(GameState.Home);
}
}
static class AudioConfigLoader
{
public static Dictionary<uint, T> Load<T>(string path, Func<T, uint> keySelector)
{
string json = System.IO.File.ReadAllText(path);
var wrapper = JsonUtility.FromJson<AudioObjectArrayWrapper<T>>(json);
return wrapper.AudioObjects.ToDictionary(keySelector);
}
public static T Load<T>(string tableName) where T : IBinarySerializable, new()
{
TextAsset bytes = Resources.Load<TextAsset>(tableName);
if (!bytes)
Debug.LogError($"未找到表 {tableName}");
IBinarySerializable data = new T();
bool readOk = FileManager.ReadBinaryDataFromBytes(bytes.bytes, ref data);
if (readOk)
return (T)data;
Debug.LogError($"{tableName} 解析出错,类型 {typeof(T)}");
return default;
}
class AudioObjectArrayWrapper<T>
{
public T[] AudioObjects;
}
}
}