Unity加载内置资源

博客围绕Unity内置资源(如精灵、shader等)的代码访问展开。介绍了编辑器中AssetDatabase.GetBuiltinExtraResource和运行时Resources.GetBuiltinResource的使用,虽尝试多种方法但加载精灵无果。最后提出用ScriptObject引用内置资源解决,该方案有避免非公开API、可自动化、不造成资源重复等优点。

内置的资源,如精灵、shader、材质、字体等,怎么用代码去访问?

在编辑器中,可以用AssetDatabase.GetBuiltinExtraResource访问,只是传入的path比较迷,可以说不是path,只是一个key。

具体的path可以网上查得到,比如那几个精灵的path,在ugui的源码中可以找到。

key在哪里定义的?这个就说不清了,可能是资源管理器初始化时写的这么一些特定的path。

运时的的API:Resources.GetBuiltinResource就更是晦涩了,API文档上也没有提及,网上能查到关于它的使用都是加载内置的字体或者材质。

经过长久的尝试,传入各种key,但是精灵是始终加载不出来。

也试过在Editor中拿到对象后,在Resources目录下创建一个asset,也提示资源已存在。

其实在Unity安装路径下的Editor\Data\Resources下,我们可以看到那个文件:unity_builtin_extra,另外,用Unity安装目录中的binary2text.exe也可以把这个二进制文件转成文本格式。可以看到里面是YAML那一套,有shader,有网格,有图片,等等。。对应的数据也是直接嵌在里面的。这个就涉及到子资源的概念了,也就是同一个路径下,可以有多个对象,因为它们是序列化在一个.asset文件中的。

说了这么多,但是最终还是没有通过上面的接口加载到精灵,可能它就没有一个可控查询的名字。

另外内置资源在发布时肯定会按需裁剪的,因为它本身有几十M。

最后转换思路,用的ScriptObject引用内置资源解决的。这种方案好处是:

1、没有用到一些非公开的API和路径,这些很可能变动。

2、在Editor中的接口可以访问内置资源,因此可以全代码、自动化。

3、引用不会造成资源重复。

核心代码很简单:

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

namespace Serialize3rd
{
    [CreateAssetMenu(fileName = "BuiltinResRef")]
    public class BuiltinResRef : ScriptableObject
    {
        public List<UnityEngine.Object> List;

        public void Add(UnityEngine.Object res)
        {
            if (List == null)
                List = new List<UnityEngine.Object>();
            if (List.Contains(res))
                return;
            else
                List.Add(res);
        }

        public UnityEngine.Object Load(Type t, string name)
        {
            foreach (var i in List)
            {
                if (i != null && i.GetType() == t && i.name == name)
                    return i;
            }
            Debug.LogErrorFormat("对内置资源的引用中不存在{0},Type:{1}", name, t);
            return null;
        }
    }
}

然后,如果是在编辑器脚本中增加对内置资源的引用,代码段如下:

if (builtinRef == null)
{
    builtinRef = Resources.Load<BuiltinResRef>(Path.Combine(Utility.ResDir, Utility.BuiltinRefName));
    if (builtinRef == null)
    {
        builtinRef = ScriptableObject.CreateInstance<BuiltinResRef>();
        AssetDatabase.CreateAsset(builtinRef, Path.Combine("Assets", ResFolder, Utility.ResDir, Utility.BuiltinRefName + ".asset"));
    }
}
builtinRef.Add(res);
UnityEditor.EditorUtility.SetDirty(builtinRef);
AssetDatabase.SaveAssets();

 

### 在 Unity 中使用 Thread 实现异步加载资源的最佳实践 尽管 Unity 不建议直接使用 C# 的 `Thread` 类进行多线程编程,但在特定场景下可以通过合理的设计来利用多线程技术提升性能。以下是基于最佳实践的解决方案: #### 1. 避免阻塞主线程 Unity 的渲染和 UI 更新依赖于其主线程,因此任何耗时的操作都不应在主线程中执行。为了实现异步加载资源而不影响主线程性能,可以将耗时的任务放在单独的工作线程中处理。 ```csharp using UnityEngine; using System.Threading; public class AsyncResourceLoader : MonoBehaviour { private bool isLoading = false; private byte[] fileData; private string filePath = "path/to/your/resource"; // 替换为目标路径 void Update() { if (!isLoading && Input.GetKeyDown(KeyCode.Space)) { LoadFileAsync(); } } private void LoadFileAsync() { isLoading = true; new Thread(LoadFileOnThread).Start(); // 启动新线程加载文件 } private void LoadFileOnThread() { try { fileData = System.IO.File.ReadAllBytes(filePath); Debug.Log("File loaded successfully on background thread."); StartCoroutine(ProcessLoadedData()); } catch (System.Exception e) { Debug.LogError($"Error loading file: {e.Message}"); } } private IEnumerator ProcessLoadedData() { yield return null; // 确保回到主线程再继续操作 if (fileData != null) { Texture2D texture = new Texture2D(2, 2); texture.LoadImage(fileData); // 将字节数组转换为纹理 GetComponent<Renderer>().material.mainTexture = texture; Debug.Log("Texture assigned to material."); } isLoading = false; } } ``` 上述代码展示了如何通过工作线程加载文件并将其数据传递回主线程以供进一步处理[^1]。注意,所有涉及 Unity API 的操作都必须在主线程中完成。 --- #### 2. 利用 Task 提升灵活性 相比手动管理线程,`Task` 是更现代的选择,能够简化异步逻辑的同时保持较高的可维护性和扩展性。 ```csharp private async void LoadFileWithTask() { isLoading = true; try { byte[] data = await Task.Run(() => System.IO.File.ReadAllBytes(filePath)); Debug.Log("File loaded with Task."); Texture2D texture = new Texture2D(2, 2); texture.LoadImage(data); Renderer renderer = GetComponent<Renderer>(); if (renderer != null) { renderer.material.mainTexture = texture; } } catch (System.Exception ex) { Debug.LogError($"Failed to load resource asynchronously: {ex.Message}"); } finally { isLoading = false; } } ``` 此方法结合了 `async/await` 和 `Task` 来优化异步流程,同时保留了对复杂任务的支持能力。 --- #### 3. 考虑 Unity 自带工具的优势 除了自定义线程外,还可以充分利用 Unity 提供的功能模块(如 `AssetBundle` 或 `Addressables`),它们内置了许多针对资源管理和异步加载的优化机制。 例如,使用 Addressables 加载资源: ```csharp using UnityEngine.AddressableAssets; IEnumerator LoadResourceUsingAddressables(string label) { var operation = Addressables.LoadAssetAsync<Texture2D>(label); yield return operation; if (operation.Status == AsyncOperationStatus.Succeeded) { GetComponent<Renderer>().material.mainTexture = operation.Result; Debug.Log("Resource loaded via Addressables."); } else { Debug.LogError("Failed to load resource using Addressables."); } } ``` 这种方法不仅减少了开发者自行管理线程的需求,还提供了更高的稳定性和兼容性[^3]。 --- #### 注意事项 - **避免捕获 SynchronizationContext**:在设计异步任务时应特别小心,防止意外引入不必要的上下文切换开销[^2]。 - **内存管理**:确保及时释放不再使用的资源,尤其是大尺寸图像或音频文件[^4]。 - **错误处理**:始终为可能发生的异常准备妥善的恢复策略,以免中断整个应用程序运行。 --- ### 结论 综上所述,在 Unity 中实施异步资源加载需综合考虑项目需求和技术约束条件。无论是采用传统线程还是现代化工具链,均需遵循良好的编码习惯以及平台特性指导原则。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值