推荐阅读
- 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之UGUI | UGUI | Unity的UI系统UGUI全解析,从UGUI的基础控件开始讲起,然后将UGUI的原理,UGUI的使用全面教学。 |
Unity3D之读取数据 | 文件读取 | 使用Unity3D读取txt文档、json文档、xml文档、csv文档、Excel文档。 |
Unity3D之数据集合 | 数据集合 | 数组集合:数组、List、字典、堆栈、链表等数据集合知识分享。 |
Unity3D之VR/AR(虚拟仿真)开发 | 虚拟仿真 | 总结博主工作常见的虚拟仿真需求进行案例讲解。 |
Unity3D之插件 | 插件 | 主要分享在Unity开发中用到的一些插件使用方法,插件介绍等 |
Unity3D之日常开发 | 日常记录 | 主要是博主日常开发中用到的,用到的方法技巧,开发思路,代码分享等 |
Unity3D之日常BUG | 日常记录 | 记录在使用Unity3D编辑器开发项目过程中,遇到的BUG和坑,让后来人可以有些参考。 |