【GameFramework框架内置模块】8、文件系统(File System)

推荐阅读

  • CSDN主页
  • GitHub开源地址
  • Unity3D插件分享
  • 简书地址

大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。

一、前言

【GameFramework框架】系列教程目录:
https://blog.csdn.net/q764424567/article/details/135831551

最近好忙,鸽了好久。不能颓废了,继续发力。

今天分享的是GF框架的File System文件系统。

二、正文

2-1、介绍

首先,引用比较官方的说法:

GF的FileSystem虚拟文件系统,使用类似磁盘存储的感念对零散的文件进行集中管理,优化资源加载时产生的内存分配,也可以对资源进行局部片段加载,极大的提升了资源加载的性能。

通俗点讲就是,我这个模块就是加载磁盘文件的,优化加载时产生的内存分配。

那么,它是怎么优化的呢?

比如说一个文件夹里面有几千个文件,传输的话会非常的慢,因为普通的文件系统是一个一个读取写入,每次读取写入都是一次磁盘IO,所以花费大量时间,而这个文件系统将会将整个文件夹当成一个压缩包,然后再进行读取写入,那么就减少了大量的IO,提高了性能。

2-2、使用说明

查看文件是否存在:

using GameFramework.FileSystem;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityGameFramework.Runtime;

public class TestFileSystem : MonoBehaviour
{
    void Start()
    {
        FileSystemComponent fileSystemComponent = GameEntry.GetComponent<FileSystemComponent>();
        string fullPath = System.IO.Path.Combine(Application.persistentDataPath, "FileSystem.dat");

         检查是否存在文件系统,参数要传递的是文件系统的完整路径
        bool hasFileSystem = fileSystemComponent.HasFileSystem(fullPath);
        Debug.Log(hasFileSystem);
    }
}

读写文件:

using GameFramework.FileSystem;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityGameFramework.Runtime;

public class TestFileSystem : MonoBehaviour
{
    void Start()
    {
        FileSystemComponent fileSystemComponent = GameEntry.GetComponent<FileSystemComponent>();
        // 要创建的文件系统的完整路径
        string fullPath = Path.Combine(Application.persistentDataPath, "FileSystem.dat");

        // 要创建的文件系统最大能容纳文件数量
        int maxFileCount = 16;
        // 要创建的文件系统最大能容纳的块数据数量
        int maxBlockCount = 256;

        // 创建文件系统(使用读写模式进行访问)
        IFileSystem fileSystem = fileSystemComponent.CreateFileSystem(fullPath, FileSystemAccess.ReadWrite, maxFileCount, maxBlockCount);

        // 将字节数组写入指定文件
        byte[] buffer = new byte[1024]; // 读取文件片段使用的 buffer,用此方式能够复用 buffer 来消除 GCAlloc
        bool result = fileSystem.WriteFile("FileName.dat", buffer);

        // 将流写入指定文件
        FileStream fs = new FileStream("FileName.dat", FileMode.Create, FileAccess.Write);
        bool result2 = fileSystem.WriteFile("FileName.dat", fs);

        // 将物理文件写入指定文件
        bool result3 = fileSystem.WriteFile("FileName.dat", @"E:\PhysicalFileName.dat");

    }
}

2-3、实现及代码分析

File System虽说是一个独立的模块,但是一般不直接使用,通常要配合其他模块,比较典型的例子就是资源打包和读取额时候。

因为在做资源更新的时候,会将资源进行整理分包加载,资源管理器ResourceComponent继承了File System文件系统,只需要在构建ResoucrceCollection.xml时,在对应的资源Resources标签下指定FileSystem属性即可:在这里插入图片描述
Build资源的时候,会自动将此Resource放入指定的文件系统,运行加载资源时,也会自动去对应的FileSystem文件中进行加载。

下面就以打包和加载来分析FileSystem文件系统的代码实现。

2-3-1、打包代码分析

1、点击Start Build Resource按钮后,会调用BuildResource函数:
在这里插入图片描述
在这里插入图片描述

/// 资源生成器。
internal sealed class ResourceBuilder : EditorWindow
  private void BuildResources()
    {
        if (m_Controller.BuildResources())
        {
            Debug.Log("Build resources success.");
            SaveConfiguration();
        }
        else{
            Debug.LogWarning("Build resources failure.");
        }
    }
}

2、创建文件系统,存储在文件系统中:

public sealed partial class ResourceBuilderController
{
    public bool BuildResources()
    {
    	......
		AssetBundleManifest assetBundleManifest = BuildPipeline.BuildAssetBundles(workingPath, assetBundleBuildDatas, buildAssetBundleOptions, GetBuildTarget(platform));
		......
		if (OutputPackageSelected)
        {
            CreateFileSystems(m_ResourceDatas.Values, outputPackagePath, m_OutputPackageFileSystems);
        }
        ......
}

3、创建文件系统后,遍历所有的打包资源,获取标记了FileSystem标签的项,加入文件系统:

private void CreateFileSystems(IEnumerable<ResourceData> resourceDatas, string outputPath, Dictionary<string, IFileSystem> outputFileSystem)
{
    outputFileSystem.Clear();
    string[] fileSystemNames = GetFileSystemNames(resourceDatas);
    if (fileSystemNames.Length > 0 && m_FileSystemManager == null)
    {
        m_FileSystemManager = GameFrameworkEntry.GetModule<IFileSystemManager>();
        m_FileSystemManager.SetFileSystemHelper(new FileSystemHelper());
    }

    foreach (string fileSystemName in fileSystemNames)
    {
        int fileCount = GetResourceIndexesFromFileSystem(resourceDatas, fileSystemName).Length;
        string fullPath = Utility.Path.GetRegularPath(Path.Combine(outputPath, Utility.Text.Format("{0}.{1}", fileSystemName, DefaultExtension)));
        IFileSystem fileSystem = m_FileSystemManager.CreateFileSystem(fullPath, FileSystemAccess.Write, fileCount, fileCount);
        outputFileSystem.Add(fileSystemName, fileSystem);
    }
}

4、写入文件系统,把每个经过加密处理的资源写入到指定的文件系统中:

public sealed partial class ResourceBuilderController
{
    private bool ProcessOutput(Platform platform, string outputPackagePath, string outputFullPath, string outputPackedPath, bool additionalCompressionSelected, string name, string variant, string fileSystem, ResourceData resourceData, byte[] bytes, int length, int hashCode, int compressedLength, int compressedHashCode)
    {
        string fullNameWithExtension = Utility.Text.Format("{0}.{1}", GetResourceFullName(name, variant), GetExtension(resourceData));

        if (OutputPackageSelected)
        {
            if (!string.IsNullOrEmpty(fileSystem))
            {
                if (!m_OutputPackageFileSystems[fileSystem].WriteFile(fullNameWithExtension, bytes))
                {
                    return false;
                }
            }
        }
	}
}
2-3-2、加载代码分析

1、使用ResourceManager加载资源:

internal class ResourceManager : GameFrameworkModule, IResourceManager
{
    /// 异步加载资源。
    public void LoadAsset(string assetName, Type assetType, int priority, LoadAssetCallbacks loadAssetCallbacks, object userData){
        m_ResourceLoader.LoadAsset(assetName, assetType, priority, loadAssetCallbacks, userData);
    }
}

2、使用Resource任务池加载,使用多个LoadResourceAgent代理,异步加载资源,每个资源加载都是一个LoadAssetTask,每个任务开始时都会分配一个LoadResourceAgent代理:

internal sealed class TaskPool<T> where T : TaskBase
{
    public void Update(float elapseSeconds, float realElapseSeconds)
    {
        ProcessRunningTasks(elapseSeconds, realElapseSeconds);
        ProcessWaitingTasks(elapseSeconds, realElapseSeconds);
    }
    //处理任务池等待列表
	private void ProcessWaitingTasks(float elapseSeconds, float realElapseSeconds)
    {
        LinkedListNode<T> current = m_WaitingTasks.First;
        while (current != null && FreeAgentCount > 0)
        {
            ITaskAgent<T> agent = m_FreeAgents.Pop();
            LinkedListNode<ITaskAgent<T>> agentNode = m_WorkingAgents.AddLast(agent);
            T task = current.Value;
            LinkedListNode<T> next = current.Next;
            //LoadResourceAgent代理开始读取资源
            StartTaskStatus status = agent.Start(task);
            current = next;
            ...
        }
    }
}

3、资源文件的读取,使用了资源加载代理辅助器,分别为LoadFromFile、LoadFromMemory两种不同的加载方式:

/// 默认加载资源代理辅助器。
public class DefaultLoadResourceAgentHelper : LoadResourceAgentHelperBase, IDisposable
{
    /// 通过加载资源代理辅助器开始异步读取资源文件。
    public override void ReadFile(string fullPath)
    {
        m_FileAssetBundleCreateRequest = AssetBundle.LoadFromFileAsync(fullPath);
    }

    /// 通过加载资源代理辅助器开始异步读取资源文件。
    public override void ReadFile(IFileSystem fileSystem, string name)
    {
        FileInfo fileInfo = fileSystem.GetFileInfo(name);
        m_FileAssetBundleCreateRequest = AssetBundle.LoadFromFileAsync(fileSystem.FullPath, 0u, (ulong)fileInfo.Offset);
    }

    /// 通过加载资源代理辅助器开始异步读取资源二进制流。
    public override void ReadBytes(string fullPath)
    {
        m_UnityWebRequest = UnityWebRequest.Get(Utility.Path.GetRemotePath(fullPath));
        m_UnityWebRequest.SendWebRequest();
    }

    /// 通过加载资源代理辅助器开始异步读取资源二进制流。
    public override void ReadBytes(IFileSystem fileSystem, string name)
    {
        byte[] bytes = fileSystem.ReadFile(name);
        m_LoadResourceAgentHelperReadBytesCompleteEventHandler(this, LoadResourceAgentHelperReadBytesCompleteEventArgs.Create(bytes));
    }
}

4、最后,通过LoadMain,从Bundle中读取资源,最后运行监听事件LoadComplete,加载全部完成:

/// 默认加载资源代理辅助器。
public class DefaultLoadResourceAgentHelper : LoadResourceAgentHelperBase, IDisposable
{
    /// 通过加载资源代理辅助器开始异步读取资源文件。
    public override void ReadFile(string fullPath)
    {
        m_FileAssetBundleCreateRequest = AssetBundle.LoadFromFileAsync(fullPath);
    }

    /// 通过加载资源代理辅助器开始异步读取资源文件。
    public override void ReadFile(IFileSystem fileSystem, string name)
    {
        FileInfo fileInfo = fileSystem.GetFileInfo(name);
        m_FileAssetBundleCreateRequest = AssetBundle.LoadFromFileAsync(fileSystem.FullPath, 0u, (ulong)fileInfo.Offset);
    }

    /// 通过加载资源代理辅助器开始异步读取资源二进制流。
    public override void ReadBytes(string fullPath)
    {
        m_UnityWebRequest = UnityWebRequest.Get(Utility.Path.GetRemotePath(fullPath));
        m_UnityWebRequest.SendWebRequest();
    }

    /// 通过加载资源代理辅助器开始异步读取资源二进制流。
    public override void ReadBytes(IFileSystem fileSystem, string name)
    {
        byte[] bytes = fileSystem.ReadFile(name);
        m_LoadResourceAgentHelperReadBytesCompleteEventHandler(this, LoadResourceAgentHelperReadBytesCompleteEventArgs.Create(bytes));
    }
}

三、后记

如果觉得本篇文章有用别忘了点个关注,关注不迷路,持续分享更多Unity干货文章。


你的点赞就是对博主的支持,有问题记得留言:

博主主页有联系方式。

博主还有跟多宝藏文章等待你的发掘哦:

专栏方向简介
Unity3D开发小游戏小游戏开发教程分享一些使用Unity3D引擎开发的小游戏,分享一些制作小游戏的教程。
Unity3D从入门到进阶入门从自学Unity中获取灵感,总结从零开始学习Unity的路线,有C#和Unity的知识。
Unity3D之UGUIUGUIUnity的UI系统UGUI全解析,从UGUI的基础控件开始讲起,然后将UGUI的原理,UGUI的使用全面教学。
Unity3D之读取数据文件读取使用Unity3D读取txt文档、json文档、xml文档、csv文档、Excel文档。
Unity3D之数据集合数据集合数组集合:数组、List、字典、堆栈、链表等数据集合知识分享。
Unity3D之VR/AR(虚拟仿真)开发虚拟仿真总结博主工作常见的虚拟仿真需求进行案例讲解。
Unity3D之插件插件主要分享在Unity开发中用到的一些插件使用方法,插件介绍等
Unity3D之日常开发日常记录主要是博主日常开发中用到的,用到的方法技巧,开发思路,代码分享等
Unity3D之日常BUG日常记录记录在使用Unity3D编辑器开发项目过程中,遇到的BUG和坑,让后来人可以有些参考。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/467228.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何解决网络中IP地址发生冲突故障?

0、前言 本专栏为个人备考软考嵌入式系统设计师的复习笔记&#xff0c;未经本人许可&#xff0c;请勿转载&#xff0c;如发现本笔记内容的错误还望各位不吝赐教&#xff08;笔记内容可能有误怕产生错误引导&#xff09;。 1、个人IP地址冲突解决方案 首先winR&#xff0c;调出…

Centos strema 9 环境部署Glusterfs9

本文档只是创建复制卷&#xff0c;分布式卷&#xff0c;分布式复制卷&#xff0c;纠删卷 操作系统 内核 角色 Ip地址 说明 CentOS Stream 9 x86_64 5.14.0-427.el9.x86_64 客户端 client 192.168.80.119 挂载存储业务机器 CentOS Stream 9 x86_64 5.14.0-427.el9.x8…

Fiddler不仅可以抓包,还可以做接口测试喔

前言 Fiddler最大的优势在于抓包&#xff0c;我们大部分使用的功能也在抓包的功能上&#xff0c;Fiddler做接口测试也是非常方便的。对应没有接口测试文档的时候&#xff0c;可以直接抓完包后&#xff0c;copy请求参数&#xff0c;修改下就可以了。 Composer简介 点开右侧Co…

【推荐系统】NCF神经协同过滤

NCF框架 NCF框架是本文要实现的3个模型的主体结构。 首先是输入层&#xff0c;分别包含两个特征向量 v u v_u vu​和 v i v_i vi​&#xff0c;描述了用户u和物品i。输入仅由一个用户向量和一个物品向量构成&#xff0c;它们分别是以one-hot编码的二值化稀疏向量。 接着是Em…

章节2:单词本该这样记

为什么我们记不住单词&#xff1f; 单词不是被胡编乱造出来的&#xff0c;单词是有规律的&#xff0c;单词是符合人类的逻辑的。 单词实际意思结构意义历史文化 我们要怎么记单词&#xff1f; 掌握单词的结构规律了解与单词有关的历史文化灵活巧计&#xff0c;不要太拘泥于…

MySQL的insert-on-duplicate语句详解

一、insert-on-duplicate语句语法 注意&#xff1a;ON DUPLICATE KEY UPDATE只是 MySQL的特有语法&#xff0c;并不是SQL标准语法&#xff01; INSERT INTO … ON DUPLICATE KEY UPDATE 是 MySQL 中一种用于插入数据并处理重复键冲突的语法。 这个语法适用于在 insert的时候…

MindGraph:文字生成知识图

欢迎来到MindGraph&#xff0c;这是一个概念验证、开源的、以API为先的基于图形的项目&#xff0c;旨在通过自然语言的交互&#xff08;输入和输出&#xff09;来构建和定制CRM解决方案。该原型旨在便于集成和扩展。以下是关于X的公告&#xff0c;提供更多背景信息。开始之前&a…

iOS报错-Command PhaseScriptExecution failed with a nonzero exit code

问题&#xff1a;iOS debug没问题&#xff0c;一打包就报错&#xff1a; Command PhaseScriptExecution failed with a nonzero exit code 解决方法如下&#xff1a; 在项目的Pods目录下&#xff0c;找到Targets Support Files->Pods-xxxx-frameworks.sh 如下&#xff1a…

C语言数组—二维数组

二维数组的创建 //数组创建 int arr[3][4]; //三行四列&#xff0c;存放整型变量 double arr[2][4];二维数组的初始化 我们如果这样初始化&#xff0c;效果是什么样的呢 int arr[3][4] { 1,2,3,4,5,6,7,8,9,10,11,12 };那如果我们不写满十二个呢 int arr[3][4] { 1,2,3,4…

数据可信流通:从运维信任到技术信任

1.数据可信流通概念 "数据可信流通"通常指的是确保数据在不同系统、应用程序或者组织之间的传输和交换过程中的可信性、完整性和安全性。在数据流通的过程中&#xff0c;确保数据的真实性、完整性和保密性是非常重要的&#xff0c;尤其是涉及到敏感信息或者重要数据…

Day21:实现退出功能、开发账号设置、检查登录状态

实现退出功能 将登录凭证修改为失效状态。跳转至网站首页。 数据访问层 不用写了&#xff0c;已经有了updateStatus方法&#xff1b; 业务层 UserService public void logout(String ticket) {loginTicketMapper.updateStatus(ticket, 1);}Controller层 RequestMapping(p…

glib交叉编译

Glib交叉编译 逸一时&#xff0c;误一世。 —— 田所浩二「夏夜银梦」 交叉编译 GLib 涉及到在一个平台上生成能够在另一个平台上运行的目标文件。在这种情况下&#xff0c;我们将会在一台主机&#xff08;通常是开发机器&#xff09;上使用交叉编译工具链来构建 GLib 库&#…

Linux:git的基础操作

git的下载 版本控制系统一般分为两种&#xff0c;集中式版本控制系统&#xff0c;分布式版本控制系统 什么是集中式版本控制系统&#xff1a;版本库集中存放在中央服务器&#xff0c;工作时候使用自己的电脑&#xff0c;当工作时候在中央服务器上拉取最新版本的代码&#xff0c…

微服务:高并发带来的问题的容错方案

1.相关脚本&#xff08;陈天狼&#xff09; 启动nacos客户端&#xff1a; startup.cmd -m standalone 启动sentinel控制台&#xff1a; # 直接使⽤jar命令启动项⽬(控制台本身是⼀个SpringBoot项⽬) java -Dserver.port8080 -Dcsp.sentinel.dashboard.serverlocalhost:808…

【渗透测试实战】用渗透实例sqllibs第46关来讲解SQL报错注入的操作顺序

1、查出库名&#xff08;database()&#xff09; http://127.0.0.1/sqli7/Less-46/?sortupdatexml(1,if(12,1,concat(0x7e,database(),0x7e)),1) 2、通过库名&#xff0c;查表名&#xff08;table_name&#xff09; http://127.0.0.1/sqli7/Less-46/?sortupdatexml(1,if(12…

VMware虚拟机和主机之间无法复制粘贴,移动文件,重新安装vmware-tools变灰,VMware Tools继续运行脚本未能在虚拟机中成功运行。

起初&#xff0c;虚拟机只是无法和主机之间进行复制粘贴&#xff0c;移动文件。查询了很多资料,反反复复地安装卸载vmware-tools&#xff0c;但是都没有成功。通过这篇文章&#xff1a;虚拟机安装VMware Tools的两种方法_vmware tools有3种安装方式-CSDN博客 安装了vmware_too…

中国巨型地下中微子实验室准备探究宇宙奥秘

JUNO设施将于今年上线&#xff0c;将有助于确定哪种类型的中微子质量最高 - 这是物理学中最大的谜团之一。 中国江门地下中微子天文台&#xff08;JUNO&#xff09;的建设工作。朱诺号希望在2024年底之前探测到中微子。图片来源&#xff1a;Qiu Xinsheng/VCG via Getty 开平区…

JAVA八股day1

遇到的问题 相比于包装类型&#xff08;对象类型&#xff09;&#xff0c; 基本数据类型占用的空间往往非常小为什么说是几乎所有对象实例都存在于堆中呢&#xff1f;静态变量和成员变量、成员变量和局部变量的区别为什么浮点数运算的时候会有精度丢失的风险&#xff1f;如何解…

PHP姓名快速匿名化工具(重组脱敏)

PHP姓名重组工具(脱敏/匿名化工具) 将excel数据姓名列粘贴提交&#xff0c;得到随机姓随机中间字随机尾字的重组姓名 那些年自用瞎搞的代码&#xff0c;今日整理成网页交提交得到结果的交互功能分享。 <?php //PHP姓名重组工具(脱敏/匿名化工具) //将excel数据姓名列粘贴…

Linux TCP参数——tcp_adv_win_scale

文章目录 tcp_adv_win_scaleip-sysctl.txt解释buffering overhead内核缓存和应用缓存示例计算深入理解从2到1(tcp_adv_win_scale的值)总结 tcp_adv_win_scale adv-advise&#xff1b;win-window; 用于指示TCP中接收缓存比例的值。 static inline int tcp_win_from_space(int …