- 前言
- 提前准备
- 实现步骤
- 步骤一:创建对象池管理者
- 步骤二:创建抽象工厂
- 步骤三:子弹工厂代码
- 步骤四:如何生成游戏对象
- 步骤五:如何回收游戏对象
- 添加新对象池
- 注意事项
- 最后
最近在复习自己所做的FPS项目,发现之前写的对象池代码简直就难以入目。。。
加上我最近学习了工厂模式。我就优化了下我的对象池代码。
更新后:代码更加简洁,更加易懂,操作方便,代码耦合性降低。
- 注意:本例子用的是子弹预制体,大家可以换其他的预制体。
1.我们需要在Assets文件夹下创建一个Resources文件夹
2. 需要在Resources文件夹下加入我们的预制体
创建一个对象池,让他继承自单例模板类。
(关于单例模板类怎么写:浅谈设计模式和其Unity中的应用:一、单例模式)
PS:代码如果没有注释的话会简短很多,但是为了大家方便看懂,我还是打了注释。
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; public class PoolManager : Singleton{ /// /// 所有的对象池,每一个元素都对应一个游戏对象的对象池 /// private Dictionary> pool = new Dictionary >() { {"AssaultRifleBullet", new Stack () }, //步枪子弹对象池 {"SniperBullet", new Stack () }, //狙击子弹对象池 {"HandgunBullet", new Stack () }, //手枪子弹对象池 {"SMGBullet", new Stack () }, //冲锋枪子弹对象池 {"ShotGunBullet", new Stack () }, //霰弹枪子弹对象池 }; /// /// 存储所有的可以用于对象池的预制体,放在Resources文件夹下 /// private DictionaryPrefabs = new Dictionary (); [SerializeField] /// /// 存储需要动态加载的预制体的名字 /// private ListprefabsName; protected override void Awake() { base.Awake(); DontDestroyOnLoad(this); InitDictionary(); } /// /// 动态加载预制体,注意Resources文件夹下的所有预制体的名字要和对象池名字相同 /// private void InitDictionary() { for (int i = 0; i < prefabsName.Count; i++) { Prefabs.Add(prefabsName[i], (GameObject)Resources.Load(prefabsName[i])); } } ////// 回收至对象池 /// /// 游戏对象在对象池中的名字 /// 被回收的游戏对象 public void CollectObject(string gameobjectName, GameObject go) { if(pool.TryGetValue(gameobjectName, out Stackstack)) { //将游戏对象压入栈(加入到对象池) go.SetActive(false); stack.Push(go); } } /// /// 创建对象调用 /// /// 游戏对象在对象池中的名字 ///从对象池取出来的游戏对象 public GameObject Creat(string gameobjectName) { GameObject res = null; //如果存在该游戏对象所对应的对象池 if (pool.TryGetValue(gameobjectName, out Stackstack) && Prefabs.ContainsKey(gameobjectName)) { //如果对象池里面有对象,就直接拿出来,不然重新生成一个预制体。 res = stack.Count >= 1 ? stack.Pop() : Instantiate(Prefabs[gameobjectName]); res?.gameObject.SetActive(true); } return res; } /// /// 创建对象调用 /// /// 游戏对象在对象池中的名字 /// 游戏对象的坐标 /// 游戏对象的旋转 ///从对象池取出来的游戏对象 public GameObject Creat(string gameobjectName, Vector3 position, Quaternion rotation) { GameObject res = Creat(gameobjectName); res.transform.position = position; res.transform.rotation = rotation; return res; } }
- 注意,处于Resources文件夹下的预制体名字要和prefabsName列表里的名字要一样。
这里限于篇幅,我就不贴代码了。
抽象工厂步骤我写过一个文章:浅谈设计模式和其Unity中的应用:三、抽象工厂模式
using System.Collections; using System.Collections.Generic; using UnityEngine; using System; public class BulletFactory : Singleton步骤四:如何生成游戏对象, IBaseFactory { protected override void Awake() { base.Awake(); DontDestroyOnLoad(this); } /// /// 创建子弹时调用 /// /// 子弹名称:AssaultRifleBullet、SniperBullet、HandgunBullet、SMGBullet、ShotGunBullet ///子弹 public GameObject Creat(string produceName) { return PoolManager.Ins.Creat(produceName); } ////// 创建子弹时调用 /// /// 子弹名称:AssaultRifleBullet、SniperBullet、HandgunBullet、SMGBullet、ShotGunBullet /// 子弹位置 /// 子弹旋转 ///子弹 public GameObject Creat(string produceName, Vector3 position, Quaternion rotation) { GameObject go = Creat(produceName); go.transform.position = position; go.transform.rotation = rotation; return go; } }
假设我的枪械脚本有一个射击函数,那我们就可以这样调用
public void HandleShot()
{
GameObject bullet = FactoryProducer.GetFactory(FactoryType.BulletFactory).Creat(firearmBulletName);
}
步骤五:如何回收游戏对象
比如我的子弹脚本有一个定时器,过5s就自动回收本游戏对象,那我们就可以这么写:
private void RecoveryToPool()
{
PoolManager.Ins.CollectObject(owner.firearmBulletName, gameObject);
}
添加新对象池
如果以后我们要增加一个对象池,只需要在PoolManager的对象池中添加一行代码
然后在下方两张图添加对应预制体和预制体名字(和上方的对象池名字要一样)
这三张图片箭头指向的名字要一样,不然无法正确加载。
如果大家有什么更好的方式,希望大佬可以指点下,一起学习,共同进步!



