Unity工具类 ResourcesManager资源管理器

Unity Resources资源管理器

一、目的

通常来讲,直接从Resources下进行资源的加载,有几个明显的缺点

  1. 需要指明资源的详细路径,一旦资源位置迁移,代码也要做出相应的改变
  2. 缺少缓存机制,从Resources下加载资源的过程较为耗时,同一个资源只加载一次
  3. 部分API,如异步加载使用起来较为复杂,和游戏逻辑放在一起紧耦合,需要单独封装

Resources资源管理器则是对这些方面进行优化,并加以封装,全局唯一专门负责Resources资源的加载

二、Resources管理器

1. 路径问题的解决

资源的加载,一般路径会因为文件夹结构的调整经常改变,但资源名称很少会进行更改,解决这个问题的核心思想就是如何只用资源名称就能加载到Resources文件夹下指定的资源,可以编写一个Editor工具用来构建资源名称 和 资源路径的映射关系

Editor工具

一般资源类型,比较常见的有prefab预制体,audioClip音频,texture图片等等,利用U3D提供的AssetDatabase下的API可以轻松获取到Resources下指定类型的资源路径,具体见代码注释。

using System.Collections.Generic;
using System.IO;
using UnityEditor;

public class GenrateResConfig : Editor
{
    //映射文件保存位置
    private static string configPath = "Assets/StreamingAssets/ConfigMap.txt";
    //在Unity编辑器界面中 设置一组下拉选项,指定选项路径,点击后就会触发此函数
    [MenuItem("Tools/Resources/Generate ResConfig File")]
    public static void Generate()
    {
        Dictionary<string, List<string>> dic = new Dictionary<string, List<string>>();
        File.Delete(configPath);
        //TODO: 将更多需要保存的资源类型和筛选器加入到此字典中
        //资源类型可以查阅Unity API,根据类型其后缀可能多种多样根据实际资源决定
        dic.Add("prefab", new List<string>() { "prefab" });
        dic.Add("audioclip", new List<string>() { "mp3", "mp4" });
        dic.Add("texture", new List<string>() { "png", "jpg", "bmp" });
        foreach (var item in dic)
        {
            string[] mapArr = getMapping(item.Key,item.Value);
            //3.写入文件
            if (!Directory.Exists("Assets/StreamingAssets"))
            {
                Directory.CreateDirectory("Assets/StreamingAssets");
            }
            File.AppendAllLines(configPath, mapArr);
        }
        //刷新
        AssetDatabase.Refresh();

    }

    private static string[] getMapping(string type,List<string> suffixes)
    {
        //生成资源配置文件
        //1.查找Resources目录下所有预制件的完整路径
        //返回值为GUID 资源编号 , 参数1 指明类型,参数2 在哪些路径下查找
        string[] resFiles = AssetDatabase.FindAssets($"t:{type}", new string[] { "Assets/Resources" });
        for (int i = 0; i < resFiles.Length; i++)
        {
            resFiles[i] = AssetDatabase.GUIDToAssetPath(resFiles[i]);
            //2.生成对应关系  名称=路径
            string fileName = Path.GetFileNameWithoutExtension(resFiles[i]);
            string filePath = resFiles[i].Replace("Assets/Resources/", string.Empty);
            foreach(string filter in suffixes)
            {
                filePath = filePath.Replace("."+filter, string.Empty);
            }
            resFiles[i] = fileName + "=" + filePath;
        }
        return resFiles;
    }
}

注意点

  1. Unity提供的AssetDatabase.FindAssets方法,指定的类型无视大小写,但类型名必须正确,图片texture , 音乐audioclip, 预制体prefab等等。
  2. 最终生成的路径映射和名称,均不带后缀,这也是为什么要添加filter List的原因,应该把此种类型文件的后缀都删除。
  3. 如果除了图片,音乐,预制体有新的资源存储,应在代码的TODO处添加对应新的类型和筛选器列表。

最终能在StreamingAssets文件夹下得到一个ConfigMap.txt如下图样式

在这里插入图片描述

2. 文件的读取和解析

对于在StreamingAssets下文件的读取,Unity提供了相关的API,可以分行读取,一次性读取等等

通常来讲,配置文件都会按行存储,每一行都会读取出来,并放入合适的数据结构中,在本章中每一行的数据格式为xx = xx/xx/xx,键值对的形式很适合使用Dictionary来存储,下面的ConfigReader类负责按行读取文件,并提供委托可以处理单行的数据,是通用读取类。

using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
using System;
namespace Common
{
    ///<summary>
    ///负责读取配置文件并提供解析行
    ///<summary>
    public class ConfigReader
    {
        /// <summary>
        /// 加载(获取)配置文件
        /// </summary>
        /// <param name="fileName">文件名</param>
        /// <returns>获取的字符串(待解析)</returns>
        public static string GetConfigFile(string fileName)
        {
            string url;
            //在移动端通过Application.StreamingAssets不靠谱可能会出错 应用以下方法
            //url根据不同平台有不同的路径,利用宏标签在编译期间运行,根据所处平台选择哪条语句
            //发布后相当于就选择了一条合适的语句url=xxxx  
            //if(Application.platform == RuntimePlatform.Android) 性能稍差
#if UNITY_EDITOR || UNITY_STANDALONE
            url = "file://" + Application.dataPath + "/StreamingAssets/" + fileName;
#elif UNITY_IPHONE
            url = "file://" + Application.dataPath + "/Raw/"+fileName;
#elif UNITY_ANDROID
            url = "jar:file://" + Application.dataPath + "!/assets/"+fileName;
#endif


            //移动端根据url加载文件资源最终返回一个string
            UnityWebRequest www = UnityWebRequest.Get(url);
            www.SendWebRequest();
            while (true)
            {
                if (www.downloadHandler.isDone)
                    return www.downloadHandler.text;
            }
        }

        //handler委托函数负责处理每行的数据
        public static void Reader(string fileContent,Action<string> handler)
        {

            //读出来的string   "xxxName=xxxPath/r/nxxxName=xxxPath/r/n....
            //解析字符串,利用StringReader字符串读取器,流用完必须释放内存
            //using 代码块结束自动释放资源
            using (StringReader reader = new StringReader(fileContent))
            {
                string line;
                while ((line = reader.ReadLine()) != null) //逐行获取字符串
                {
                    //解析方法
                    handler(line);
                }
            }
        }
    }

}

使用方法如下, 将映射文件解析到Dictionary中便于使用

string fileContent = ConfigReader.GetConfigFile("ConfigMap.txt");
configMap = new Dictionary<string, string>();
//解析文件(string ----> configMap)
ConfigReader.Reader(fileContent, BuildMap);
/// <summary>
/// 负责处理解析每行字符串的功能
/// </summary>
/// <param name="line">每行字符串</param>
private void BuildMap(string line)
{
     string[] keyValue = line.Split('=');
     configMap.Add(keyValue[0], keyValue[1]);
}
3. ResourcesManager

经过创建映射文件,读取映射文件,解析映射文件,现在我们能够得到一个 名字-路径的映射字典,只需要封装一些通用的方法,即很容易的实现通过名称加载资源,在这个管理类中,还需解决缓存防止多次加载的问题,异步加载的协程和委托等等,下面是完整源码。 里面涉及到的MonoSingleton的脚本是Mono单例类,可以自行查阅实现单例类或者阅读 笔者的这篇博客https://blog.csdn.net/Q540670228/article/details/125607457

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

namespace Common
{
    ///<summary>
    ///资源加载管理类
    ///<summary>
    public class ResourcesManager : MonoSingleton<ResourcesManager>
    {
        //负责储存资源的名字和路径映射
        private static Dictionary<string, string> configMap;

        //缓存已经加载的资源
        private static Dictionary<string, object> cacheDic;

        protected override void Init()
        {
            base.Init();
            //加载文件
            string fileContent = ConfigReader.GetConfigFile("ConfigMap.txt");

            configMap = new Dictionary<string, string>();

            cacheDic = new Dictionary<string, object>();

            //解析文件(string ----> prefabConfigMap)
            ConfigReader.Reader(fileContent, BuildMap);
        }

        /// <summary>
        /// 负责处理解析每行字符串的功能
        /// </summary>
        /// <param name="line">每行字符串</param>
        private void BuildMap(string line)
        {
            string[] keyValue = line.Split('=');
            configMap.Add(keyValue[0], keyValue[1]);

        }


        /// <summary>
        /// 同步加载资源
        /// </summary>
        /// <typeparam name="T">加载资源类型</typeparam>
        /// <param name="resourceName">资源名称</param>
        /// <returns></returns>
        public T Load<T>(string resourceName)where T:Object
        {
            //从字典中获取路径加载预制件
            if (configMap.ContainsKey(resourceName))
            {
                string resourceKey = $"{resourceName}_{typeof(T)}";
                if (!cacheDic.ContainsKey(resourceKey))
                {
                    T res = Resources.Load<T>(configMap[resourceName]);
                    cacheDic.Add(resourceKey, res);
                }    
                return cacheDic[resourceKey] as T;

            }
            else return default(T);
        }


        /// <summary>
        /// 异步加载资源
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="resourceName"></param>
        /// <param name="action"></param>
        public void LoadAsync<T>(string resourceName,UnityAction<T> action = null) where T : Object
        {
            StartCoroutine(LoadAsyncCore<T>(resourceName, action));
        }
        private IEnumerator LoadAsyncCore<T>(string resourceName, UnityAction<T> action) where T : Object
        {
            if (configMap.ContainsKey(resourceName))
            {
                string resourceKey = $"{resourceName}_{typeof(T)}";
                if (!cacheDic.ContainsKey(resourceKey))
                {
                    ResourceRequest request = Resources.LoadAsync<T>(configMap[resourceName]);
                    yield return request;
                    //由于采用异步协程,需要二重判断
                    if (!cacheDic.ContainsKey(resourceKey))
                        cacheDic.Add(resourceKey, request.asset as T);
                }
                action?.Invoke(cacheDic[resourceKey] as T);

            }
            else action?.Invoke(default(T));
        }

    }

}

使用方法举例如下,同步加载较为简单不再赘述

异步加载

//在资源中加载音乐文件
AudioSource bkMusic; //省略获取组件的代码
string musicName = "Bk";
ResourcesManager.Instance.LoadAsync<AudioClip>(musicName,(clip)=> {
    bkMusic.clip = clip;
    bkMusic.Play();
    bkMusic.loop = true;
    bkMusic.volume = bkVolume;
});
  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Unity 资源管理是一种在Unity开发环境中使用的工具,它可以帮助开发者轻松地浏览、搜索和下载Unity的各种资源。这些资源包括项目模板、插件、脚本、材质、模型等等。以下是关于Unity资源管理下载的一些信息。 要使用Unity资源管理下载资源,首先需要在Unity中打开Asset Store窗口。在Asset Store中,可以找到各种各样的资源供开发者下载和使用。开发者可以通过关键字搜索来浏览资源,也可以按照分类浏览资源,例如游戏对象、材质、效果等等。当找到想要的资源后,只需点击下载按钮即可将资源添加到Unity的项目中。 Unity资源管理的下载速度通常取决于网络连接的速度和资源的大小。资源下载完成后,可以在Unity的项目中使用它们。通过资源管理,开发者可以方便地查看和管理已下载的资源,可以进行重命名、移动、删除等操作。 此外,Unity资源管理也提供了一些高级功能,例如资源预览、评分和评论。开发者可以在资源页面中查看资源的预览图和详细信息,并查看其他用户对该资源的评分和评论。这些功能可以帮助开发者更好地了解资源的质量和适用性。 总的来说,Unity资源管理Unity开发环境中的一个强大的工具,可以帮助开发者更轻松地浏览、搜索和下载各种资源。它能够大大提高开发效率,使开发者能够更快地找到并使用适合自己项目的资源

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值