游戏设计模式——Unity对象池
对象池这个名字听起来好像不明觉厉,其实就是将一系列需要反复创建和销毁的对象存储在一个看不到的地方,下次用同样的东西时往这里取,类似于一个存放备用物质的仓库。
它的好处就是避免了反复实例化个体的运算,能减少大量内存碎片,当然你需要更多的空间来存这些备用对象,相信使用这些空间是非常值得的。
最常见的应用就是子弹的创建和销毁。
一般对象池都是一个全局性的通用脚本,可以采用单例模式来设计。
https://www.cnblogs.com/koshio0219/p/11203631.html
对象池至少包含以下两个基本功能:
1.从池中取出指定类型的对象
2.回收各式各样的对象到池中
先定义对象池和池子的容量:
private const int maxCount = ;
private Dictionary<string, List<GameObject>> pool = new Dictionary<string, List<GameObject>>();
容量是一个常量,最好取二的幂值,这样的话可以刚好占用所有内存位的资源,避免浪费。
这里池子用字典标识,key为对象的名字,这样比较好记,你用InstanceID也没问题。
每个同样的对象一般在池子中可以有很多,用一个List来存。
下面先定义回收对象的方法:
public void RecycleObj(GameObject obj)
{
var par = Camera.main;
obj.transform.SetParentSafe(par.transform);
obj.SetActive(false); if (pool.ContainsKey(obj.name))
{
if (pool[obj.name].Count < maxCount)
{
pool[obj.name].Add(obj);
}
}
else
{
pool.Add(obj.name, new List<GameObject>() { obj });
}
}
这里将回收的对象统一放在了场景主摄像机下,你也可以选择放在自己喜欢的位置。
回收对象就是先把对象隐藏,然后看池子中有没有这一类对象,有的话没有超过容量上限就直接扔进去。
如果没有这类对象,那就创建这一类型对象的Key值(名字:比如说螃蟹),顺便添加第一只螃蟹。
如果回收对象时改变了父物体,最好在设置父物体前后记录下对象的本地位置,旋转和缩放,可以写了一个扩展方法用于安全设置父物体:
public static void SetParentSafe(this Transform transform,Transform parent)
{
var lp = transform.localPosition;
var lr = transform.localRotation;
var ls = transform.localScale;
transform.SetParent(parent);
transform.localPosition = lp;
transform.localRotation = lr;
transform.localScale = ls;
}
经常会遇到要批量回收进池子的情况:
public void RecycleAllChildren(GameObject parent)
{
for (; parent.transform.childCount > ;)
{
var tar = parent.transform.GetChild().gameObject;
RecycleObj(tar);
}
}
对象可以回收了,那怎么取呢,自然也是能从池子里取就从池子里取,实在不行才去实例化:
public GameObject GetObj(GameObject perfab)
{
//池子中有
GameObject result = null;
if (pool.ContainsKey(perfab.name))
{
if (pool[perfab.name].Count > )
{
result = pool[perfab.name][];
result.SetActive(true);
pool[perfab.name].Remove(result);
return result;
}
}
//池子中缺少
result = Object.Instantiate(perfab);
result.name = perfab.name;
RecycleObj(result);
GetObj(result);
return result;
}
如果池子中有对象,取出来之后记得要把这个对象从该类对象的列表中移除,不然下次可能又会取到这家伙,而这家伙已经要派去做别的了。
如果池子中缺少对象,那就只能实例化了,要注意把实例化后的对应改为大家都一样的名字,这样方便下一次取能找到它。
没有对象的情况下,我这里又重新回收了一下再取一次,你也可以直接返回该对象,相当于在取的时候不存在这类对象的话我提前做了标记。
和Instantiate方法一样,加一个可以设置父对象的重载方法:
public GameObject GetObj(GameObject perfab, Transform parent)
{
var result = GetObj(perfab);
result.transform.SetParentSafe(parent);
return result;
}
下面是完整脚本:
using System.Collections.Generic;
using UnityEngine; public class ObjectPool : Singleton<ObjectPool>
{
private const int maxCount = ;
private Dictionary<string, List<GameObject>> pool = new Dictionary<string, List<GameObject>>(); public GameObject GetObj(GameObject perfab)
{
//池子中有
GameObject result = null;
if (pool.ContainsKey(perfab.name))
{
if (pool[perfab.name].Count > )
{
result = pool[perfab.name][];
if (result != null)
{
result.SetActive(true);
pool[perfab.name].Remove(result);
return result;
}
else
{
pool.Remove(perfab.name);
}
}
}
//池子中缺少
result = Object.Instantiate(perfab);
result.name = perfab.name;
RecycleObj(result);
GetObj(result);
return result;
} public GameObject GetObj(GameObject perfab, Transform parent)
{
var result = GetObj(perfab);
result.transform.SetParentSafe(parent);
return result;
} public void RecycleObj(GameObject obj)
{
var par = Camera.main;
obj.transform.SetParentSafe(par.transform);
obj.SetActive(false); if (pool.ContainsKey(obj.name))
{
if (pool[obj.name].Count < maxCount)
{
pool[obj.name].Add(obj);
}
}
else
{
pool.Add(obj.name, new List<GameObject>() { obj });
}
} public void RecycleAllChildren(GameObject parent)
{
for (; parent.transform.childCount > ;)
{
var tar = parent.transform.GetChild().gameObject;
RecycleObj(tar);
}
} public void Clear()
{
pool.Clear();
}
}
因为是用名字作为存储的Key值,所以不同类的物体命名不能相同,不然可能会取错对象。
另外由于上面的脚本有更改父物体的情况,在取出物体之后根据需要也可以对transform进行归位:
public static void ResetLocal(this Transform transform)
{
transform.localPosition = Vector3.zero;
transform.localRotation = Quaternion.identity;
transform.localScale = Vector3.one;
}
上面是对Transform类的一个扩展方法,例如:
var ins = ObjectPool.Instance.GetObj(bulletPrefab, parent.transform);
ins.transform.ResetLocal();
游戏设计模式——Unity对象池的更多相关文章
- Unity——对象池管理
Unity对象池管理 一.Demo展示 二.逻辑 在游戏中会出现大量重复的物体需要频繁的创建和销毁:比如子弹,敌人,成就列表的格子等: 频繁的创建删除物体会造成很大的开销,像这种大量创建重复且非持续性 ...
- Unity 对象池 生产 保存
Unity对象池主要是保存那些常用的物体,避免他们在不断销毁和创造中损坏性能. 主要思路为:创造物体时,判断是否存在,如果存在则调用并使其显示.如果不存在则创造一个新的. 当销毁时,调用协程延时隐藏物 ...
- 游戏开发设计模式之对象池模式(unity3d 示例实现)
前篇:游戏开发设计模式之命令模式(unity3d 示例实现) 博主才学尚浅,难免会有错误,尤其是设计模式这种极富禅意且需要大量经验的东西,如果哪里书写错误或有遗漏,还请各位前辈指正. 原理:从一个固定 ...
- unity游戏开发_对象池
现在假设游戏中我们需要实现一个这样功能,按下鼠标左键,发射一颗子弹,3秒之后消失.在这个功能中,我们发射了上百上千发子弹,就需要实例化生成上百上千次.这时候,我们就需要使用对象池这个概念,每次实例化生 ...
- Unity ----- 对象池GameObjectPool
孙广东 2014.6.28 非常早之前看到的外国文章,认为不错,分享一下. 对象池在AssetStore中也是有非常多插件的,可是有些重了.自己写一个轻量的岂不是非常好. 当你须要创建大量某种类型对象 ...
- Unity 对象池的使用
在游戏开发过程中,我们经常会遇到游戏发布后,测试时玩着玩着明显的感觉到有卡顿现象.出现这种现象的有两个原因:一是游戏优化的不够好或者游戏逻辑本身设计的就有问题,二是手机硬件不行.好吧,对于作为程序员的 ...
- Unity对象池的实现
对象池是一个单例类: using System.Collections; using System.Collections.Generic; using UnityEngine; public cla ...
- GoLang设计模式06 - 对象池模式
这次介绍最后一个创建型模式--对象池模式.顾名思义,对象池模式就是预先初始化创建好多个对象,并将之保存在一个池子里.当需要的时候,客户端就可以从池子里申请一个对象使用,使用完以后再将之放回到池子里.池 ...
- 游戏设计模式——Unity事件队列(纪念京阿尼事件)
“对消息或事件的发送与受理进行时间上的解耦.” 在游戏开发过程中,经常会出现不同板块之间的信息交流,或是存在“当...,就...”的情况,事件队列编程模式可以有效解决消息传递中产生的脚本耦合问题,让同 ...
随机推荐
- Python 获取服务器的CPU个数
在使用gunicorn时,需要设置workers, 例如: gunicorn --workers=3 app:app -b 0.0.0.0:9000 其中,worker的数量并不是越多越好,推荐值是C ...
- 【win10主机】访问virtualbox上【32位winXP系统虚拟机】上启动的项目
win10上创建虚拟网卡: 1,右键此电脑点击管理——设备管理器——网络适配器: 2,点左上角菜单栏的 操作——添加过时硬件: 3,点下一步 4,点安装我手动从列表选择的硬件(高级)M 5,点网络适配 ...
- java多线程----悲观锁与乐观锁
java多线程中悲观锁与乐观锁思想 一.悲观锁 总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线 ...
- C# - 协变、逆变 看完这篇就懂了
1. 基本概念 官方:协变和逆变都是术语,前者指能够使用比原始指定的派生类型的派生程度更大(更具体的)的类型,后者指能够使用比原始指定的派生类型的派生程度更小(不太具体的)的类型.[MSDN] 公式: ...
- 百度地图api之----根据用户ip定位城市
LocalCity 这个类是利用用户IP地址去百度数据库里查询得到IP所在的城市,用法如下: var objCity = new BMap.LocalCity(); objCity.get(funct ...
- pyinstaller打包出错numpy.core.multiarray failed to import
py原文件运行时正常,但用pyinstaller打包为exe后,在运行则报错: 这是因为cv2要求的numpy版本与你装的numpy版本不一样,导致冲突:网上很多说升级numpy,但你把numpy升的 ...
- Codeforces 814D
题意略. 思路: 由于不重合这个性质,我们可以将每一个堆叠的圆圈单独拿出来考虑,而不用去考虑其他并列在同一层的存在, 在贪心解法下,发现,被嵌套了偶数层的圆圈永远是要被减去的,而奇数层的圆圈是要加上的 ...
- 在.net core web网站中添加webSocket支持
注意:前置条件,操作系统 windows 8 以上,IIS Express 8.0 以上. 第1步:在Startup.cs文件的头部添加如下引用: using System.Net.WebSocket ...
- 你真的了解Mybatis的${}和#{}吗?是否了解应用场景?
转自:https://www.cnblogs.com/mytzq/p/9321526.html 动态sql是mybatis的主要特性之一.在mapper中定义的参数传到xml中之后,在查询之前myba ...
- HDU 5984 题解 数学推导 期望
Let’s talking about something of eating a pocky. Here is a Decorer Pocky, with colorful decorative s ...