文章目录

  • 一、介绍
  • 二、了解AB包是什么
  • 三、了解AB包有什么作用
    • 1. 相对于Resources 下的资源,AB包更好管理资源
    • 2. 减小包体大小
    • 3. 用于热更新
  • 四、生成AB包资源文件
    • 1. 自定义打包工具
    • 2. 官方提供的打包工具:Asset Bundle Browser
    • 3. AssetBundleBrowser参数相关
    • 4. AB包生成的文件
  • 五、使用AB包资源文件
    • 1. 若不熟悉打包有必要了解
    • 2. AB包的路径
    • 3. 使用Unity 提供的API (写代码)
    • 4. 异步加载(协程实现)
    • 5. 卸载AB包
    • 6. 依赖关系
      • AB包的依赖

一、介绍

  1. 入门级别学习AssetBundle包
  2. 文章整体思路参考大神教程:【唐老狮】Unity热更新之AssetBundle
  3. 另外参考官网以及其他大神博客
  4. 本次使用的 Unity 版本是 2020.3.12f1c1 专业版

二、了解AB包是什么

  1. AssetBundle 简称 AB 包
  2. AB包就是特定平台(iOS、安卓)的资源压缩包,类似压缩文件,也可以理解为游戏资源打包的 压缩文件
  3. 可以打成AB包的资源:模型、贴图、预制体、音效、材质球等等

注意:C#代码不能打AB包,因为C#代码在执行前需要一个专门的编译过程,C#是编译语言

三、了解AB包有什么作用

1. 相对于Resources 下的资源,AB包更好管理资源

2. 减小包体大小

  • 压缩资源
  • 减少初始化包大小:客户端初始包大小可以减少很多,把资源AB包放在服务器,启动的时候下载

3. 用于热更新

  1. 资源热更新:美术资源更新,只换资源
  2. 脚本热更新
    总结:AB打包更加灵活,更容易实现热更新

四、生成AB包资源文件

1. 自定义打包工具

有些公司会自己写工具,而且是写好的工具,一般支持自定义功能

2. 官方提供的打包工具:Asset Bundle Browser

  1. 安装工具,Window -> Package Manager

    输入框中搜索 Asset Bundle Browser

    如果搜索不到,因为Unity版本过低,可以去Unity的AssetBundleBrower网站上下载。

  2. 下载好之后,可以在 Window 看到,直接打开

    打开的界面如下

    3. 如何把资源关联到AB包中
    如何把资源关联到AB包中

    注意:C# 代码不能打AB包
    关联之后,打开AssetBundle 窗口后可以看到分为AB列表、AB包详细信息、资源列表、资源详细信息


    若没有显示AssetBundle选项,是因为没有点开最下面的Cube

  • 场景AB包:
  1. 预制体本身是一堆数据,AB打包只是把关联的数据打入AB包内。

3. AssetBundleBrowser参数相关

详细的介绍可以参考官网:Unity Asset Bundle Browser 工具

  1. window -> AssetBundleBrowser -> Build

  2. Compression 打包压缩模式详细

    1. LZMA压缩格式压缩程度很高,打出来的包非常小,但使用起来需要完全解压,速度相对较慢。
    2. LZ4的压缩格式,这种格式压缩程度比较低,打出来的包比LZMA格式打出的包大了一些(但是也比不压缩的包小),使用起来速度比较快。
    3. No Compression不压缩格式,这就是保留原资源大小不进行压缩,使用起来是最快的。

    一般商业上使用LZMA格式和LZ4格式的比较多

    这里选择yes,会自动创建并放到该文件夹,打包成功之后可以刷新Project窗口看到该文件夹

4. AB包生成的文件

  1. 打包后的文件:

    打开Asset\StreamingAssets文件夹:

    打开AssetBundles\StandaloneWindows文件夹:

  2. StandaloneWindows 是主包,存储了包与包之间的关系,非常重要。没有后缀名的文件是资源文件,内部都是二进制,.manifest文件是对应的资源文件相关的配置信息,依赖相关的信息。

  3. Inspect 界面,查看详细信息包的

    可以看到AB包大小

五、使用AB包资源文件

1. 若不熟悉打包有必要了解

第一种资源来源:自动打包资源,Unity 场景中直接使的资源会随着场景被自动打包到游戏中,这些资源会在场景加载的时候由unity自动加载,这些场景只要放置在Unity工程目录的Asset文件夹下,程序不需要关系他们的打包和加载,因为这些资源都是静态加载的。(但实际开发中,我们一般都会动态创建GameObject ,资源是动态加载的,所以静态的资源一般不多)

第二种资源来源: Resources资源,指的是在Unity工程的Asset目录下面可以建一个Resources 文件夹。Resources可从脚本中按需加载资源,而不必在场景中创建资源实例用于游戏,需要加载Resources的资源时,使用 Resources.Load函数即可动态加载这些资源。这个文件夹下放置的所有资源,不论是否被场景用到,都会被打包到游戏中,这是平时开发常用的资源加载方式,但缺点是资源都直接打包到游戏中,不好做增量更新
注意:如果 Resources 文件夹是 Editor 的子文件夹,则其中的资源可通过 Editor 脚本加载,但会从构建中删除。

AssetBundle 资源:可以理解为Unity的压缩包,这个压缩包可以包含针对特定平台的资源,比如说:贴图、模型、prefab、音频甚至整个场景,我们可以把资源打包成多个独立的AB包,这些包和游戏包是分离的,可以用来做增量包,更新内容(一开始游戏没有的玩到的内容做成增量包,玩到了通过网络下载)、还可以做分包发布,减少安装包大小(比如大多页游的游戏资源是随着游戏的过程增量下载的,或者有些手游资源过大,渠道要求发布的客户端包体大小被限制在100M以内,那也可以做成增量包,玩游戏的时候通过网络下载),也可以做成增量更新,可以热更修复游戏bug

所以综上所述,AssetBundle资源更加灵活,可以实现资源的AB包热更,配合Lua语言开发游戏还可以实现热更修复bug、更新玩法等。

—— 参考Unity 打包原理

2. AB包的路径

因为我们可以通过路径加载AB包,所以我们读取的AB包应该是copy出来在 StreamingAssets 文件夹中的AB包,而不是在我们选定的输出路径中的AB包,因为输出路径与Asset文件夹同级,不会被打包。而 StreamingAssets 文件夹会随着Unity3D打包而被打包出去,在PC平台是可读可写的,在安卓和IOS都只是可读的。

3. 使用Unity 提供的API (写代码)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class ABTest : MonoBehaviour
{// Start is called before the first frame updatevoid Start(){// 1. 加载 AB包AssetBundle ab = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "module"); // 从文件中同步加载 :路径 + 包名// 2. 加载 AB包中的资源 建议用泛型写法 或者 Type指定类型加载// 只使用名字加载 会出现 同名不同类型资源 分不清//GameObject obj = ab.LoadAsset<GameObject>("Cube");    // 泛型加载GameObject obj = ab.LoadAsset("Cube", typeof(GameObject)) as GameObject;    // Type指定类型加载Instantiate(obj);}// Update is called once per framevoid Update(){}
}
  1. 把脚本挂到MainCamera上,然后运行Unity,可以看到正方体:
  2. 如果再加载一个物体代码和运行结果:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class ABTest : MonoBehaviour
{// Start is called before the first frame updatevoid Start(){// 1. 加载 AB包AssetBundle ab = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "module"); // 从文件中同步加载 :路径 + 包名// 2. 加载 AB包中的资源 建议用泛型写法 或者 Type指定类型加载// 只使用名字加载 会出现 同名不同类型资源 分不清//GameObject obj = ab.LoadAsset<GameObject>("Cube");    // 泛型加载GameObject cube = ab.LoadAsset("Cube", typeof(GameObject)) as GameObject;    // Type指定类型加载Instantiate(cube);// 加载一个桶GameObject barrel_Sealed_01 = ab.LoadAsset("Barrel_Sealed_01", typeof(GameObject)) as GameObject;    // Type指定类型加载Instantiate(barrel_Sealed_01);}// Update is called once per framevoid Update(){}
}

注意:这里的预制体显示紫色,说明该预制体已经加载出来但是预制体上的资源丢失,这与下面要讲到的依赖有关。
AB包不能重复加载,一个名字的AB包只有一个,重复加载会报错

4. 异步加载(协程实现)

  • 不熟悉协程的同学有必要提前了解 《Unity协程的原理与应用》by宇亓大佬~
  • 另外为了使用Sprite资源自己单独找图集做头像,不熟悉Texture 2D的话可以提前了解Unity Texture 2D 图集切割 输出单个PNG,输出单个PNG后还要把每一个转成Sprite格式
  1. 头像图片生成新的AB包




    build 过程报错:

    解决方法:出现bug的原因是是使用了UnityEditor相关的API,所以把相关的脚本(我这里是ImageSlicer.cs)放到 Assets/Editor/ 目录下即可解决,相关报错都可以用这个操作解决

  2. 使用协程实现异步的代码

    IEnumerator LoadABRes(string ABName, string resName)
    {// 第一步 加载AB包AssetBundleCreateRequest abcr = AssetBundle.LoadFromFileAsync(Application.streamingAssetsPath + "/" + ABName);yield return abcr;// 第二步 加载资源AssetBundleRequest abr = abcr.assetBundle.LoadAssetAsync(resName, typeof(Sprite));yield return abr;// 显示spriteimg.sprite = abr.asset as Sprite;
    }
    

    启动协程方式:

    // 异步加载 -> 协程StartCoroutine(LoadABRes("sprite", "Avatar7")); // 输入包名,资源名
    
  3. Sprite需要显示在Image上,所以需要建一个UI画布给图片显示,在场景里直接添加画布显示

    脚本里可以添加一个Public的Image:public Image img; 直接把场景里面的image拖到img中

  4. 完整的代码

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;public class ABTest : MonoBehaviour
    {public Image img;// Start is called before the first frame updatevoid Start(){// 1. 加载 AB包AssetBundle ab = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "module"); // 从文件中同步加载 :路径 + 包名// 2. 加载 AB包中的资源 建议用泛型写法 或者 Type指定类型加载// 只使用名字加载 会出现 同名不同类型资源 分不清//GameObject obj = ab.LoadAsset<GameObject>("Cube");    // 泛型加载GameObject cube = ab.LoadAsset("Cube", typeof(GameObject)) as GameObject;    // Type指定类型加载Instantiate(cube);// 加载一个球//GameObject sphere = ab.LoadAsset("sphere", typeof(GameObject)) as GameObject;    // Type指定类型加载//Instantiate(sphere);// 异步加载 -> 协程StartCoroutine(LoadABRes("sprite", "Avatar7"));}IEnumerator LoadABRes(string ABName, string resName){// 第一步 加载AB包AssetBundleCreateRequest abcr = AssetBundle.LoadFromFileAsync(Application.streamingAssetsPath + "/" + ABName);yield return abcr;// 第二步 加载资源AssetBundleRequest abr = abcr.assetBundle.LoadAssetAsync(resName, typeof(Sprite));yield return abr;// 显示spriteimg.sprite = abr.asset as Sprite;  }// Update is called once per framevoid Update(){}
    }
  5. 运行结果

5. 卸载AB包

  1. 卸载所有加载的AB包接口

        // Update is called once per frame
    void Update()
    {if (Input.GetKeyDown(KeyCode.Space)){//卸载所有加载的AB包 参数为true 会把通过AB包加载的资源也卸载了AssetBundle.UnloadAllAssetBundles(true);}
    }
    

    运行后卸载:


    图消失了,资源丢失,变成紫色也说明立方体的资源丢失材质球丢失

    若参数改成false:

     // Update is called once per framevoid Update(){if (Input.GetKeyDown(KeyCode.Space)){//卸载所有加载的AB包 参数为true 会把通过AB包加载的资源也卸载了AssetBundle.UnloadAllAssetBundles(false);}}
    

    再次运行卸载后,资源不会消失

  2. 卸载单个AB包

    // 卸载单个AB包, 参数为 true 会卸载该AB包所有加载的资源ab.Unload(true);
    

    若Cube使用Unload函数,则资源也被卸载

    若参数为false,则资源还在,只卸载AB包

    // 卸载单个AB包ab.Unload(false);
    

    运行结果:

6. 依赖关系

  1. 加载有材质球的Cube




    这时打开 AssetBundles 会发现这个材质球默认跟Cube在同一个AB包内:

    若把材质球放到另外的AB包内,比如说materials包,再次bulid:


    然后把上一次的卸载代码注释掉,直接运行:

  2. 依赖知识点:一个资源身上用到了别的AB包中的资源,若加载资源的时候只加载了自己的AB包,通过这个资源创建的对象,会出现资源丢失的情况,这种时候需要把依赖包一起加载才能正常

    所以我们加载Cube的时候,也应该加载materials

    // 加载依赖包AssetBundle abRely = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "materials");
    

    添加这个加载materials的代码后,再次运行就可以看到Cube正常显示。

    上面的这种方法可以解决当前Cube不能正常显示材质球问题,但是如果这个Cube不止依赖材质球,也许还有其他资源,那么这种时候,就像是一个资源用到了数个其他AB包中的资源:

AB包的依赖

一个资源身上用到了别的AB包的资源,如果只加载自己的AB包,通过自己的AB创建对象就回出现资源丢失的情况,这时只需要把依赖包一起加载了就正常显示。

加载AB包:

   AssetBundle ab = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "model");

加载依赖包:

   AssetBundle abRely = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "materials");

如何获取当前这个AB包内的资源依赖了什么AB包?通过主包来获取这一依赖信息!可以打印出来看看依赖包名是啥

   // 加载主包AssetBundle abMain = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "StandaloneWindows");// 加载主包中的固定文件AssetBundleManifest abManifest = abMain.LoadAsset<AssetBundleManifest>("AssetBundleManifest");// 从固定文件中 得到依赖信息 直接填包名string[] strs = abManifest.GetAllDependencies("module");// 得到依赖包的名字for (int i = 0; i < strs.Length; i++){Debug.Log(strs[i]);}

运行打印:

所以需要加载当前AB包依赖的AB包时,把打印换成AB包加载即可:

       // 加载主包AssetBundle abMain = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "StandaloneWindows");// 加载主包中的固定文件AssetBundleManifest abManifest = abMain.LoadAsset<AssetBundleManifest>("AssetBundleManifest");// 从固定文件中 得到依赖信息 直接填包名string[] strs = abManifest.GetAllDependencies("module");// 得到依赖包的名字for (int i = 0; i < strs.Length; i++){// 可以得到所有AB包 应该需要存起来释放掉~  这里主要是为了测试AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + strs[i]);}

运行结果:

以上加载依赖的弊端:

  • 当AB包中存在多个资源时,某些资源只依赖于部分依赖包,那么此时若没有加载当前AB包的所有资源时,会加载了一些“不必要”的依赖包。比如说:
  • 还可以直接打开主包的Manifest文件查看依赖内容:

Unity 3D 热更新之基于 Asset Bundle Browser 的 AssetBundle包相关推荐

  1. [Unity3D学习]Unity代码热更新 源码下载

    转载自:http://blog.gamerisker.com/archives/608.html 之前的一篇文章<[Unity3D学习]Unity代码热更新解决方案测试结果总结>只是说了一 ...

  2. lua 给userdata设置元表_UE4热更新:基于UnLua的Lua编程指南

    本片文章搬运自我自己的博客:原文链接: UE4热更新:基于UnLua的Lua编程指南 作者: ZhaLiPeng UE使用的是C++这种编译型语言,在编译之后就成了二进制,只有通过玩家重新安装才能打到 ...

  3. UE4热更新:基于UnLua的Lua编程指南

    UE4热更新:基于UnLua的Lua编程指南 https://imzlp.me/posts/36659/ https://imzlp.me/posts/36659/ Z's Blog 首页 归档 分类 ...

  4. 【文集】Unity的热更新

    热更新对游戏很重要,但是unity自身是不支持热更新的,因此这方面还是有不少方案和文章 Unity热更新之LuaInterface(上篇) Unity热更新之LuaInterface(下篇) Lua ...

  5. Unity HybridCLR热更新技术实现

    最近我们在项目中遇到了一个问题:经常需要修改游戏逻辑,如果每次修改都需要重新打包发布,那将会非常耗时,于是我们开始寻找解决方案.最后我们找到了 Unity HybridCLR 热更新技术,实现了游戏逻 ...

  6. unity python热更新_Unity热更新介绍和测试方法

    最近项目中增加了热更新了功能,程序也完成了相应的开发,接下来就需要对这个模块进行相应的测试工作了,在测试开始之前,了解下其原理总是很有好处的. 1:什么是热更新 简单的理解就是:用户重启客户端就能实现 ...

  7. uLua,一个Unity+Lua热更新解决方案!

    原文:http://game.ceeger.com/forum/read.php?tid=16483&fid=16 看了坛子上同学用Kopilua,以为真的跨平台没问题,就实验了安卓手机,然后 ...

  8. uLua最新的Unity+Lua热更新解决方案!!!

    看了坛子上同学用Kopilua,以为真的跨平台没问题,就实验了安卓手机,然后就开始铺游戏框架,干了一星期到昨晚想起来到ipad上跑跑,然后我跟我的小Demo一起崩溃了.今天搜索luajit,终于在u3 ...

  9. 【Unity】热更新插件【ULua】学习教程整理

    前言 IOS不能热更新,不是因为不能用反射,是因为 System.Reflection.Assembly.Load 无法使用 System.Reflection.Emit 无法使用 System.Co ...

  10. 用ECMAScript4 ( ActionScript3) 实现Unity的热更新 -- 使用原型链和EventTrigger

    原型链是JS的必备,作为ECMAScript4,原型链也是支持的. 特别说明,ActionScript3是支持完整的面向对象继承支持的,原型链只在某些非常特殊的情况下使用. 本文旨在介绍如何使用原型链 ...

最新文章

  1. JDK+TOMCAT在LINUX下简单的配置
  2. 完整的python项目流程
  3. Alpha 冲刺 (3/10)
  4. 两个操作字符串的方法:读取指定位置的字符和找出某个字符串的位置
  5. STL sort解析
  6. Lintcode9 Fizz Buzz solution 题解
  7. 接收字节流_Java中的IO流之输入流|乐字节
  8. win2008环境mysql主从配置
  9. java 日历类_java常用的类---日历类
  10. installshield使用教程
  11. 使用PS2019制作明信片
  12. Html中怎么用CSS让ul中多个li标签不换行横排显示
  13. HDUOJ 6441 Find Integer
  14. 基础操作案例 :ArcGIS PRO基础教程(一)
  15. 最新爱词霸 Java + mysql (含源码+数据库)
  16. 阿里灵杰融合智能算力,全栈AI服务为探索者铺路
  17. 自己用VB.net开发的小型ERP系统,作为开发ERP系统的 一个参考
  18. 高等代数_证明_任何矩阵都相似于一个上三角矩阵
  19. 压缩算法:字符串(JSON)压缩和解压【JDK之Deflater压缩与Inflater解压】
  20. 北京乘坐出租车被拒载,险些被掳走一人

热门文章

  1. 服务器 t610硬盘开关,戴尔服务器T610
  2. linux磁盘配额分区,Linux磁盘配额
  3. 《通信原理》用matlab实现加性高斯白噪声信道实验
  4. 纤维过滤器和石英砂过滤器的区别
  5. 算法时间复杂度Θ(n2)与 O(n2)
  6. word安全模式解除
  7. 组态王与Modbus协议的地址对应规则
  8. 图像元数据(Metadata) ——Exif信息分析
  9. paypal ipn java_javashop中paypal使用指南
  10. 全球及中国建筑涂料市场盈利能力分析与产值规模预测报告2022版