Unity加载本地音乐/声音(异步加载)

using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
using System.Collections.Generic;

//内存占用 ≈ 时长(秒) × 采样率(Hz) × 声道数 × 2字节
//3 分钟(180 秒)、44100Hz、立体声(2 声道):
//180 × 44100 × 2 × 2 ≈ 63 MB

/// <summary>
/// 音乐管理器:负责加载、缓存和播放背景音乐。
/// 使用 StreamingAssets 文件夹中的音频文件。
/// </summary>
public class SoundManager : MonoBehaviour
{
    /// <summary>
    /// 全局单例实例
    /// </summary>
    public static SoundManager Instance { get; private set; }

    /// <summary>
    /// 播放音乐的 AudioSource 组件
    /// </summary>
    private AudioSource _audioSource;

    /// <summary>
    /// 音乐缓存字典,避免重复加载
    /// </summary>
    private readonly Dictionary<string, AudioClip> _musicCache = new();

    /// <summary>
    /// 播放顺序队列,用于控制缓存数量
    /// </summary>
    private readonly Queue<string> _playHistory = new();

    [Header("缓存设置")]
    [Tooltip("允许缓存的最大歌曲数量")]
    public int MaxCacheCount = 5;

    /// <summary>
    /// 当前正在播放的音乐文件名
    /// </summary>
    private string _currentTrack;

    private void Awake()
    {
        if (Instance != null)
        {
            Destroy(gameObject);
            return;
        }

        Instance = this;
        DontDestroyOnLoad(gameObject);
        _audioSource = gameObject.AddComponent<AudioSource>();
    }

    /// <summary>
    /// 播放指定文件名的音乐(避免重复播放同一首)
    /// </summary>
    /// <param name="fileName">音频文件名(需放置在 StreamingAssets 文件夹中)</param>
    public void Play(string fileName)
    {
        // 如果当前正在播放相同的曲目,忽略此次请求
        if (_currentTrack == fileName && _audioSource.isPlaying)
            return;

        if (_musicCache.TryGetValue(fileName, out var clip))
        {
            _audioSource.clip = clip;
            _audioSource.Play();
            _currentTrack = fileName;
        }
        else
        {
            StartCoroutine(LoadAndPlay(fileName));
        }
    }

    /// <summary>
    /// 从 StreamingAssets 异步加载音乐并播放,同时管理缓存
    /// </summary>
    private IEnumerator LoadAndPlay(string fileName)
    {
        string path = System.IO.Path.Combine(Application.streamingAssetsPath, fileName);
        using UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(path, AudioType.MPEG);
        yield return request.SendWebRequest();

        if (request.result != UnityWebRequest.Result.Success)
        {
            Debug.LogError($"音乐加载失败: {request.error}");
            yield break;
        }

        AudioClip clip = DownloadHandlerAudioClip.GetContent(request);

        // 加入缓存(仅当未缓存时)
        if (!_musicCache.ContainsKey(fileName))
        {
            _musicCache[fileName] = clip;
            _playHistory.Enqueue(fileName);

            // 缓存超过上限时,移除最早播放的音乐
            if (_playHistory.Count > MaxCacheCount)
            {
                string oldest = _playHistory.Dequeue();
                if (_musicCache.ContainsKey(oldest))
                {
                    Destroy(_musicCache[oldest]);
                    _musicCache.Remove(oldest);
                }
            }
        }

        _audioSource.clip = clip;
        _audioSource.Play();
        _currentTrack = fileName;
    }
}

 

posted on 2025-08-16 23:44  zqiang0803  阅读(82)  评论(0)    收藏  举报

导航