【Unity】安卓端加载素材:实现资源从 StreamingAssets 到 PersistentDataPath 的复制与加载

在 Unity 开发中,StreamingAssets 是一个常用的文件夹,用来存放需要打包到应用程序中的静态文件。但在某些情况下(动态文件的更新和修改,需要读写等),为了支持文件动态读取或修改,资源需要从 StreamingAssets 复制到 PersistentDataPath,再进行加载。

比如我现在是需要将一个电脑端的数字人加载项目放到AR眼镜中,单纯从 StreamingAssets中读取资源在眼镜中无法正常显示,但复制到 PersistentDataPath再动态加载就可以成功显示。

本文将详细介绍如何在执行资源加载之前,将 StreamingAssets 的资源复制到 PersistentDataPath 并正确加载这些资源。本文主要内容包括以下几个部分:

  1. Unity 路径简介StreamingAssetsPersistentDataPath 的区别。
  2. 核心实现步骤:如何在调用资源加载逻辑之前完成复制。
  3. 完整代码示例:实际场景下的完整实现代码。

1. Unity 路径简介

StreamingAssets 文件夹

  • 作用:用于存放静态资源文件,资源会被原封不动地打包到应用程序中。
  • 访问方式
    • PC 平台:通过 Application.streamingAssetsPath 获取路径,可直接访问文件。
    • Android 平台:由于资源被打包在 APK 中,无法直接访问,需使用 UnityWebRequest 或其他方式读取。

PersistentDataPath 文件夹

  • 作用:用于存储动态生成的文件或需要持久化的文件(例如保存文件、日志等)。
  • 访问方式:通过 Application.persistentDataPath 获取路径,可直接读取和写入文件。

2. 核心实现步骤

在调用资源加载逻辑之前,首先需要确保资源已被复制到 PersistentDataPath,并将资源路径更新为新路径。以下是核心步骤:

  1. 复制资源:遍历资源路径,将资源从 StreamingAssets 复制到 PersistentDataPath
  2. 更新路径:将资源路径替换为指向 PersistentDataPath 的新路径。
  3. 加载资源:调用加载逻辑时使用更新后的路径。

3. 实现代码

以下是简化的实现代码,用于展示核心逻辑。

主函数

private mainFunction()
{
    
    
    // 获取资源路径
    var basePath = GetBasePath();

    // 定义资源路径
    //PostResult是自定义的类
    var meta = new PostResult
    {
    
    
        upload_zzz_url = $"{
      
      basePath}/zzz.fbx",
        upload_yyy_url = $"{
      
      basePath}/yyy.png",
        upload_xxx_url = new[] {
    
     $"{
      
      basePath}/xxx1.fbx", $"{
      
      basePath}/xxx2.fbx" }
    };

    // 1. 复制资源到 PersistentDataPath
    await CopyAllResourcesToPersistentDataPath(meta);

    // 2. 更新路径为 PersistentDataPath
    UpdateMetaPathsToPersistent(meta);

    // 3. 加载资源
    //实现Load()加载资源的代码
}

步骤 1:复制所有资源到 PersistentDataPath

实现一个方法,将资源路径从 StreamingAssets 复制到 PersistentDataPath。以下为伪代码:

private async UniTask CopyAllResourcesToPersistentDataPath(PostResult meta)
{
    
    
	// 定义一个列表来存储所有路径
	List<string> allPaths = new List<string>
	{
    
    
	    meta.upload_zzz_url,
	    meta.upload_yyy_url
	};
	allPaths.AddRange(meta.upload_xxx_url);
	
    // 遍历所有路径并逐一复制
    foreach (var path in meta.AllPaths)
    {
    
    
        await CopyToPersistentDataPath(path);
    }
}

private async UniTask CopyToPersistentDataPath(string sourcePath)
{
    
    
    // 目标路径
    string persistentPath = GetPersistentPath(sourcePath);

    // 如果文件已存在,跳过
    if (FileExists(persistentPath)) return;

	// 判断平台
	if (Application.platform == RuntimePlatform.Android && !sourcePath.Contains("://"))
	{
    
    
	    sourcePath = $"file://{
      
      sourcePath}"; // Android 平台需要添加前缀
	}
	
    // 使用 UnityWebRequest 从 StreamingAssets 读取文件
	using (UnityWebRequest request = UnityWebRequest.Get(sourcePath))
	{
    
    
	    await request.SendWebRequest();

	    if (request.result == UnityWebRequest.Result.Success)
	    {
    
    
	        // 将文件写入到 PersistentDataPath
	        File.WriteAllBytes(persistentPath, request.downloadHandler.data);
	    }
	    else
	    {
    
    
	        Debug.LogError($"Failed to copy file from {
      
      sourcePath} to {
      
      persistentPath}: {
      
      request.error}");
	    }
	}
}

步骤 2:更新资源路径为 PersistentDataPath

复制完成后,需要更新资源路径为指向 PersistentDataPath

private void UpdateMetaPathsToPersistent(PostResult meta)
{
    
    
    // 更新单个路径
    meta.upload_zzz_url = UpdatePathToPersistent(meta.upload_zzz_url);

    // 更新数组路径
    meta.upload_xxx_url = meta.upload_xxx_url.Select(UpdatePathToPersistent).ToArray();
}

private string UpdatePathToPersistent(string sourcePath)
{
    
    
    string fileName = Path.GetFileName(sourcePath);
    return Path.Combine(Application.persistentDataPath, fileName);
}