
AB包学习内容基础操作。打包、加载包、加载资源、管理器字典缓存已加载重复加载时的处理AssetDatabase和AB包加载资源快速转换根据配表做AB包资源校验、自动设置包名AB包从streaming解压到persistent模拟手机环境使用UnityWebRequest异步生成AB包清单从服务器下载最新AB包清单比对、热更等操作打开游戏时解压和热更的关系这里还要看服务器是放所有资源还是只放更新的。放全量资源则根本不需要本地解压资源。所以这里假设只放更新的资源。没有本地资源对比文件则从streaming解压资源下载对比文件对比远程对比文件不一样且有资源则下载远程的本地资源缺失、远程也没有更新则从本地解压。概述重复加载已加载的AB包会报错但是不会阻止后面的代码执行。获取已加载的AB包可以用AssetBundle.GetAllLoadedAssetBundles()assetBundle AssetBundle.GetAllLoadedAssetBundles() .FirstOrDefault(b b.name mainABName);主包包内的AssetBundleManifest和.manifest文件用ab.LoadAllAssets();加载主包所有资源发现里面有一个AssetBundleManifest。每个AB包随包打出来的还有一个.manifest文本文件。主包内部AssetBundleManifest可以用代码manifestassetBundleMain.LoadAssetAssetBundleManifest(AssetBundleManifest); string[] depsmanifest.GetAllDependencies(ABName);得到包集内任意包依赖的包。主包.manifest里面有这个包集包含的所有包的名字和依赖。ManifestFileVersion: 0 CRC: 3013574967 AssetBundleManifest: AssetBundleInfos: Info_0: Name: role Dependencies: Dependency_0: mat Info_1: Name: mat Dependencies: {}主包内部AssetBundleManifest和主包.manifest包含的信息好像是差不多的一个是二进制一个是文本。具体包的.manifestAssets:记录了手动添加进包的资源没有记录因依赖自动添加的资源。比如人物prefab只有prefab没有模型fbx、动画controller、材质mat。Dependencies记录了自己依赖的包。AssetBundle的编辑器这里用/会先建立文件夹再放AB包。后面的None是AB包后缀。AssetBundle的APIAssetBundle AssetBundle.LoadFromFile(string path)从文件加载AB包。但是安卓平台不能用需要用UnityWebRequest。IEnumerableAssetBundle AssetBundle.GetAllLoadedAssetBundles()返回已加载的AB包。可以as成数组在加载一个AB包前检查有没有已经加载。不能as成列表。Object ab1.LoadAsset(string name)从AB包ab1加载资源返回UnityEngine.Object。T ab1.LoadAssetT(string name)加载资源泛型版。Object ab1.LoadAsset(string name,Type type)加载资源反射版。AssetBundleManifestAB包附带的说明文件记录了自己依赖的AB包。通过AssetBundleManifest manifest; manifestabMain.LoadAssetAssetBundleManifest(AssetBundleManifest);加载。string[] depsmanifest.GetAllDependencies(ABName);从说明文件获得依赖包的名字数组。T ab1.LoadAssetT(string name)能直接传入预制体身上的组件吗不行。必须GameObject。path EditorGUILayout.TextField(path); resName EditorGUILayout.TextField(resName); if (GUILayout.Button(直接加载组件)) { var t MyABManager.LoadResLevelConfigController(path, resName); Instantiate(t); }AB包分包策略打包的有预制体配表每类资源有一个配表配表很多所以还会有一个总配表记录各配表的路径。使用AB包加载时都需要包名资源名2个信息。单个预制体它和同类物品的包名相同不需要单独记可以记在配表里。然后配表和它的预制体打进一个包还是所有配表以及总配表打一个包如前面所说预制体的包名记在配表里如果配表和预制体打一个包那就是配表里记录自己的包名了那加载配表的包名必须从包外部得到就要记录在总配表。所以两种分包策略对应两种记录包名的策略配表和预制体一个包包名要记录在总配表里预制体一个包所有配表一个包预制体包名记录在配表里考虑到总配表里还要记录乱七八糟的信息应该把这个和某类物品强相关的信息放在这类物品的配表。所以分包策略选择所有配表一个包每类物品一个包。如果用SO直接引用预制体打包配表会强制带上所有预制体导致不可能把配表和预制体分开打包。SO的直接引用看起来方便是一种强耦合如果总配表也通过引用引用物品配表整个资源系统都只能打一个包。总之整个资源系统是一棵树从树根——一个硬编码的配表或总包路径能一层层找到所有资源。从开发到build开发中资源频繁变化要用ab包每次资源变化都要重新打包一次。这不现实。所以开发中用AssetDataBase加载。流程开发中AssetDataBase加载开发完build前打AB包运行一个校验程序确定所有资源都能找到build。AssetDataBase和AB包要求的信息不同的问题AssetDataBase需要Assets下的路径有后缀AB包需要包名资源名。所以我们程序里给全所有信息Assets下路径包名要资源名可以从Assets路径中剥离出资源名。资源校验从总配表开始每个路径都要加载一下确保加载到。The AssetBundle XXX cant be loaded because another AssetBundle with the same files is already loaded.重复加载已加载的AB包会报错但是不会阻止后面的代码执行。无需用try包裹。问题打包一些Unity未识别后缀如.lua的文件会报错能打AB包的文件后缀名是有一个范围的不是任意都能。lua脚本只能把后缀改成txt打包。assetBundle.LoadAsset、assetBundle.LoadAssetAsync资源名到底要不要加后缀名AssetBundleBrowser打包的实验是文件名无后缀名、文件名有后缀名、Assets下路径有后缀名都可以加载。手动打包在AssetBundleBuild指定Assets下路径有后缀名加载用文件名无后缀名会失败。AB包里的预制体可以挂脚本吗可以。AB包加载出来的预制体上面的脚本不能编辑也导航不到Asset里的脚本预制体被打进AB包后预制体上的脚本是什么状态预制体被打包进AssetBundle后其附加脚本的状态和行为特征如下脚本序列化状态预制体上的脚本及其序列化字段会完整保留在AB包中包括所有public字段的当前值标记为[SerializeField]的私有字段值脚本间的引用关系需确保被引用资源也在同一AB包或已加载的依赖包中56运行时加载特性脚本关联性通过AssetBundle.LoadAsset加载预制体时Unity会自动绑定原有脚本组件需确保脚本类存在于当前工程若脚本类不存在则显示MissingScript警告但其他组件仍可正常加载跨平台兼容性脚本的跨平台执行需满足脚本代码必须编译进目标平台程序集避免使用平台相关API如UnityEditor命名空间依赖管理要求若脚本引用了其他AB包的资源如材质、贴图需先加载依赖包推荐使用Addressables系统管理复杂依赖关系典型问题处理当出现脚本失效时应检查脚本类名/命名空间是否被修改脚本是否被意外排除在构建之外是否缺少必要的程序集引用总结AB包里的预制体上的脚本不能改是一种序列化类似冻结状态会保留成员字段和类名执行逻辑还是找项目里的脚本因此可以预制体打进AB包后更新脚本让预制体执行新脚本。自定义字典维护已加载AB包的漏洞改一下脚本dll重编译这个字典就销毁了但是内存里的AB包不会销毁。然后就全乱了。在编辑器环境下这经常发生。然后出现The AssetBundle StandaloneWindows cant be loaded because another AssetBundle with the same files is already loaded.如果出现The AssetBundle XXX cant be loaded because another AssetBundle with the same files is already loaded.如何获得AB包AssetBundle.GetAllLoadedAssetBundles().FirstOrDefault(b b.name ABName);assetBundleMainAssetBundle.LoadFromFile(mainABName); if (assetBundleMain null) {//如果报错“已经在内存了” assetBundleMain AssetBundle.GetAllLoadedAssetBundles() .FirstOrDefault(b b.name mainABName); }