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

【游戏引擎架构】6.2 资源管理器

资源管理器可以分为离线部分系统和运行时系统

文章目录

  • 离线资源管理
    • 数据库
    • 资产管道
  • 运行时资源管理
    • 文件结构
    • 内存管理
    • 文件间引用

离线资源管理

数据库

UE的数据库可以直接浏览、编辑资产,看到运行时的状态;但也存在两个较大的缺点:

  1. 版本管理不友好。资产以二进制的大文件存储,无法直接使用版本管理工具比对差异。虽然引擎本身对P4做了diff蓝图的支持,但蓝图以外的内容或者不使用p4进行版本管理的团队还是无法对资产作差
  2. 移动文件有残留。移动文件时会在原来的位置留下一个重定向文件。虽然这种设计可以使得引用链上游的资产不需要更改指向,但会导致冗余数据的留存。对此UE也设计了删除这个中间文件的按钮(位于右键菜单中),不过目前UE4应该还需要手动清理

资产管道

用于将第三方工具创建出来的资产导入引擎使用
资产管道的处理通常分为导出、编译和链接:

  1. 导出
    从第三方工具导出成磁盘上存储的内容
  2. 编译
    经过编译的步骤转化为目标引擎可以识别的格式
  3. 链接
    把资产之间的引用关系链接起来

运行时资源管理

文件结构

可以已有的或自定义文件结构存储。但在进行文件结构设计时,进行打包和压缩有助于加速资源 的加载

  1. 打包
    建议打包是因为这种存储方式可以加快文件的读取
    文件读取的耗时大头可分为三个部分:
    1. 寻道时间
    2. 开启文件的时间
    3. 读取文件的时间
      将n个资源打包成1个 可以减少上述步骤2:开启文件的耗时
      可以参考UE将多个资源文件打成一个pak的方式
  2. 压缩
    压缩资源文件可以加快资源的传输,由于蓝光光盘/DVD等存储媒介数据传输极慢,压缩的加载提升尤为明显

内存管理

需要考虑如何进行内存管理可以最大化内存利用率、减少内存碎片
内存的分配策略可分为以下几种:

  1. 堆分配:最简单粗暴的方式,引擎层完全不考虑内存碎片的存在。依赖操作系统的虚拟分页对内存碎片的映射来解决。但这种方式不适用于虚拟分页做得不太好的主机平台
  2. 堆栈分配:不会产生内存碎片的分配方式。使用单个或者双栈来进行内存的分配
    单栈:内存单向增长,以关卡为单位分配、入栈和出栈。适合以关卡为单位进行的游戏
    双栈:分为上下两个栈,上面的栈内存自上而下增长,下面的栈内存自下而上增长。双栈的设计可用于不同的分工,如一个栈存放常驻内存的资源,另一个存放临时资源。或者一个用于当前正在展示的内容,另一个用于即将使用的内容,实现一个缓冲策略等等
  3. 池分配:设定一个固定的块大小,作为内存分配的基本单位。使用链表串联一个资源的各个块。优点是灵活性和适用度较高;缺点是为了填充成完整块,容易造成资源浪费
  4. 资源组分配:基于池分配器做了一点改进,解决池分配的缺点。
    使用一个链表将所有填充的部分串联起来,需要使用内存的时候从这里查找。但资源组分配的缺点是需要复杂度较高,而且需要考虑生命周期的问题:原资源释放的时候会造成使用其填充内存的资源被部分释放。书中提到了一个解决方案是保证原资源的生命周期>=填充资源的生命周期。比如,一个关卡内部的资源就可以使用这个关卡的填充内存。
  5. 资源文件分段分配:将资源文件根据生命周期或者使用场景划分为不同的段,分给不同的时机加载。
    比如UE4在游戏启动的时候只加载pak文件的头部路径信息,而不真正将所有资源加载到内存。(UE5拆得更彻底,直接将描述信息拆成一个单独的文件.utoc和真正存储资产内容的.uasset)使用到的时候才读取资源部分

文件间引用

存取得时候需要处理引用关系尤其是指针的存取。因为指针实际上只是一个内存地址,程序终止以后这个地址就没有意义了。

但我们需要把内容存储到磁盘上,因此不能直接存此时指针的内容而是需要做一个转换。

转换的方式可以是转为存储GUID或者偏移量

  1. 存储GUID
    Global Unique Identifier 全局唯一标识符,每个资源管理系统都应该设计的资源唯一标识
  2. 存储偏移量
    如果存储的是偏移量,需要另外存储一个指针转换表来进行映射

此外还需要考虑C++的对象引用。对于此书中提出两个解决方案:1. 二进制文件不使用C++对象 2. 使用一个表来映射C++对象的偏移量和其所属的类

最后由于存在文件之间的交叉引用,读取的时候,先将所有的引用内容全部反序列化回内存,再将所有的指针转回内存地址

http://www.lryc.cn/news/174490.html

相关文章:

  • spring的ThreadPoolTaskExecutor装饰器传递调用线程信息给线程池中的线程
  • 转载 - 洞察问题本质,解决工作难题
  • 关于计算机找不到d3dx9_43.dll,无法继续执行代码修复方法
  • 《从零开始的Java世界》01基本程序设计
  • 【数据开发】数据全栈知识架构,数据(平台、开发、管理、分析)
  • 基于STM32的宠物托运智能控制系统的设计(第十七届研电赛)
  • 数据结构的奇妙世界:实用算法与实际应用
  • uniapp实现表格冻结
  • Spring面试题11:什么是Spring的依赖注入
  • 用于设计 CNN 的 7 种不同卷积
  • 备受以太坊基金会青睐的 Hexlink,构建亿级用户涌入 Web3的入口
  • 合约升级标准 ERC2535 的设计解析和不足
  • 【Vue】ElementUI实现登录注册
  • linux 安装 wordpress
  • LeetCode902最大为 N 的数字组合(相关话题:数位DP问题,递归遍历和减枝)
  • USB总线-Linux内核USB3.0主机控制器驱动框架分析(十二)
  • SQL模板-用户留存率计算
  • LeakCanary 源码详解(3)
  • springboot使用SSE
  • 搞定ESD(一):静电放电测试标准解析
  • 问界M7的诸多优点(自动驾驶走进我们的生活二)
  • [运维|数据库] msql中的 FIND_IN_SET如何转化为pg数据中的ARRAY_POSITION的函数
  • LeetCode 面试题 05.03. 翻转数位
  • Fiddler抓包工具配置+Jmeter基本使用
  • IOTE 2023国际物联网展直击:芯与物发布全新定位芯片,助力多领域智能化发展
  • 【软件设计师-从小白到大牛】上午题基础篇:第二章 操作系统
  • 【20230921】关于sing-box命令行程序开机自启动运行(Windows、Linux)
  • LeetCode 75-02:字符串的最大公因子
  • k8s1.19使用ceph14
  • Leetcode 50. Pow(x, n)