first commit
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Audio;
|
||||
|
||||
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;
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// 公开接口
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
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: 按文件名播放
|
||||
}
|
||||
|
||||
/// <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 ──
|
||||
// 在此处注册所有游戏状态 enum,TypeId 需与策划表中填写的数字一致
|
||||
// 示例(请根据实际 enum 修改):
|
||||
// StateGroupRegistry.Register<GameState>(1);
|
||||
// StateGroupRegistry.Register<Theme>(2);
|
||||
StateGroupRegistry.Register<GameState>(1);
|
||||
|
||||
// ── 启动默认音乐与环境音 ──
|
||||
// 触发一次初始状态,让音乐系统从默认状态开始匹配
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user