Unity学习之资源管理(Resources、AssetDatabase、AssetBundle、Addressable)
在 Unity 中,资源管理是游戏开发的核心环节之一。以下详细介绍四种常用的资源管理方式:Resources、AssetDatabase、AssetBundle 和 Addressable,并提供相应的代码案例。
1. Resources
Resources 是 Unity 最基础的资源管理方式,需要把资源文件放置在项目的Assets/Resources
目录下。若该目录不存在,可手动创建, 这个文件夹下的资源将被加密和压缩打包。
Resources工作原理:
(1) 资源定位:Resource API在运行时会遍历Assets目录及其子目录下名为“Resources”的文件夹,然后通过路径字符串进行资源定位(路径字符串是相对于Resources文件夹的相对路径,不包含文件拓展名。
(2) 资源打包:一旦找到匹配的资源文件,Resource API会将资源文件及其元数据打包到一个序列化文件中。这个序列化文件包含了资源的实际数据以及其他描述信息,如资源类型、引用关系等。
(3) 资源加载:当资源被加载时,Resource API会读取序列化文件,将资源的实际数据加载到内存中。
缺点:
1. 所有 Resources 文件夹中的资源会被打包到游戏主包中, 这一限制,意味着Resource API并不支持打空包。
2. 如果是实战项目,建议将资源打成AssetBundle
放在StreamingAssets
目录或服务器中,方便热更资源。
Resource同步加载资源:
使用Resources.Load
方法能够同步加载资源。该方法会阻塞主线程,直至资源加载完成。
using UnityEngine;public class SyncResourceLoader : MonoBehaviour
{void Start(){// 加载位于Resources目录下的预制体,"PrefabName"为预制体文件名(不含扩展名)GameObject prefab = Resources.Load<GameObject>("PrefabName");if (prefab != null){// 实例化预制体Instantiate(prefab);}}
}
Resource异步加载资源
使用Resources.LoadAsync
方法可以异步加载资源,此方法不会阻塞主线程,在资源加载过程中,游戏可以继续运行。
using UnityEngine;
using System.Collections;public class AsyncResourceLoader : MonoBehaviour
{IEnumerator Start(){// 开始异步加载位于Resources目录下的预制体ResourceRequest request = Resources.LoadAsync<GameObject>("PrefabName");// 等待资源加载完成yield return request;if (request.asset != null){GameObject prefab = request.asset as GameObject;// 实例化预制体Instantiate(prefab);}}
}
Resource卸载资源
当资源不再使用时,为避免内存泄漏,需要及时卸载资源。可以使用Resources.UnloadUnusedAssets
方法卸载所有未使用的资源:
// 卸载所有未使用的资源
Resources.UnloadUnusedAssets();
2. AssetDatabase
AssetDatabase是UnityEditor资源管理类,它提供了在Unity的Assets目录及其子目录下查找、加载已有的资源,还允许用户创建、删除、修改资源。
与Resource相比优势:
(1) 灵活性和组织性:AssetDatabase提供了更灵活和组织性更强的资源管理方式。通过AssetDatabase,您可以在项目中自由地组织和管理资源文件的文件夹结构,以便更好地组织和定位资源。而Resource文件夹只提供了一个默认的资源文件夹(Assets/Resorces文件夹),资源文件的组织和管理相对较为有限。
(2) 运行时加载:使用AssetDatabase允许用户在运行时动态加载和卸载资源。用户可以根据需要按需加载资源,减少内存占用和加载时间。而使用Resource方式加载资源,资源文件需要事先打包到应用程序中,无法动态加载和卸载。
(3) 打空包:这是放弃使用Resource转而使用AssetDatabase最重要的原因。
注意点:
(1) AssetDatabase仅在Unity编辑器模式下使用,当构建游戏时请使用其他资源管理方案!!!
(2) 管理资源方式,将资源组织到自己建立目录下比如下图GameRes目录,判断如果在编辑器模式下,通过AssetDatabase.LoadAsserAtPath加载资源,如果打包移动端这个方案不可以了,必须先打包assetbundle,两种方式,如果需要放在远端,放到指定远程资源服务器上。放在本地将打包好的assetbundle移到StreamingAssets文件夹,通过AssetBundle.LoadFromFile加载资源。
AssetDataBase导入资源:
using UnityEngine;
using UnityEditor;
//Unity用脚本导入资源(没什么卵用)
public class ImportAsset
{[MenuItem("AssetDatabase/ImportExample")]static void ImportExample(){AssetDatabase.ImportAsset("Assets/UI/cube.png", ImportAssetOptions.Default);}
}
AssetDataBase加载资源:
using UnityEditor;
using UnityEngine;public class AssetDatabaseExample : EditorWindow
{[MenuItem("Window/AssetDatabase Example")]static void ShowWindow(){GetWindow<AssetDatabaseExample>("AssetDatabase Example");}void OnGUI(){if (GUILayout.Button("Load Asset")){// 加载资源(需要完整路径)Texture2D texture = AssetDatabase.LoadAssetAtPath<Texture2D>("Assets/Textures/Example.png");if (texture != null){Debug.Log("Loaded texture: " + texture.name);}}if (GUILayout.Button("Find Assets")){// 查找指定类型的所有资源string[] guids = AssetDatabase.FindAssets("t:AudioClip");foreach (string guid in guids){string path = AssetDatabase.GUIDToAssetPath(guid);Debug.Log("Found audio clip: " + path);}}if (GUILayout.Button("Create Material")){// 创建新资源Material material = new Material(Shader.Find("Standard"));AssetDatabase.CreateAsset(material, "Assets/Materials/NewMaterial.mat");AssetDatabase.SaveAssets();}}
}
AssetDataBase创建资源:
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor; //在手机上上没有,需要使用条件编译
#endifpublic class CreatAsset : MonoBehaviour {[MenuItem("AssetDatabase/CreatAssetExample")]static void CreatExample(){string str;
#if UNITY_EDITOR//创建Material material = new Material(Shader.Find("Specular"));AssetDatabase.CreateAsset(material, "Assets/Materials/myMaterials.mat");if(AssetDatabase.Contains(material))//如果存在print(material.name);//创建一个文件夹str = AssetDatabase.CreateFolder("Assets", "NewFolder");if (AssetDatabase.GUIDToAssetPath(str) != "")print("Folder Asset Created !");elseprint(str);#endif}
}
3. AssetBundle
在Unity开发中,AssetBundle是重要的资源管理工具,它允许开发者将游戏资源如模型、纹理、音频等打包成小型的、可被动态加载的压缩包。通过AssetBundle,开发者能够有效地管理大型游戏项目中的资源,实现按需加载,优化内存使用,并支持远程更新内容而不需重新发布整个应用程序。
特点:
(1) 资源分发 :允许将游戏内容划分成小块,按需下载,从而减少初始下载大小。
(2) 动态加载 :使得游戏可以在运行时动态加载和卸载资源,提升内存使用效率。
(3) 热更新 :支持远程更新AssetBundle来修复bug或添加新内容,无需提交新的应用程序版本。
打包 AssetBundle(编辑器脚本):
using UnityEngine;
using UnityEditor;
using System.IO;public class BuildAssetBundle : ScriptableObject
{public static void BuildAssetBundles(){// 创建构建目标对象BuildTarget target = BuildTarget.StandaloneWindows;// 设置构建目录string assetBundleDirectory = Path.Combine("Builds", target.ToString());// 如果目录不存在,则创建该目录if (!Directory.Exists(assetBundleDirectory))Directory.CreateDirectory(assetBundleDirectory);// 设置AssetBundle构建参数AssetBundleBuild[] assetBundleBuilds = new AssetBundleBuild[]{new AssetBundleBuild{assetBundleName = "mybundle",assetNames = new[] { "Assets/Models/MyModel.prefab" }}};// 执行构建BuildPipeline.BuildAssetBundles(assetBundleDirectory, assetBundleBuilds, BuildAssetBundleOptions.None, target);}
}
加载 AssetBundle(运行时):
using UnityEngine;
using System.Collections;public class AssetBundleLoader : MonoBehaviour
{IEnumerator LoadFromFileExample(){string bundlePath = "Assets/AssetBundles/mybundle";// 从本地文件加载AssetBundleCreateRequest request = AssetBundle.LoadFromFileAsync(bundlePath);yield return request;AssetBundle bundle = request.assetBundle;if (bundle == null){Debug.LogError("Failed to load AssetBundle!");yield break;}// 加载包中的资源AssetBundleRequest assetRequest = bundle.LoadAssetAsync<GameObject>("MyPrefab");yield return assetRequest;GameObject prefab = assetRequest.asset as GameObject;Instantiate(prefab);// 卸载AssetBundle,但保留已加载的资源bundle.Unload(false);}IEnumerator LoadFromWebExample(){string url = "http://example.com/assetbundles/mybundle";// 从网络加载using (WWW www = new WWW(url)){yield return www;AssetBundle bundle = www.assetBundle;if (bundle != null){// 加载并实例化资源GameObject prefab = bundle.LoadAsset<GameObject>("MyPrefab");Instantiate(prefab);bundle.Unload(false);}}}void Start(){StartCoroutine(LoadFromFileExample());// 或 StartCoroutine(LoadFromWebExample());}
}
AssetBundle配置
在Unity编辑器的Inspector面板中找到AssetBundle的设置,可以为每一个想要打包的资源或预制体指定一个或多个AssetBundle名称。选择资源后,在面板的底部部分会出现一个”AssetBundle”的标签,其中包含”Variant”的字段,可以用于定义变体,这对于管理不同平台或语言的资源非常有用。
代码块示例:
// 用于设置AssetBundle名称的示例代码
AssetImporter importer = AssetImporter.GetAtPath("Assets/Models/MyModel.prefab");
importer.SetAssetBundleName("mybundle");
打包移动端StreamingAssets文件夹AssetBundle加载方式:
方法一:AssetBundle.LoadFromFile同步加载(推荐)
//加载AssetBundle
string abResPath = Path.Combine(Application.streamingAssetsPath, "3dprefabs");
AssetBundle ab = AssetBundle.LoadFromFile(abResPath);
//加载Asset
GameObject prefab = ab.LoadAsset<GameObject>("Cube");
//实例化
GameObject cube = Instantiate<GameObject>(prefab);
方法二:UnityWebRequest异步加载(支持服务器上在资源加载)
IEnumerator LoadAsset()
{//注意:本地目录需要加上"file://"string uri = "file://" + Application.streamingAssetsPath + "/3dprefabs";UnityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(uri, 0);yield return request.SendWebRequest();if (request.isNetworkError){Debug.LogError(request.error);}//获取到ab包AssetBundle ab = DownloadHandlerAssetBundle.GetContent(request);//加载AssetGameObject prefab = ab.LoadAsset<GameObject>("Cube");//实例化GameObject go = Instantiate<GameObject>(prefab);
}
关于资源AssetBundle热更新:
从服务器端下载AssetBundle
到本地目录:Application.persistentDataPath
。
加载资源的时候,优先去Application.persistentDataPath
找看看有没有对应的资源,如果有,已Application.persistentDataPath
中的资源为准,否则,再去Application.streamingAssets
目录中找资源。
//你的资源文件
string resName = System.IO.Path.Combine(Application.persistentDataPath, "你的资源文件名");
if(System.IO.File.Exists(resName))
{//在Application.persistentDataPath目录中加载资源
}
else
{//在Application.streamingAssets目录中加载资源
}
最后 Addressables
Addressable Assets是 Unity 推出的新一代资源管理系统,基于 AssetBundle 但提供了更高层次的抽象,它解决了传统资源加载的痛点,提供了更智能、更灵活的资源管理方案。
核心特点:
(1) 按需加载:只加载需要的资源
(2) 自动内存管理:智能释放不需要的资源
(3) 支持热更新:可以动态更新资源
更详细的介绍推荐看博文:
https://blog.csdn.net/linxinfa/article/details/122390621