初学者,自用笔记

单例父类模板

by B站 超级依薇尔

//让单例早于awake被创建并调用
//并且不用手动创建一个空对象来装载Manager
    public class TheManager<T> : MonoBehaviour where T : MonoBehaviour
    {
        // 单例实例
        public static T Instance
        {
            get
            {
                // 若实例为空,在场景中查找
                if (_instance == null)
                {
                    _instance = Object.FindAnyObjectByType<T>();
                  //_instance = Object.FindAnyObjectByType<T>();
                  
                    // 若场景中未找到实例
                    if (_instance == null)
                    {
                        Debug.Log($"单例模式 {typeof(T).Name} 场景中不存在,尝试创建...");

                        // 查找带有 "Manager" 标签的游戏对象
                        GameObject target = GameObject.FindGameObjectWithTag("Manager");
                        if (target == null)
                        {
                            // 若未找到,创建一个新的游戏对象
                            target = new GameObject();
                            target.name = $"单例模式管理器 {typeof(T).Name}";
                        }
                        try
                        {
                            // 尝试在目标游戏对象上添加组件
                            _instance = target.AddComponent<T>();
                        }
                        catch (System.Exception e)
                        {
                            // 处理添加组件失败的情况
                            Debug.LogError($"创建单例 {typeof(T).Name} 失败: {e.Message}");
                        }
                    }
                }
                return _instance;
            }
            set
            {
                // 若实例为空,将传入的值赋给实例
                if (_instance == null)
                {
                    _instance = value;
                }
            }
        }
        // 静态实例变量
        private static T _instance;
        private void Awake()
        {
            // 若实例为空,将当前对象赋值给实例
            if (_instance == null)
            {
                _instance = this as T;
                // 标记该对象在场景切换时不被销毁
                DontDestroyOnLoad(_instance.gameObject);
            }
            else
            {
                // 若实例已存在,销毁当前对象
                if (this != _instance)
                {
                    Destroy(gameObject);
                }
            }
        }
    }
  //用例public class GameManager : Singleton<GameManager>
  //调用为Manager.Instance,子类无需再次初始化

MusicManager模板

将音频文件存储在Resources文件夹中
using UnityEngine;
using Manager;
using System.Collections.Generic;
public class MusicManager : _Manager<MusicManager>
{
    [SerializeField] private AudioSource musicSource;  // 背景音乐源
    private Dictionary<string, AudioSource> audioSources = new Dictionary<string, AudioSource>();
    private Dictionary<string, AudioClip> audioClips = new Dictionary<string, AudioClip>();

    //音频路径文件为 public const string BGM_MAIN = "Audio/Music/bgm_main";格式

    private void Awake()
    {
        if (musicSource == null)
        {
            musicSource = gameObject.AddComponent<AudioSource>();
            musicSource.loop = true;
        }

    }

    void Start()
    {

    }

    void Update()
    {

    }

    public AudioClip LoadAudio(string path)//加载音频
    {
        return (AudioClip)Resources.Load(path);
    }

    public AudioClip GetAudio(string path)//获取音频资源,避免重复加载
    {
        if (!audioClips.ContainsKey(path))
        {
            audioClips[path] = LoadAudio(path);
        }
        return audioClips[path];
    }

    public void PlayMainMusic(string path, float volume = 1.0f, bool loop = true)//播放背景音乐
    {
        AudioClip clip = GetAudio(path);
        if (clip == null) return;

        musicSource.clip = clip;
        musicSource.volume = volume;
        musicSource.loop = loop;
        musicSource.Play();
    }

    public void StopMainMusic()
    {
        musicSource.Stop();
    }

    public void SetMainMusicVolume(float volume)
    {
        musicSource.volume = volume;
    }

    public void PlaySFX(string path, float volume = 1.0f)//playoneshot可以叠加播放,用于音效交叉混合效果
    {
        this.musicSource.PlayOneShot(GetAudio(path), volume);
    }


    public AudioSource GetAudioSource(string name,bool loop = true)// 动态获取指定名称的AudioSource
    {
        if (!audioSources.TryGetValue(name, out AudioSource source))
        {
            GameObject audioObject = new GameObject($"AudioSource_{name}");
            audioObject.transform.SetParent(transform);
            source = audioObject.AddComponent<AudioSource>();
            source.loop = loop;
            audioSources[name] = source;
        }
        return source;
    }

    public void PlayAudio(string name, string path, float volume = 1.0f, bool loop = true)// 播放独立命名的音频
    {
        AudioClip clip = GetAudio(path);
        if (clip == null) return;

        AudioSource source = GetAudioSource(name, loop);
        source.clip = clip;
        source.volume = volume;
        source.loop = loop;
        source.Play();
    }


    public void StopAudio(string name)// 停止指定音频
    {
        if (audioSources.TryGetValue(name, out AudioSource source))
        {
            source.Stop();
        }
    }

    public void SetAudioVolume(string name, float valume = 0f)//设置指定音频音量
    {
        if (audioSources.TryGetValue(name, out AudioSource source))
        {
            source.volume = valume;
        }
    }

    public void RemoveAudioSource(string name)//删除不再需要的音频
    {
        if (audioSources.TryGetValue(name, out AudioSource source))
        {
            source.Stop(); // 停止播放
            Destroy(source.gameObject); // 销毁游戏对象
            audioSources.Remove(name); // 从字典中移除
        }
    }

    // 清理所有非保留音频源
    public void CleanupAudioSources(List<string> exceptions = null)//exceptions为需要保留的音频
    {
        List<string> sourcesToRemove = new List<string>();

        foreach (var pair in audioSources)
        {
            // 保留例外列表中的音频
            if (exceptions != null && exceptions.Contains(pair.Key))
                continue;

            sourcesToRemove.Add(pair.Key);
        }

        foreach (string name in sourcesToRemove)
        {
            RemoveAudioSource(name);
        }
    }
}