在Unity中,有几个特殊的文件夹用于不同的用途:
// 该方法获取到的路径只会在编辑模式下使用,游戏开发过后路径就不存在了
print(Application.dataPath);
// 一般不会获取,只能使用Resources相关API加载,如果非要获取,使用工程路径拼接
print(Application.dataPath+"/Resources");
// 路径获取
print(Application.streamingAssetsPath);
// 路径获取
print(Application.persistentDataPath);
通过代码动态加载Resources文件夹指定路径下的资源,可以避免复杂的拖拽操作。
注意:资源加载后一定要使用!预制体对象加载后一定要实例化!
// 在一个工程当中,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>("");
如果我们加载过大的资源可能会造成程序卡顿。卡顿的原因是从硬盘把数据读取到内存中是需要进行计算的,越大的资源耗时越长,会造成掉帧卡顿的现象。Resources异步加载就是在内部新开一个线程进行资源加载,不会造成主线程卡顿。
注意:异步加载不能马上得到资源,至少要相隔一帧。
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);
}
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;
}
}
事件监听异步加载
协程异步加载
Resources加载一次资源过后该资源就一直放在内存中作为缓存,第二次加载时发现缓存中存在该资源会取出来直接使用,所以多次重复加载不会浪费资源,但会浪费性能,每次加载伴随着查找。
// 卸载指定资源
Resources.UnloadAsset("资源路径");
// 卸载未使用的资源
// 一般在换场景中和GC一起使用
Resources.UnloadUnusedAssets();
GC.Collect();
注意:Resources.UnloadAsset方法不能释放GameObject类型对象,因为他会用于实例化对象,他只能用于不需要实例化的内容,比如图片、音效、文本等。
GC (Garbage Collection) 是垃圾回收的缩写,是.NET运行时环境中的自动内存管理机制。
GC.Collect()
时优点:
缺点:
StringBuilder
代替字符串拼接foreach
循环(在Unity中)List
时预分配容量下面是一个简单的资源管理器实现,提供统一的方法给外部用于资源异步加载:
///
/// 资源管理器类,用于统一管理Unity中Resources文件夹的异步资源加载
/// 使用单例模式确保全局只有一个资源管理器实例
///
public class ResourcesManager
{
private static ResourcesManager instance = new ResourcesManager();
// 公共静态属性,提供对ResourcesManager唯一实例的全局访问点
// 使用Lambda表达式简化属性定义
public static ResourcesManager Instance => instance;
///
/// 泛型方法,用于异步加载Resources文件夹中的指定资源
///
/// 要加载的资源类型,必须是UnityEngine.Object或其派生类
/// 资源在Resources文件夹中的路径/名称(不包含文件扩展名)
/// 资源加载完成后的回调委托,会将加载到的资源(类型为T)作为参数传入
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);
};
}
}
使用示例:
///
/// 示例类,展示如何使用ResourcesManager进行资源加载
///
public class Example : MonoBehaviour
{
// 用于存储加载的文本资源
private TextAsset textAsset;
void Start()
{
// 调用ResourcesManager单例的LoadRes方法异步加载资源
// 指定了要加载的资源类型为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);
}
});
}
}
这个资源管理器的主要优点是: