当前位置: 首页 > news >正文

Unity基础-Resources资源动态加载

Unity基础-Resources资源动态加载

五、Resources资源动态加载

1. 特殊文件夹

在Unity中,有几个特殊的文件夹用于不同的用途:

  1. 工程路径(Assets)
// 该方法获取到的路径只会在编辑模式下使用,游戏开发过后路径就不存在了
print(Application.dataPath);
  1. Resources资源文件夹
// 一般不会获取,只能使用Resources相关API加载,如果非要获取,使用工程路径拼接
print(Application.dataPath+"/Resources");
  • 需要我们自己创建
  • 作用:资源文件夹
  • 需要通过Resources相关API动态加载的资源需要放在其中
  • 该文件夹下的所有文件都会打包出去
  • 打包时Unity会对其进行压缩加密
  • 该文件夹打包后只读
  1. StreamingAssets流动资源文件夹
// 路径获取
print(Application.streamingAssetsPath);
  • 需要我们自己创建
  • 作用:流动文件夹
  • 打包出去不会压缩加密,任由我们摆布
  • 移动平台只读,PC平台可读可写
  • 可以放入一些需要自定义动态加载的初始资源
  1. persistentDataPath持久数据文件夹
// 路径获取
print(Application.persistentDataPath);
  • 不需要我们自己创建
  • 作用:固定数据文件夹
  • 所有平台可读可写
  • 一般用于放置动态下载或者动态创建的文件,游戏中创建或者获取的文件都放在其中
  • 与游戏的热更新息息相关
  1. Plugins插件文件夹
  • 路径获取:一般不获取
  • 不同平台的插件文件放在其中,例如Android和Ios
  1. Editor编辑器文件夹
  • 路径获取:一般不获取
  • 需要我们自己创建
  • 开发Unity编辑器时,编辑器相关脚本放在该文件夹中
  • 该文件夹内容不会被打包出去
  1. StandardAssests默认资源文件夹
  • 需要我们自己创建
  • 一般Unity自带资源都放在这个文件夹下,代码和资源优先被编译

2. Resources资源动态加载

通过代码动态加载Resources文件夹指定路径下的资源,可以避免复杂的拖拽操作。

2.1 常用资源类型
  • 预设体对象—GameObject
  • 音效文件—AudioClip
  • 文本文件—TextAsset
  • 图片文件—Texture

注意:资源加载后一定要使用!预制体对象加载后一定要实例化!

2.2 资源加载方法
// 在一个工程当中,Resources文件夹可以有多个,通过API加载时,他会自己去这些同名的文件夹中去找资源,打包时所有文件加内容会打包到一起// 1. 预设体对象
GameObject obj1 = Resources.Load("资源路径(如果有嵌套加/)");
Instantiate(obj);// 2. 音效资源
GameObject obj2 = Resources.Load("") as AudioClip;
audioS.clip = obj2;
audioS.Play();// 3. 文本资源
// 文本资源支持的格式: .txt .xml .bytes .json .html .csv
TextAsset ta = Resources.Load("") as TextAsset;
print(ta); // 4. 图片
Texture tex = Resources.Load("") as Texture;private void OnGUI()
{// 在屏幕上绘制一个纹理(图片)// new Rect(0,0,100,100) 定义了纹理的绘制区域,即从屏幕左上角(0,0)开始,宽度为100,高度为100的矩形GUI.DrawTexture(new Rect(0,0,100,100), tex);
}// 5. 加载指定类型的资源
Texture tex = Resources.Load("", typeof(Texture)) as Texture;// 6. 加载指定名字的所有资源
Object[] objs = Resources.LoadAll("");
foreach(Object item in objs)
{if(item is Texture){}else if(item is TextAsset){}
}// 7. 使用泛型加载
// 避免使用 of 关键字
TextAsset ta = Resources.Load<TextAsset>("");

3. Resources资源异步加载

如果我们加载过大的资源可能会造成程序卡顿。卡顿的原因是从硬盘把数据读取到内存中是需要进行计算的,越大的资源耗时越长,会造成掉帧卡顿的现象。Resources异步加载就是在内部新开一个线程进行资源加载,不会造成主线程卡顿。

注意:异步加载不能马上得到资源,至少要相隔一帧。

3.1 异步加载的两种方法
  1. 通过异步加载完成事件监听
private TextAsset textAsset;
void Start()
{// 使用Unity中给的异步加载方法ResourceRequest rq = Resources.LoadAsync<TextAsset>("Text");// 监听加载事件结束rq.completed += Load;// 一定要等加载结束过后才能使用
}// 定义的监听函数要符合事件签名
private void Load(AsyncOperation asyncOperetion)
{// asset是资源对象,加载完毕就能得到textAsset = (asyncOperetion as ResourceRequest).asset as TextAsset;print(textAsset);
}
  1. 通过协程
void Start()
{StartCoroutine(enumaretor());
}IEnumerator enumaretor()
{ResourceRequest rq = Resources.LoadAsync<TextAsset>("Text");// Unity中规定如果返回一个ResourceRequest类型的值,Unity会识别出正在异步加载// 下面这条语句的意思是加载完向后执行yield return rq;textAsset = rq.asset as TextAsset;print(textAsset);// 下面是RecourceRequest中的其他成员变量和属性// 每帧打印异步加载进度while(!rq.isDone){print(rq.priority);yield return null;}
}
3.2 两种方法的区别
  1. 事件监听异步加载

    • 好处:写法简单
    • 坏处:只能在资源加载结束后进行处理,更像是"线性加载"
  2. 协程异步加载

    • 好处:可以在协程中处理复杂逻辑,比如同时加载多个资源,比如进度条更新
    • 坏处:写法麻烦

4. Resources资源卸载

4.1 资源缓存机制

Resources加载一次资源过后该资源就一直放在内存中作为缓存,第二次加载时发现缓存中存在该资源会取出来直接使用,所以多次重复加载不会浪费资源,但会浪费性能,每次加载伴随着查找。

4.2 手动释放资源
// 卸载指定资源
Resources.UnloadAsset("资源路径");// 卸载未使用的资源
// 一般在换场景中和GC一起使用
Resources.UnloadUnusedAssets();
GC.Collect();

注意:Resources.UnloadAsset方法不能释放GameObject类型对象,因为他会用于实例化对象,他只能用于不需要实例化的内容,比如图片、音效、文本等。

4.3 GC(垃圾回收)机制

GC (Garbage Collection) 是垃圾回收的缩写,是.NET运行时环境中的自动内存管理机制。

GC的基本概念
  • GC是自动内存管理机制,用于自动回收不再使用的内存
  • 在C#中,GC是.NET运行时环境的一部分
  • 它负责跟踪和释放不再被程序使用的对象所占用的内存
GC的工作原理
  1. 标记阶段:GC会标记所有仍然被引用的对象
  2. 清除阶段:删除所有未被标记的对象
  3. 压缩阶段:整理内存碎片,使可用内存连续
GC的触发时机
  • 当系统内存不足时
  • 当分配新对象时,如果可用内存不足
  • 当程序显式调用 GC.Collect()
GC的优缺点

优点:

  • 自动管理内存,减少内存泄漏
  • 程序员不需要手动释放内存
  • 简化了内存管理

缺点:

  • 可能造成程序暂停(GC暂停)
  • 内存使用效率可能不如手动管理
  • 无法精确控制内存释放的时机
减少GC压力的最佳实践
  1. 使用对象池重用对象
  2. 避免在频繁调用的方法中创建新对象
  3. 使用值类型(struct)代替引用类型(class)
  4. 缓存频繁使用的对象
  5. 使用 StringBuilder 代替字符串拼接
  6. 避免使用 foreach 循环(在Unity中)
  7. 使用 List<T> 时预分配容量
在Unity中的特殊考虑
  • Unity的GC是分代的
  • 移动平台对GC更敏感
  • 可以使用Unity Profiler监控GC行为
  • 在关键性能点可以手动触发GC

5. 资源管理器实现

下面是一个简单的资源管理器实现,提供统一的方法给外部用于资源异步加载:

/// <summary>
/// 资源管理器类,用于统一管理Unity中Resources文件夹的异步资源加载
/// 使用单例模式确保全局只有一个资源管理器实例
/// </summary>
public class ResourcesManager
{private static ResourcesManager instance = new ResourcesManager();// 公共静态属性,提供对ResourcesManager唯一实例的全局访问点// 使用Lambda表达式简化属性定义public static ResourcesManager Instance => instance;/// <summary>/// 泛型方法,用于异步加载Resources文件夹中的指定资源/// </summary>/// <typeparam name="T">要加载的资源类型,必须是UnityEngine.Object或其派生类</typeparam>/// <param name="name">资源在Resources文件夹中的路径/名称(不包含文件扩展名)</param>/// <param name="unityAction">资源加载完成后的回调委托,会将加载到的资源(类型为T)作为参数传入</param>public void LoadRes<T>(string name, UnityAction<T> unityAction) where T : Object{// 调用Unity内置的Resources.LoadAsync方法,开始异步加载指定路径的资源// ResourceRequest对象是异步加载操作的句柄,可以用来查询进度或等待完成ResourceRequest rq = Resources.LoadAsync(name);// 为ResourceRequest的completed事件添加一个匿名回调函数// 当异步加载操作完成时,这个lambda表达式会被调用rq.completed += (AsyncOperation a) => {// 'a' 参数是AsyncOperation类型,需要将其转换为ResourceRequest// 然后访问其asset属性(类型为UnityEngine.Object),再将其安全地转换为T类型// 最后,调用传入的unityAction回调,并传入加载到的资源unityAction((a as ResourceRequest).asset as T);};}
}

使用示例:

/// <summary>
/// 示例类,展示如何使用ResourcesManager进行资源加载
/// </summary>
public class Example : MonoBehaviour
{// 用于存储加载的文本资源private TextAsset textAsset;void Start(){// 调用ResourcesManager单例的LoadRes方法异步加载资源// <TextAsset> 指定了要加载的资源类型为TextAsset// "Text" 是Resources文件夹中资源的名称(例如:Assets/Resources/Text.txt)// (asset) => { ... } 是一个lambda表达式,作为资源加载完成后的回调函数ResourcesManager.Instance.LoadRes<TextAsset>("Text", (asset) => {// 在资源加载完成后,将加载到的asset(此时是Object类型)安全地转换为TextAsset类型textAsset = asset;// 打印加载到的TextAsset对象,通常会显示其名称和类型print("异步加载完成,加载到的TextAsset是: " + textAsset.name);// 检查textAsset是否成功加载,并打印其内容if (textAsset != null){print("文本内容:\n" + textAsset.text);}});}
}

这个资源管理器的主要优点是:

  1. 使用单例模式,方便全局访问
  2. 支持异步加载,不会阻塞主线程
  3. 使用泛型,可以加载任何类型的Unity资源
  4. 使用Lambda表达式简化了回调处理
  5. 代码简洁且易于使用
http://www.xdnf.cn/news/977059.html

相关文章:

  • 大模型在输尿管上段积脓预测与治疗方案制定中的应用研究
  • 传输层协议TCP(下)
  • AJAX、Axios 与 Fetch:现代前端数据请求技术对比
  • 提升iOS开发效率:通过KeyMob等工具进行全面性能分析与调试
  • 解决windows下pycharm终端conda无法激活虚拟环境问题
  • IntelliJ IDEA代码提示忽略大小写设置详解
  • TRO警报,Kim Haskins维权进行时:卖猫周边或面临TRO冻结?
  • 【群体结构ADMIXTURE之三】监督分群在祖先成分分析中的应用及原理
  • 建站SEO优化之站点地图sitemap
  • 调试`build.sh` 和用 `CMake` 编译出来的 `.elf` / `.bin` / `.hex` 文件大小或行为不同?
  • 重构技术奇点的路径:三智双融认知大飞跃
  • 如何设计一个用于大规模生产任务的人工智能AI系统
  • OpenSSL 无法验证 DevSidecar 的自签名证书
  • 【数据结构】图论最短路圣器:Floyd算法如何用双矩阵征服负权图?
  • Go 协程(Goroutine)入门与基础使用
  • Go 的 fs 包(1/2):现代文件系统抽象
  • 零基础玩转物联网-串口转以太网模块如何快速实现与HTTP服务器通信
  • Solidity从入门到精通-函数及数据存储和作用域
  • 用 IRify 深入探索 WebShell 中的 Source/Sink 挖掘
  • AWS CloudFormation实战:构建可复用的ECS服务部署模板
  • AWS之混合云
  • 2025年渗透测试面试题总结-长亭科技[社招]应急响应工程师(题目+回答)
  • Roboguide工作站机器人重新安装软件包
  • 顶顶通电话机器人功能列表
  • 【前端面试】八、工程化
  • 如何顺利将电话号码转移到新iPhone?
  • 如何将文件从 iPhone 传输到闪存驱动器
  • App UI 设计中色彩搭配如何激发用户的深层情感
  • 算法第13天|继续学习二叉树:平衡二叉树(递归)、二叉树所有路径(递归)、左叶子之和(递归)
  • 基于 WebWorker 的 WebAssembly 图像处理吞吐量分析