当前位置: 首页 > news >正文

Unity学习笔记--如何优雅简便地利用对象池生成游戏对象(进阶版)LRU + 对象池

前言

之前写过一篇关于对象池的文章,现在来看写的并不是很好,所以来考虑优化下。

现在来看一年前写的代码,越看越不能入目hhh

Unity学习笔记–如何优雅简便地利用对象池生成游戏对象

前置知识

Unity学习笔记–使用 C# 开发一个 LRU

代码实现

PoolManager.cs

using System;
using System.Collections.Generic;
using Factory;namespace ToolManager
{public class PoolManager{private Dictionary<string, LinkedListNode<Tuple<string, Pool>>> lru_dict;   // Key : pool_name == obj_nameprivate LinkedList<Tuple<string, Pool>> cache;private int capacity;public PoolManager(int capacity_in = 64){capacity = capacity_in;cache = new LinkedList<Tuple<string, Pool>>();lru_dict = new Dictionary<string, LinkedListNode<Tuple<string, Pool>>>();}public bool HasPool(string path){return lru_dict.ContainsKey(path);}public bool AddPool(BaseFactory factory, int init_count = 0){string pool_name = factory.GetObjPath();if (HasPool(pool_name)){return false;}Pool pool = new Pool(this, pool_name, factory, init_count);LinkedListNode<Tuple<string, Pool>> node = new LinkedListNode<Tuple<string, Pool>>(Tuple.Create(pool_name, pool));LRUAdd(node);return true;}public bool DelPool(string name){if (!HasPool(name)){return false;}var node = lru_dict[name];GetPool(node).ReleasePool();LRURemove(node);return true;}public object PopOne(string name){object res = null;if (HasPool(name)){var node = lru_dict[name];LRUChange(node);Pool pool = GetPool(node);res = pool.PopOne();LRURemove(node);}return res;}public object[] Pop(string name, int count){object[] res = null;if (HasPool(name)){var node = lru_dict[name];LRUChange(node);Pool pool = GetPool(node);res = pool.Pop(count);LRURemove(node);}return res;}public void PushOne(string name, object obj){if (HasPool(name)){var node = lru_dict[name];LRUChange(node);GetPool(node).PushOne(obj);RefreshLRU();}}public void Push(string name, object[] objs){if (HasPool(name)){var node = lru_dict[name];LRUChange(node);GetPool(node).Push(objs);RefreshLRU();}}private Pool GetPool(LinkedListNode<Tuple<string, Pool>> node){return node.Value.Item2;}// ------------------------- LRU Function -------------------------private void LRUChange(LinkedListNode<Tuple<string, Pool>> node){cache.Remove(node);cache.AddLast(node);}private void LRUAdd(LinkedListNode<Tuple<string, Pool>> node){lru_dict.Add(node.Value.Item1, node);cache.AddLast(node);}private void LRURemove(LinkedListNode<Tuple<string, Pool>> node){cache.Remove(node);lru_dict.Remove(node.Value.Item1);}private void RefreshLRU(){int lru_count = LRUCacheCount;while (lru_count > capacity){Pool pool = GetPool(cache.First);int n_objects_to_remove = Math.Min(pool.PoolCount, lru_count - capacity);for (int i = 0; i < n_objects_to_remove; i++){pool.ReleaseOne();}if(pool.PoolCount == 0){DelPool(pool.pool_name);}lru_count = LRUCacheCount;}}private int LRUCacheCount{get{int count = 0;foreach (var node in lru_dict.Values){count += node.Value.Item2.PoolCount;}return count;}}private class Pool{private PoolManager pool_mgr;private BaseFactory factory;private Queue<object> queue;public string pool_name;public Pool(PoolManager pool_mgr_in, string pool_name_in, BaseFactory factory_in, int init_count_in){pool_mgr = pool_mgr_in;pool_name = pool_name_in;factory = factory_in;queue = new Queue<object>();InitPool(init_count_in);}private void InitPool(int init_count){for (int i = 0; i < init_count; i++){queue.Enqueue(factory.CreateObject());}}public void ReleasePool(){foreach (var obj in queue){factory.DestroyObject(obj);}queue.Clear();factory.ReleaseFactory();}public object PopOne(){if (queue.Count > 0){object obj = queue.Dequeue();factory.ResetObject(obj);return obj;}return factory.CreateObject();}public object[] Pop(int count){object[] objs = new object[count];for (int i = 0; i < count; i++){objs[i] = PopOne();}return objs;}public void PushOne(object obj){factory.RecycleObject(obj);queue.Enqueue(obj);}public void Push(object[] objs){foreach (var obj in objs){PushOne(obj);}}public void ReleaseOne(){factory.DestroyObject(queue.Dequeue());}public int PoolCount { get => queue.Count; }}}
}

BaseFactory.cs

namespace Factory
{public abstract class BaseFactory{protected string obj_path;public BaseFactory(string obj_path_in){obj_path = obj_path_in;}public abstract object CreateObject();public abstract void ResetObject(object obj);public abstract void RecycleObject(object obj);public abstract void DestroyObject(object obj);public abstract void ReleaseFactory();public virtual string GetObjPath(){return obj_path;}}
}

使用

创建 Factory

using Factory;public class BulletFactory : BaseFactory
{public BulletFactory(string obj_path_in) : base(obj_path_in){}public override object CreateObject(){Bullet bullet = new Bullet(obj_path);bullet.ReuseInit();return bullet;}public override void DestroyObject(object obj){((Bullet)obj).Destroy();}public override void RecycleObject(object obj){((Bullet)obj).Recycle();}public override void ReleaseFactory(){}public override void ResetObject(object obj){((Bullet)obj).ReuseInit();}
}

创建 object

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Bullet
{private GameObject go;private string path;private static GameObject prefab;public Bullet(string path_in){path = path_in;if (prefab == null){prefab = (GameObject) Resources.Load(path);}}public void ReuseInit(){if (go){go.SetActive(true);}else{go = GameObject.Instantiate(prefab);}go.GetComponent<RecycleMe>().DelayCall(1, Func);}public void Destroy(){GameObject.Destroy(go);}public void Recycle(){go.SetActive(false);}private void Func(){BulletManager.Ins.PushOne(path, this);}
}

创建 BulletManager

BulletManager.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using ToolManager;public class BulletManager : MonoBehaviour
{private PoolManager pool_mgr;private static BulletManager _ins;public static BulletManager Ins{get => _ins;}private void Awake(){Init();}private void Init(){_ins = this;pool_mgr = new PoolManager();}public void PushOne(string path, Bullet obj){if (!pool_mgr.HasPool(path)){pool_mgr.AddPool(new BulletFactory(path));}pool_mgr.PushOne(path, obj);}public Bullet PopOne(string path){if (!pool_mgr.HasPool(path)){pool_mgr.AddPool(new BulletFactory(path));}Bullet bullet = (Bullet)pool_mgr.PopOne(path);return bullet;}
}

验证

为了验证,我写了一个 ShootManager.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class ShootManager : MonoBehaviour
{private List<Bullet> bullet_list;private string path = "Bullet";private static ShootManager _ins;public static ShootManager Ins{get => _ins;}private void Awake(){Init();}private void Init(){_ins = this;bullet_list = new List<Bullet>();}private void Update(){if (Input.GetMouseButtonDown(0)){bullet_list.Add(BulletManager.Ins.PopOne(path));}}
}

创建之后过 1s 之后回收自己。
RecycleMe.cs

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class RecycleMe : MonoBehaviour
{public void DelayCall(float delay, Action func){StartCoroutine(DestroyAfterDelayCoroutine(delay, func));}IEnumerator DestroyAfterDelayCoroutine(float delay, Action func){yield return new WaitForSeconds(delay);func.Invoke();}}

效果

LRUPool

相关文章:

Unity学习笔记--如何优雅简便地利用对象池生成游戏对象(进阶版)LRU + 对象池

前言 之前写过一篇关于对象池的文章&#xff0c;现在来看写的并不是很好&#xff0c;所以来考虑优化下。 现在来看一年前写的代码&#xff0c;越看越不能入目hhh Unity学习笔记–如何优雅简便地利用对象池生成游戏对象 前置知识 Unity学习笔记–使用 C# 开发一个 LRU 代码实…...

【Spring专题】Bean的声明周期流程图

前言 我向来不主张【通过源码】理解业务&#xff0c;因为每个人的能力有限&#xff0c;甚至可能会因为阅读错误导致出现理解上的偏差&#xff0c;所以我决定&#xff0c;还是先帮大家【开天眼】&#xff0c;先整体看看流程图&#xff0c;好知道&#xff0c;Spring在写源码的过…...

C++实现俄罗斯方块(源码+详解)

&#x1f442; Take me Hand Acoustic - Ccile Corbel - 单曲 - 网易云音乐 源码Debug工具 &#xff08;1&#xff09;cppreference.com &#xff08;主&#xff09; &#xff08;2&#xff09;必应 (bing.com) &#xff08;3&#xff09;GPT&#xff08;主&#xff09; &#…...

01:STM32点灯大师和蜂鸣器

目录 一:点亮1个LED 1:连接图 2:函数介绍 3:点灯代码 二:LED闪烁 1:函数介绍 2:闪烁代码 三:LED流水灯 1:连接图 2:函数介绍 3:流水灯代码 四:蜂鸣器 1:连接图 2:蜂鸣器代码 一:点亮1个LED 1:连接图 因为IO口与LED负极相连所以IO口输出低电频,点亮LED (采用的是低…...

linux pwn 基础知识

环境搭建 虚拟机安装 镜像下载网站为了避免环境问题建议 22.04 &#xff0c;20.04&#xff0c;18.04&#xff0c;16.04 等常见版本 ubuntu 虚拟机环境各准备一份。注意定期更新快照以防意外。虚拟机建议硬盘 256 G 以上&#xff0c;内存也尽量大一些。硬盘大小只是上界&#…...

Unity Poisson分布 【由ChatGPT生成】

Unity Poisson分布 【由ChatGPT生成】 前言项目Unity场景布置代码编写添加并设置脚本运行效果总结 前言 在Unity游戏开发中&#xff0c;数学和统计学的概念常常用于解决各种问题&#xff0c;从资源分配到游戏机制的设计。本文将探讨Poisson分布在Unity游戏开发中的实际应用和作…...

permission denied while trying to connect to the Docker daemon socket 错误

安装 docker 执行错误如下&#xff1a; $ docker pspermission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get “http://%2Fvar%2Frun%2Fdocker.sock/v1.24/containers/json”: dial unix /var/run/docker.sock: connect:…...

pytorch nn.ModuleList和nn.Sequential的用法笔记

有部分内容转自: pytorch小记:nn.ModuleList和nn.Sequential的用法以及区别_慕思侣的博客-CSDN博客 但是有部分内容做了修改调整, 在构建网络的时候,pytorch有一些基础概念很重要,比如nn.Module,nn.ModuleList,nn.Sequential,这些类我们称为为容器(containers),可…...

SQL | 高级数据过滤

5-高级数据过滤 通过组合WHERE子句&#xff0c;建立功能更强的检索语句。 5.1-组合WHERE子句 前面写的都是单一条件下的WHERE子句&#xff0c;SQL语句允许给出多个WHERE子句来组合检索&#xff0c;这些WHERE子句通过AND子句或者OR子句进行连接。 操作符&#xff08;operato…...

ARM架构银河麒麟docker,源码编译安装GDAL

docker中安装依赖 sudo apt-get update sudo apt-get install build-essential autoconf automake libtool sudo apt-get install libproj-dev libgeos-dev libjson-c-dev libpng-dev libjpeg-dev sudo apt-get install python3-dev sudo apt-get install python3.11-dev去官网…...

(3)原神角色数据分析-3

绘图类 在名为“WRITEPHOT.py”的文件中&#xff0c;定义如下绘图方式&#xff0c;则在主页面(app.py)文件中&#xff0c;可通过如下方式调用&#xff1a; from WRITEPHOTO import WriteScatter,WriteFunnel,WriteBarData,WritePie,WriteLineBar 代码如下&#xff1a; "…...

skywalking日志收集

文章目录 一、介绍二、添加依赖三、修改日志配置1. 添加链路表示traceId2. 添加链路上下文3. 异步日志 四、收集链路日志 一、介绍 在上一篇文章skywalking全链路追踪中我们介绍了在微服务项目中使用skywalking进行服务调用链路的追踪。 本文在全链路追踪的基础上&#xff0c…...

ASL国产CS5212规格书 DP转VGA 替代RTD2166低成本方案 兼容IT6516设计原理图

CS5212可替代兼容瑞昱RTD2166和联阳T6516&#xff0c;ASL集睿致远这款芯片是一种高性能的DP显示端口到VGA转换器芯片。它结合了DisplayPort输入接口和模拟RGB DAC输出接口&#xff0c;嵌入式单片机基于工业标准8051核心。 CS5212适用于多个细分市场和显示器应用程序&#xff1…...

关于Jquery的Validate插件--rules添加自定义方法(强密码验证方法)

简介&#xff1a;请看菜鸟教程&#xff0c;根据给出的方法&#xff0c;自定义识别密码是否为复杂密码的方法 链接: https://www.runoob.com/jquery/jquery-plugin-validate.html Query Validate 插件为表单提供了强大的验证功能&#xff0c;让客户端表单验证变得更简单&#…...

股票自动交易接口开发原理及源码分享

股票自动交易接口的开发原理涉及多个方面&#xff0c;主要包括以下几个步骤&#xff1a; 1. 数据接口获取&#xff1a;通过连接到证券交易所或第三方数据提供商的API&#xff0c;获取实时市场数据&#xff0c;包括股票报价、交易成交量、买卖盘口等信息。 2. 策略定义&#x…...

2023/8/11题解

时间限制: 1000MS 内存限制: 65536KB 解题思路 建树 模拟 &#xff0c;复杂在于建树&#xff0c;此处从题目需求可知需要按层建树&#xff0c;所以需要队列模拟&#xff0c;查找比较容易就是普通的深搜 参考代码 #include<bits/stdc.h> using namespace std; vector<…...

构造函数

一、构造函数 构造函数用来在创建对象时初始化对象&#xff0c;为对象数据成员赋初始值。 类的数据成员是不能在类定义时初始化的&#xff0c;类定义并没有产生一个实体&#xff0c;而是给出了一个数据类型&#xff0c;不占用存储空间&#xff0c;无处容纳数据。 如果一个类…...

JS 原型与继承

本文内容学习于&#xff1a;后盾人 (houdunren.com) 一、原型对象 每个对象都有一个原型prototype对象&#xff0c;通过函数创建的对象也将拥有这个原型对象。 原型是一个指向对象的指针。 1.可以将原型理解为对象的父亲&#xff0c;对象从原型对象继承来属性 2.原型就是对象…...

解决 Oracle 数据库中表被锁问题的方案和方法

我们经常会遇到表被锁的情况&#xff0c;这可能会严重影响数据库的性能和可用性。我将与大家分享如何识别、分析和解决这些问题&#xff0c;以及如何使用特定的 SQL 查询来执行解锁操作。 了解表锁的原因 首先&#xff0c;让我们来了解一下导致表被锁的常见原因。长时间运行的…...

ORACLE行转列、列转行实现方式及案例

ORACLE行转列、列转行实现方式及案例 行转列案例方式1.PIVOT方式2.MAX和DECODE方式3.CASE WHEN和GROUP BY 列转行案例方式1.UNPIVOT方式2.UNION ALL 行转列 案例 假设我们有一个名为sales的表&#xff0c;其中包含了产品销售数据。表中有三列&#xff1a;product&#xff08;…...

Cursor实现用excel数据填充word模版的方法

cursor主页&#xff1a;https://www.cursor.com/ 任务目标&#xff1a;把excel格式的数据里的单元格&#xff0c;按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例&#xff0c;…...

python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

数据链路层的主要功能是什么

数据链路层&#xff08;OSI模型第2层&#xff09;的核心功能是在相邻网络节点&#xff08;如交换机、主机&#xff09;间提供可靠的数据帧传输服务&#xff0c;主要职责包括&#xff1a; &#x1f511; 核心功能详解&#xff1a; 帧封装与解封装 封装&#xff1a; 将网络层下发…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案

随着新能源汽车的快速普及&#xff0c;充电桩作为核心配套设施&#xff0c;其安全性与可靠性备受关注。然而&#xff0c;在高温、高负荷运行环境下&#xff0c;充电桩的散热问题与消防安全隐患日益凸显&#xff0c;成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案

一、TRS收益互换的本质与业务逻辑 &#xff08;一&#xff09;概念解析 TRS&#xff08;Total Return Swap&#xff09;收益互换是一种金融衍生工具&#xff0c;指交易双方约定在未来一定期限内&#xff0c;基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题

在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件&#xff0c;这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下&#xff0c;实现高效测试与快速迭代&#xff1f;这一命题正考验着…...

USB Over IP专用硬件的5个特点

USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中&#xff0c;从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备&#xff08;如专用硬件设备&#xff09;&#xff0c;从而消除了直接物理连接的需要。USB over IP的…...

JVM 内存结构 详解

内存结构 运行时数据区&#xff1a; Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器&#xff1a; ​ 线程私有&#xff0c;程序控制流的指示器&#xff0c;分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 ​ 每个线程都有一个程序计数…...

搭建DNS域名解析服务器(正向解析资源文件)

正向解析资源文件 1&#xff09;准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2&#xff09;服务端安装软件&#xff1a;bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...

解读《网络安全法》最新修订,把握网络安全新趋势

《网络安全法》自2017年施行以来&#xff0c;在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂&#xff0c;网络攻击、数据泄露等事件频发&#xff0c;现行法律已难以完全适应新的风险挑战。 2025年3月28日&#xff0c;国家网信办会同相关部门起草了《网络安全…...