Unity 资源映射框架

前言

我们知道,使用 Resources.Load< T >(path : string) 可以加载 Resources 文件夹下的文件,但是在项目中,需要一次加载多个文件时文件可能分布在多个目录下,我们不方便去简单地加载文件,同时,加载的文件的目录也可能变动,这使得代码中的目录字符串修改起来十分麻烦。

于是我们可以在编辑器中提供一个生成资源映射表的方法,当 Resources 文件夹下文件有变动时,就在编辑器中调用一次方法,重新生成资源映射信息。使程序运行时可以查询资源映射表,通过文件的名字直接取得文件的目录。

由于生成的是 名称(key) - 路径(value) 映射表,该框架不适用于有重名文件的情况,需要提前规范好命名。

生成资源映射信息

我们可以使用 AssetDatabase 来获取文件信息,在编辑器中生成资源映射信息文件,生成的文件放在 StreamingAssets 目录下,该目录中的文件不会被压缩,且在移动端是只读的,配置文件一般会放在此处。

注意: 该类只需要在编辑器中运行,我们需要创建一个Editor 文件夹,把该类放在其中,使该类处于 Assembly-CSharp 程序集中。该程序集不会被打包到发布后的游戏中。

using System.IO;
using UnityEditor;
using UnityEngine;

public class GenerateResourceConfig : Editor
{
    //需要映射的文件类型
    private static string[] fileTypes = { "prefab", "material" };

    //储存上面类型对应的扩展名
    private static string[] extensionStr = { "prefab", "mat" };

    //显示在Unity菜单工具栏中,方便点击调用
    [MenuItem("Tools/Resource/GenerateResourceConfig")]
    public static void GenerateConfig()
    {
        Debug.Log("GenerateConfig");
        for(int i = 0; i < fileTypes.Length; ++i)
        {
            //将映射信息写入到文件中
            if(i == 0)
                File.WriteAllLines("Assets/StreamingAssets/ConfigMap.txt", GenerateStrInfo(i));
            else
                File.AppendAllLines("Assets/StreamingAssets/ConfigMap.txt", GenerateStrInfo(i));
        }
        //刷新Assets
        AssetDatabase.Refresh();
    }

    public static string[] GenerateStrInfo(int type)
    {
        //获取GUID
        string[] resPaths = AssetDatabase.FindAssets("t:" + fileTypes[type], new string[] { "Assets/Resources" });
        for (int i = 0; i < resPaths.Length; ++i)
        {
            //通过GUID获取路径
            resPaths[i] = AssetDatabase.GUIDToAssetPath(resPaths[i]);
            //获取文件名
            string name = Path.GetFileNameWithoutExtension(resPaths[i]);
            //获取Resources Load时需要的路径(去掉"Assets/Resources/"和文件扩展名)
            string filePath = resPaths[i].Replace("Assets/Resources/", string.Empty).Replace("." + extensionStr[type], string.Empty);
            //通过等号分割key value,此处不宜使用空格分割,因为路径或文件名中可能包含空格
            resPaths[i] = name + "=" + filePath;
        }
        return resPaths;
    }
}

测试

提前创建好 StreamingAssets 文件夹。

在 Resources 文件夹下创建一些预制件和材质用于测试:
在这里插入图片描述

在编辑器中调用 GenerateConfig() 方法:

在这里插入图片描述

这时可以看到 StreamingAssets 目录下出现了一个 ConfigMap 的的文件。打开文件,可以看到生成的资源映射信息:

在这里插入图片描述

资源管理器

现在有了资源映射信息,我们需要一个资源管理器类,在游戏一开始读取文件,加载到 Dictionary 中。并封装一个加载资源的方法,通过传入的名称返回资源。

using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;

public class ResourceManager : MonoBehaviour
{
    private static Dictionary<string, string> configMap;

    private static string fileName = "ConfigMap.txt";

    static ResourceManager(){
        string fileContent = GetConfigFile();
        InitDict(fileContent);
    }
    
    /// <summary>
    /// 使用 UnityWebRequest 加载config文件
    /// </summary>
    private static string GetConfigFile()
    {
        string url;

        /*
         * 使用Application.streamingAssetsPath拼接的路径偶尔会不正确
         * 此处若出错则无法加载资源配置文件
         * 导致游戏中所有使用ResourcesManager加载资源的语句异常
         * 该处分别判断平台做适配更为保险
         * 
         * 条件编译是最优先处理的,不满足宏条件的语句不会被编译
         * 使用该方法效率比if else判断更佳
         */
        #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

        UnityWebRequest www = UnityWebRequest.Get(url);
        
        www.SendWebRequest();
        
        if (!string.IsNullOrEmpty(www.error) || www.isHttpError)
        {
            Debug.LogError("加载资源配置文件失败" + www.error == null ? "" : www.error); 
            return null;
        }
        
        while (true)
        {
            //等待www下载完成后返回text
            if (www.isDone)
            {
                return www.downloadHandler.text;
            }
        }
    }

    /// <summary>
    /// 初始化映射表,将映射信息存入映射表中
    /// </summary>
    private static void InitDict(string fileContent)
    {
        configMap = new Dictionary<string, string>();

        using(StringReader reader = new StringReader(fileContent))
        {
            string line = null;
            while((line = reader.ReadLine()) != null)
            {
                string[] keyValue = line.Split('=');
                if (!configMap.ContainsKey(keyValue[0]))
                    configMap.Add(keyValue[0], keyValue[1]);
                else
                    Debug.LogError("资源映射出错,存在重名文件:" + keyValue[1] + " 与 " + configMap[keyValue[0]]);
            } 
        }
    }

    /// <summary>
    /// 对外提供的方法,根据资源名字直接加载资源并返回
    /// </summary>
    public static GameObject Load<T>(string prefabName)
    {
        return Resources.Load<GameObject>(configMap[prefabName]);
    }
}
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值