首页/新闻资讯/正文详情

自定义对象池实战:用 Dictionary + List 在 Unity 中实现高效的 GameObject 复用(Unity3DTraining Object Pool2 源码解析)

发布时间:2026/9/25 5:12:52 来源:云帆数科 栏目:资讯中心
自定义对象池实战:用 Dictionary + List 在 Unity 中实现高效的 GameObject 复用(Unity3DTraining Object Pool2 源码解析)
示例工程【免费下载链接】Unity3DTraining【Unity杂货铺】unity大杂烩~项目地址https://gitcode.com/gh_mirrors/un/Unity3DTraining点击查看免费下载本篇文章聚焦 Unity3DTraining 仓库中MemoryPool_ObjectPool/Object Pool2示例的完整实现它不依赖任何第三方插件仅用Dictionarystring, ListGameObject两个基础容器就写出了一套可复用的 GameObject 对象池。读完本文你将掌握对象池的借取Get/归还Store核心模型、预制体命名与 Key 匹配的底层细节并能在自己的射击、刷怪、弹幕等高频实例化场景中直接落地这套方案。文章配套源码与场景位于 MemoryPool_ObjectPool/Object Pool2同仓库还收录了一篇泛型对象池理论译文MemoryPool_ObjectPool/Object Pool两篇可对照阅读。图 1Game 运行视图白色小方块即是从对象池中重复借出/归还的子弹实例图 2Hierarchy 面板中Gun节点下挂载的多个bullet(Clone)实例正是对象池缓存未激活子弹的直接证据一、为什么需要对象池频繁实例化的性能账本在 Unity 中大量重复出现的物体——例如发射的子弹、爆炸特效、飘字——如果每次都走Instantiate/Destroy会造成两笔开销实例化/销毁的引擎开销创建与销毁 GameObject、挂载组件、分配本地内存均需时间成本托管堆压力每次实例化都会产生托管堆分配频繁分配会推高 GC 频率引发帧率抖动。对象池的基本思路在源码注释中写得很直白GameObjectPool.cs将用过的对象保存起来等下一次需要这种对象的时候再拿出来重复使用。恰当地使用对象池可以在一定程度上减少频繁创建对象所造成的开销。同时源码注释也给出了重要警示并非所有对象都适合池化。维护对象池本身也有开销如果对象生成成本很低池化反而可能使维护池的开销大于新建对象的开销导致性能不升反降。这一点是使用对象池前必须做的取舍判断。二、核心设计借Get与还Store两个静态方法整套池的对外接口只有两个静态方法职责单一方法职责对应时机GameObjectPool.Get(prefabName, position, rotation)从池中借出一个实例池空则新建发射子弹、生成敌人时GameObjectPool.Store(go)把用完的实例归还进池隐藏而非销毁子弹飞完、敌人死亡时内部存储结构GameObjectPool.csprivate static Dictionarystring, ListGameObject pool new Dictionarystring, ListGameObject();Key预制体的名字实际是实例化后的(Clone)名见下文ValueListGameObject存放该类型下所有已回收、未激活的实例。2.1 借取Get 的三条分支逻辑Get 方法 的完整实现public static GameObject Get(string prefabName, Vector3 position, Quaternion rotation) { // 拼接制作dic的key名因为instantiate出的gameobject都会自动命名为gameobject(Clone) // 这里是为了通下面store方法里给key的命名匹配 string key prefabName (Clone); GameObject go; // 如果字典里有这个key 并且key对应的数组不为空有该种类子弹且该种类子弹中有已经创建过的未激活的子弹 if (pool.ContainsKey(key) pool[key].Count 0) { // 从gameobjectname这个key位置取出数组 ListGameObject list pool[key]; // 取出一号位的子弹 go list[0]; // 从列表中去除这个子弹拿出来用 list.RemoveAt(0); // 初始化状态 go.SetActive(true); go.transform.position position; go.transform.rotation rotation; } // 如果对象池中没有对象 else { go Instantiate(Resources.Load(prefabName), position, rotation) as GameObject; } return go; }三个关键细节值得注意Key 拼接规则key prefabName (Clone)。因为 UnityInstantiate出来的对象会被自动命名为xxx(Clone)Store时直接取go.name作为 Key二者天然对齐。这是整套池能正确分类的前提复用时的状态重置从List[0]取出后要RemoveAt(0)随后重新设置SetActive(true)、position、rotation保证复用对象状态与新建对象一致池空的兜底路径直接Instantiate(Resources.Load(prefabName), position, rotation)。注意这里用Resources.Load所以预制体必须放在Resources目录下本示例位于 Assets/Resources/bullet.prefab。2.2 归还Store 的隐藏而非销毁策略Store 方法public static GameObject Store(GameObject go) { // 获取gameobject的名字会是一个在上面get方法里创建的预设体的gameobject名字会是gameobject(Clone) string key go.name; if (pool.ContainsKey(key)) { // 就在这个key所对应的数组中加入这个g // 这个g就是已经用完的子弹放到这个数组里的gameobject都是不销毁只是取消激活等待再次利用的gameobject pool[key].Add(go); } else { // 建立一个这个key的arraylist 并把g加进去 pool[key] new ListGameObject() { go }; } go.SetActive(false); return go; }归还的核心动作是SetActive(false)而非Destroy。隐藏后对象仍驻留内存等待下一次Get时被重新激活。Dictionary的ContainsKey分支保证了每种类型首次归还时自动创建对应的List无需预初始化。三、演示场景PoolTest 发射 AutoDestroy 自动回收3.1 触发端按空格借出子弹并施加冲量PoolTest.csvoid Update () { if (Input.GetKeyDown(KeyCode.Space)) { GameObject bullet GameObjectPool.Get(bullet, trans.position, Quaternion.identity); bullet.GetComponentRigidbody().AddForce(trans.forward*30, ForceMode.Impulse); } }trans是一个Transform字段指向枪口/发射点在 Inspector 中拖拽赋值Get(bullet, ...)传入的bullet正是Resources下预制体的名字不含扩展名借出的子弹立即被AddForce(trans.forward * 30, ForceMode.Impulse)以冲量方式赋予前向速度——这也解释了为什么子弹预制体需要挂Rigidbody。3.2 回收端3 秒后自动归还AutoDestroy.cs 挂在子弹预制体上实现用完自动回池void Update () { StartCoroutine(DestroySelf()); } private IEnumerator DestroySelf() { yield return new WaitForSeconds(3f); GameObjectPool.Store(this.gameObject); }逻辑一目了然子弹飞出 3 秒后调用GameObjectPool.Store(this.gameObject)将自己归还。配合图 2 的 Hierarchy 截图可以看到Gun节点下累积了 8 个bullet(Clone)正是这些归还后处于未激活缓存状态的实例。小提醒StartCoroutine放在Update里会每帧重复启动协程生产代码通常应改为在实例激活时启动一次本示例旨在演示回收思路读者接入自己项目时按需调整即可。四、对象池设计要点与进阶方向4.1 本方案的设计取舍可据此扩展从 GameObjectPool.cs 源码结构可以归纳出该方案的特征按类型分类缓存不同预制体通过 Key 自然隔离互不干扰静态访问pool与Get/Store均为static全局任意脚本可直接调用Awake中的_instance仅为演示预留的单例赋值实际逻辑并未依赖它无容量上限、无预热首次借取时新建后续回收复用实现极简线程模型依赖 Unity 主线程的Update/协程调用未做线程安全处理——这也是 Unity 对象池的常见形态与 MemoryPool_ObjectPool/Object Pool 中Unity 只有主线程可调用 API池无需支持多线程的结论一致。4.2 与泛型对象池的对照仓库的 Object Pool/README.md 提供了一份 C# 泛型对象池译文展示了面向普通 C# 类的池化思路StackTNew()/Store()、支持重置闭包ResetAction、按需初始化OnetimeInitAction、IResetable自重置、帧末集体重置ResetAll等进阶设计。两者互补泛型池管类实例本示例管场景 GameObject。前者适合ListVector3、自定义数据结构等内存敏感场景后者直接接管 GameObject 的生命周期与激活状态更适合射击、刷怪这类场景级高频复用。4.3 落地到真实项目时建议补充的能力预热与上限场景加载时为高频类型预先GetStore一批实例并设定单类型池容量上限避免峰值时大量新建重置钩子复用对象若有复杂状态血条、Buff、动画建议在Get时统一重置保证借出即干净场景切换清理Store进池的对象若在切换场景时被自动销毁需同步清空对应 Key 的缓存避免残留引用。五、总结Unity3DTraining 的Object Pool2示例用约 70 行代码完成了对象池的完整闭环Get借出池空则Resources.LoadInstantiate兜底→Store归还SetActive(false)隐藏缓存→ 以预制体名(Clone)作为字典 Key 分类管理。它证明了对象池的核心并不复杂关键在于复用的对象必须重置到可用状态以及缓存要按类型隔离。结合图 1、图 2 的运行效果你可以快速验证反复按空格发射子弹Hierarchy 中的bullet(Clone)数量不会无限增长因为飞出的子弹 3 秒后都会回到池中等待再次借出。若需要更理论化的对象池设计讨论重置策略、集体重置、容量收缩等可继续阅读 MemoryPool_ObjectPool/Object Pool/README.md并把它与本篇的 GameObject 版实现互为参照搭建出适合自己项目的完整内存优化方案。赞分享示例工程【免费下载链接】Unity3DTraining【Unity杂货铺】unity大杂烩~项目地址https://gitcode.com/gh_mirrors/un/Unity3DTraining点击查看免费下载相关推荐BMAD-METHOD 官方模块体系解析bmb / cis / gds / tea 四大模块、安装器分发机制与版本通道BMAD METHOD 官方模块体系解析bmb / cis / gds / tea 四大模块、安装器分发机制与版本通道 BMAD METHOD 的核心功能由内AI 技能人工智能开发工具Gonum内存池实现sync.Pool在矩阵对象复用中的应用Gonum内存池实现sync.Pool在矩阵对象复用中的应用 在数值计算领域频繁创建和销毁矩阵对象会导致严重的性能损耗和内存碎片化。Gonum作为Go语言的科学计算在 Garnet 中开发自定义对象Custom Object从继承 CustomObjectBase 到注册自定义命令的完整实战在 Garnet 中开发自定义对象Custom Object从继承 CustomObjectBase 到注册自定义命令的完整实战 导读 Garnet 是缓存KV存储后端上一篇mikro-orm 虚拟实体Virtual Entities实战用动态 SQL 与 MongoDB 聚合映射只读实体下一篇探索TelegraphiOS、tvOS和macOS的安全Web服务器创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关推荐

mikro-orm 虚拟实体(Virtual Entities)实战:用动态 SQL 与 MongoDB 聚合映射只读实体
mikro-orm 虚拟实体(Virtual Entities)实战:用动态 SQL 与 MongoDB 聚合映射只读实体

后端 【免费下载链接】mikro-orm TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, MariaDB, MS SQL Server, PostgreSQL and SQLite/libSQL databases. 项目地址: https://gitcode.com/gh_mir… · 2026/9/25 5:12:52

Atlas 300V 24G推理加速卡实测:YOLOv5部署全链路与调优
Atlas 300V 24G推理加速卡实测:YOLOv5部署全链路与调优

接到手这块Atlas 300V 24G的时候,我第一反应也是去查它到底算运算加速卡还是别的什么卡。等真正把YOLOv5跑起来,又折腾了一阵驱动和模型转换之后,才发现网上一堆帖子说得云里雾里。这篇文章就围绕两个高频问题来写:Atlas 300V 24G… · 2026/9/25 5:12:52

短信轰炸防御实战:从业务逻辑到运营商协同的三层加固
短信轰炸防御实战:从业务逻辑到运营商协同的三层加固

1. 这不是“技术揭秘”,而是安全防线的实战拆解“短信轰炸”这个词最近在社交平台和社区讨论里频繁出现,但很多人一听到“揭秘”,下意识就往“黑产技术教程”或“黑客工具教学”方向联想——这恰恰是最大的认知误区。我做网络安全一线支撑工作… · 2026/9/25 5:12:46

OpenShift 基础设施组件监控插桩最佳实践:healthz / metrics / pprof 端点规范与实现验证
OpenShift 基础设施组件监控插桩最佳实践:healthz / metrics / pprof 端点规范与实现验证

测试云原生质量保障 【免费下载链接】origin Conformance test suite for OpenShift 项目地址: https://gitcode.com/gh_mirrors/or/origin 点击查看 免费下载 导读 本文基于 OpenShift Origin(openshift/origin)仓库中的官方提案文档 inst… · 2026/9/25 5:39:43

Apache Iceberg 安全威胁模型(Security Threat Model)权威解读:信任边界、漏洞分类与安全扫描校准指南
Apache Iceberg 安全威胁模型(Security Threat Model)权威解读:信任边界、漏洞分类与安全扫描校准指南

数据湖大数据数据存储 【免费下载链接】iceberg Apache Iceberg 项目地址: https://gitcode.com/gh_mirrors/icebe/iceberg 点击查看 免费下载 Apache Iceberg 常以库和集成层的形式嵌入在具备独立认证、授权与凭据管理能力的更大系统(Catalog、查询引擎… · 2026/9/25 5:39:43

指纹芯片选型:模组公差、环境鲁棒性与TrustZone协同深度
指纹芯片选型:模组公差、环境鲁棒性与TrustZone协同深度

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 5:39:43

昇腾Atlas 300V 24G是AI推理加速卡吗?YOLOv5部署全流程解析
昇腾Atlas 300V 24G是AI推理加速卡吗?YOLOv5部署全流程解析

先回答那个热搜问题:Atlas 300V 24G到底算不算运算加速卡前阵子在一个技术交流群里看到有人问:“Atlas 300V 24G是运算加速卡吗?”底下回答五花八门,有说能当显卡用的,有说只能跑华为自家框架的。刚好我上个月用这块卡… · 2026/9/25 5:39:43

Amazon Polly 语音合成实战指南:aws-doc-sdk-examples 仓库中 AWS SDK for Java 2.x 示例详解
Amazon Polly 语音合成实战指南:aws-doc-sdk-examples 仓库中 AWS SDK for Java 2.x 示例详解

示例工程教程后端 【免费下载链接】aws-doc-sdk-examples Welcome to the AWS Code Examples Repository. This repo contains code examples used in the AWS documentation, AWS SDK Developer Guides, and more. For more information, see the Readme.md file below. 项目地… · 2026/9/25 5:39:43

Atlas 300V 24G推理加速卡部署YOLO全流程实战指南
Atlas 300V 24G推理加速卡部署YOLO全流程实战指南

如果你最近在搜“atlas 300v 24g 是运算加速卡吗”,那我猜你大概率是被一张PCIe卡给整迷茫了。答案很直接:是,它是昇腾平台的一款AI推理加速卡,算力密度和显存容量都相当能打,而且现在正好是拿它来部署YOLO这类目标检测… · 2026/9/25 5:39:37

数值优化(Numerical Optimization)学习系列-03-共轭梯度方法(Conjugate Gradient)
数值优化(Numerical Optimization)学习系列-03-共轭梯度方法(Conjugate Gradient)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31

创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31

MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码