Unity对象池(应用:游戏音效管理系统)

打算为项目增加音效,但是没有头绪不知从何做起。想要做一个便于拓展的音效管理系统,通过搜集网上资料暂时得到以下两种方案。(虽然实现方式远不止两种)其中对象池技术早有耳闻,趁此机会学习并应用。

一、创建一个AudioManager

AudioManager通常是一个单例(Singleton)类,负责管理和播放游戏中的所有音频
(GPT生成代码)

using UnityEngine;

public class AudioManager : MonoBehaviour
{
    public static AudioManager Instance;

    public AudioSource bgmSource;
    public AudioSource sfxSource;

    public AudioClip playerFootstep;
    public AudioClip monsterFootstep;
    public AudioClip playerAttack;
    public AudioClip playerHurt;
    public AudioClip playerDeath;
    public AudioClip bossBgm;

    private void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
        }
        else
        {
            Destroy(gameObject);
            return;
        }
        DontDestroyOnLoad(gameObject);
    }
	
    // 播放背景音乐
    public void PlayBGM(AudioClip bgm)
    {
        bgmSource.clip = bgm;
        bgmSource.Play();
    }
    // 播放玩家走路脚步声
    public void PlayPlayerFootstep()
    {
        PlaySFX(playerFootstep);
    }
    // 播放怪物走路脚步声
    public void PlayMonsterFootstep()
    {
        PlaySFX(monsterFootstep);
    }
    // 播放玩家攻击声音
    public void PlayPlayerAttack()
    {
        PlaySFX(playerAttack);
    }
    // 播放玩家受伤声音
    public void PlayPlayerHurt()
    {
        PlaySFX(playerHurt);
    }
    // 播放玩家死亡声音
    public void PlayPlayerDeath()
    {
        PlaySFX(playerDeath);
    }
    // 播放Boss特定背景音乐
    public void PlayBossBGM()
    {
        PlayBGM(bossBgm);
    }
    // 播放音效
    public void PlaySFX(AudioClip clip)
    {
        sfxSource.PlayOneShot(clip);
    }
}

二、对象池管理音效

对象池定义

涉及对象的反复生成与销毁时频繁使用Instatiate实例化和Destory会造成较大的性能开销,于是有了对象池技术。
对象池是一个存放同种游戏对象GameObject的仓库,需要对象时将其中的对象激活,不需要时隐蔽,等待下次激活,避免了反复运算。最常用的应用是子弹的生成和销毁。
一般对象池都是一个全局性的通用脚本,可以采用单例模式来设计。

对象池功能

对象池至少包含以下两个基本功能:
1.从池中取出指定类型的对象
2.回收各式各样的对象到池中

各部分代码

定义对象池及容量

    [System.Serializable]
    public class Pool
    {
        public string tag;
        public GameObject prefab;
        public int size;
    }

    [SerializeField]
    public List<Pool> pools ;//声明一个列表的对象池
    private Dictionary<string, Queue<GameObject>> _poolDictionary;//_前缀表示私有
    private GameObject _poolParent;//父对象

Pool类是单个对象池,包含tag等属性
pools是一个列表,包含多种对象池
_poolDictionary是一个字典,key是对象类型,value是对象队列(List也可),用以区分不同对象池。

image

初始化

初始化的前提是配置好Pools
image

代码如下

private void Start()
    {
        _poolDictionary = new Dictionary<string, Queue<GameObject>>();
        _poolParent = new GameObject("poolParent");//在游戏中生成
        _poolParent.transform.SetParent(this.transform);//_poolParent的父对象是ObjectPoolManager
        InitPool();
    }

    //实例化每一个pool项目下的所有项目中的GameObject
    private void InitPool()
    {
        if (pools.Count == 0) return;
        for(int i = 0; i < pools.Count; i++)
        {
            for(int j = 0; j < pools[i].size; j++)
            {
                //实例化所有GameObject
                var item = Instantiate(pools[i].prefab);
                item.SetActive(false);
                item.transform.SetParent(_poolParent.transform);

                //判断对象池字典中是否有该类项目:创建字典键值对
                if (!_poolDictionary.ContainsKey(pools[i].tag))
                {
                    //给字典加键
                    _poolDictionary.Add(pools[i].tag, new Queue<GameObject>());
                    //给对应键加值
                    _poolDictionary[pools[i].tag].Enqueue(item);
                }
                else
                {
                    //给对应键加值
                    _poolDictionary[pools[i].tag].Enqueue(item);
                }
            }
        }
    }
    //至此把所有池子里所有项目实例化完毕

声明public方法供外界调用,生成对象

public void TryGetPoolItem(string name,Vector3 position,Quaternion rotate)
    {
        if (_poolDictionary.ContainsKey(name))
        {
            var item = _poolDictionary[name].Dequeue();//从字典中取出
            item.transform.position = position;
            item.transform.rotation = rotate;
            item.SetActive(true);
            _poolDictionary[name].Enqueue(item);//再放回去(总不能每次激活一个队列就少一个)
        }
        else
        {
            Debug.Log("当前字典名不存在");
        }
    }

完整代码ObjectPoolManager.cs

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

public class ObjectPoolManager : Singleton<ObjectPoolManager>
{
    protected override void Awake()
    {
        base.Awake();
        DontDestroyOnLoad(this);
    }

    [System.Serializable]
    public class Pool
    {
        public string tag;
        public GameObject prefab;
        public int size;
    }

    [SerializeField]
    public List<Pool> pools ;//声明一个列表的对象池
    private Dictionary<string, Queue<GameObject>> _poolDictionary;//_前缀表示私有
    private GameObject _poolParent;//父对象

    private void Start()
    {
        //pools = new List<Pool>();
        _poolDictionary = new Dictionary<string, Queue<GameObject>>();
        _poolParent = new GameObject("poolParent");//在游戏中生成
        _poolParent.transform.SetParent(this.transform);//_poolParent的父对象是ObjectPoolManager
        InitPool();
    }

    //实例化每一个pool项目下的所有项目中的GameObject
    private void InitPool()
    {
        if (pools.Count == 0) return;
        for(int i = 0; i < pools.Count; i++)
        {
            for(int j = 0; j < pools[i].size; j++)
            {
                //实例化所有GameObject
                var item = Instantiate(pools[i].prefab);
                item.SetActive(false);
                item.transform.SetParent(_poolParent.transform);

                //判断对象池字典中是否有该类项目:创建字典键值对
                if (!_poolDictionary.ContainsKey(pools[i].tag))
                {
                    //给字典加键
                    _poolDictionary.Add(pools[i].tag, new Queue<GameObject>());
                    //给对应键加值
                    _poolDictionary[pools[i].tag].Enqueue(item);
                }
                else
                {
                    //给对应键加值
                    _poolDictionary[pools[i].tag].Enqueue(item);
                }
            }
        }
    }
    //至此把所有池子里所有项目实例化完毕

    public void TryGetPoolItem(string name,Vector3 position,Quaternion rotate)
    {
        if (_poolDictionary.ContainsKey(name))
        {
            var item = _poolDictionary[name].Dequeue();//从字典中取出
            item.transform.position = position;
            item.transform.rotation = rotate;
            item.SetActive(true);
            _poolDictionary[name].Enqueue(item);//再放回去(总不能每次激活一个队列就少一个)
        }
        else
        {
            Debug.Log("当前字典名不存在");
        }
    }

}

此处缺少对象池的回收部分,该部分在SoundPoolItem.cs中

未完待续

posted @   NeroMegumi  阅读(202)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示