BLOG

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 pools ;//声明一个列表的对象池

private Dictionary> _poolDictionary;//_前缀表示私有

private GameObject _poolParent;//父对象

Pool类是单个对象池,包含tag等属性

pools是一个列表,包含多种对象池

_poolDictionary是一个字典,key是对象类型,value是对象队列(List也可),用以区分不同对象池。

初始化

初始化的前提是配置好Pools

代码如下

private void Start()

{

_poolDictionary = new Dictionary>();

_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());

//给对应键加值

_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

{

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 pools ;//声明一个列表的对象池

private Dictionary> _poolDictionary;//_前缀表示私有

private GameObject _poolParent;//父对象

private void Start()

{

//pools = new List();

_poolDictionary = new Dictionary>();

_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());

//给对应键加值

_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中

未完待续