Unity AssetBundle批量打包、加载(场景、Prefab)完整流程

news2024/11/15 5:45:29

目录

1、文章介绍

2、具体思路和写法

        (1)AB包的打包

        (2)AB包的加载

        (3)AB包卸载

3、结语

1、文章介绍

本篇博客主要起记录和学习作用,简单的介绍一下AB包批量的打包和加载AB包的方式,若各位同学有幸看到本篇博客,希望能够对你有所帮助。

2、具体的思路和写法

(1)AB包的打包

先介绍一下打ab包所要用到的api  BuildPipeline.BuildAssetBundle(string outputPath,AssetBundleBuild[] builds,BuildAssetBundleOptions assetBundleOptions,BuildTarget targetPlatform)

参数1:ab包输出路径  参数2:ab包信息(主要是assetBundleName=ab包名字,assetNames=资源名)   注意:assetNames一定是要Assets下的路径,不要使用windows路径,不然打包会报错!!!

    /// <summary>
    ///   <para>Build AssetBundles from a building map.</para>
    /// </summary>
    /// <param name="outputPath">Output path for the AssetBundles.</param>
    /// <param name="builds">AssetBundle building map.</param>
    /// <param name="assetBundleOptions">AssetBundle building options.</param>
    /// <param name="targetPlatform">Target build platform.</param>
    /// <returns>
    ///   <para>The manifest listing all AssetBundles included in this build.</para>
    /// </returns>
    public static AssetBundleManifest BuildAssetBundles(
      string outputPath,
      AssetBundleBuild[] builds,
      BuildAssetBundleOptions assetBundleOptions,
      BuildTarget targetPlatform)
    {
      BuildTargetGroup buildTargetGroup = BuildPipeline.GetBuildTargetGroup(targetPlatform);
      return BuildPipeline.BuildAssetBundles(outputPath, builds, assetBundleOptions, buildTargetGroup, targetPlatform);
    }

打包之前的准备工作:

        一般ToB的小项目会有一些资源迭代的需求,所以场景资源单独放到文件夹中管理,每次有新的迭代时,只对最新版本中的场景资源进行增量打包。

        UI资源同样的道理,但是小项目UI资源不需要分版本管理,除非是企业级的项目需要热更或者需要版本管理,则分版本管理。

 

下面是具体代码:

        打包的时候需要注意的事项,打场景包一定不能压缩,否则会加载不出来要使用BuildAssetBundleOptions.None。打其他资源的时候可以选择LZ4压缩BuildAssetBundleOptions.ChunkBasedCompression。LZ4压缩是LZMA和不压缩之间的折中方案,构建的 AssetBundle 资源文件会略大于 LZMA 压缩,但是在加载资源时不需要将所有的资源都加载下来,所以速度会比 LZMA 快。建议项目中使用它。

using System;
using System.Collections.Generic;
using System.IO;
using NWH.Common.AssetInfo;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;

namespace editor.AssetBundle
{
    public class BuildAssetBundle : Editor
    {
        /// <summary>
        /// 场景资源路径
        /// </summary>
        private static string _scenePath = $"{Application.dataPath}/AssetBundle/";

        /// <summary>
        /// UI资源路径
        /// </summary>
        private static string _uiPath = $"{Application.dataPath}/AssetBundle/Resources/";

        /// <summary>
        /// 最终场景包输出目录
        /// </summary>
        public static string SceneOutPutPath = $"{Application.persistentDataPath}/assetbundle_orgin";

        /// <summary>
        /// 最终prefab包输出目录
        /// </summary>
        public static string UiOutputPath = $"{Application.persistentDataPath}/assetbundle_uiorgin";

        [MenuItem("UnityTools/打包资源")]
        public static void BuildAssetsBundle()
        {
            BuildAllScenes();
            BuildAllPrefabs();
            //刷新文件
            AssetDatabase.Refresh();
        }
        
        private static void BuildAllScenes()
        {
            var directorys = Directory.GetDirectories(_scenePath, "V*");
            var folder = directorys[directorys.Length - 1];
            
            //获取指定文件夹下所有的.unity文件
            var sceneFiles = Directory.GetFiles(folder + $"/Scenes/", $"*.unity",
                SearchOption.AllDirectories);

            for (int i = 0; i < sceneFiles.Length; i++)
            {
                //打包进度
                EditorUtility.DisplayProgressBar($"正在打包场景中...", sceneFiles[i], 1.0f);

                if (!Directory.Exists(SceneOutPutPath))
                {
                    Directory.CreateDirectory(SceneOutPutPath);
                }

                //批量打包所有的.unity文件  设置输出路径和输出windows平台
                AssetBundleBuild buildPacket = new AssetBundleBuild();
                buildPacket.assetBundleName = $"{Path.GetFileNameWithoutExtension(sceneFiles[i]).ToLower()}.unity3d";
                buildPacket.assetNames = new string[] { sceneFiles[i].Substring(sceneFiles[i].IndexOf("Assets/")) };

                var abManifest = BuildPipeline.BuildAssetBundles(
                    SceneOutPutPath,
                    new AssetBundleBuild[]{buildPacket},
                    BuildAssetBundleOptions.None,
                    BuildTarget.StandaloneWindows64
                );
            }
        
            EditorUtility.ClearProgressBar();
        }

        private static void BuildAllPrefabs()
        {
            //获取指定文件夹下所有的.prefab文件
            var uiFiles = Directory.GetFiles(_uiPath, $"*.prefab",
                SearchOption.AllDirectories);

            if (!Directory.Exists(UiOutputPath))
            {
                Directory.CreateDirectory(UiOutputPath);
            }

            List<AssetBundleBuild> buildInfoList = new List<AssetBundleBuild>();

            for (int i = 0; i < uiFiles.Length; i++)
            {
                //打包进度
                EditorUtility.DisplayProgressBar($"正在打包预设中...", uiFiles[i], 1.0f);
                
                AssetBundleBuild buildInfo = new AssetBundleBuild();
                buildInfo.assetBundleName = $"{Path.GetFileNameWithoutExtension(uiFiles[i]).ToLower()}.unity3d";
                buildInfo.assetNames = new string[] { uiFiles[i].Substring(uiFiles[i].IndexOf("Assets/")) };
                buildInfoList.Add(buildInfo);
                
                AssetBundleManifest buildManifest = BuildPipeline.BuildAssetBundles(
                    UiOutputPath, 
                    buildInfoList.ToArray(), 
                    BuildAssetBundleOptions.ChunkBasedCompression, 
                    BuildTarget.StandaloneWindows64
                    );
            }

            EditorUtility.ClearProgressBar();
        }
    }
}

 打包完成后输出的文件:

(2)AB包的加载

        加载场景和UI资源我使用的是同步加载,需要用到异步加载或者网络加载的同学可以去看看其他的文章介绍。      

AssetBundle.LoadFromFile 从磁盘上的文件同步加载 AssetBundle。该函数支持任意压缩类型的捆绑包。 如果是 lzma 压缩,则将数据解压缩到内存。可以从磁盘直接读取未压缩和使用块压缩的捆绑包。

与 LoadFromFileAsync 相比,该版本是同步的,将等待 AssetBundle 对象创建完毕才返回。

这是加载 AssetBundle 的最快方法。

using System.Collections;
using System.Collections.Generic;
using UnityEditor.VersionControl;
using UnityEngine;
using utils;

public class ABMgr : IMgr<ABMgr>
{
    /// <summary>
    /// 包路径
    /// </summary>
    private string packagePath;
    
    /// <summary>
    /// ab包缓存
    /// </summary>
    private Dictionary<string, AssetBundle> abCache;

    /// <summary>
    /// 主包
    /// </summary>
    private AssetBundle mainAB = null;

    /// <summary>
    /// 主包中的配置文件---->用来获取依赖包
    /// </summary>
    private AssetBundleManifest manifest = null;

    protected override void Init()
    {
        base.Init();

        abCache = new Dictionary<string, AssetBundle>();
        packagePath = $"{Application.persistentDataPath}/assetbundle_uiorgin/";
    }

    private AssetBundle LoadABPackage(string abName)
    {
        AssetBundle ab;

        if (mainAB == null)
        {
            mainAB = AssetBundle.LoadFromFile(packagePath + "assetbundle_uiorgin");
            manifest = mainAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
        }

        var dependencies = manifest.GetAllDependencies(abName);

        for (int i = 0; i < dependencies.Length; i++)
        {
            if (!abCache.ContainsKey(dependencies[i]))
            {
                ab = AssetBundle.LoadFromFile(packagePath + dependencies[i]);
                
                abCache.Add(dependencies[i], ab);
            }
        }

        if (abCache.ContainsKey(abName)) return abCache[abName];
        else
        {
            ab = AssetBundle.LoadFromFile(packagePath + abName);
            abCache.Add(abName, ab);
            return ab;
        }
    }

    public T LoadResources<T>(string abName, string resName) where T : Object
    {
        AssetBundle ab = LoadABPackage(abName);

        return ab.LoadAsset<T>(resName);
    }
}

 ab包加载单例基类:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace utils
{
    public class IMgr<T> : MonoBehaviour where T: IMgr<T>
    {
        private static T instance;

        public static T Instance
        {
            get
            {
                if (instance != null) return instance;

                instance = FindObjectOfType<T>();

                //防止脚本还未挂到物体上,找不到的异常情况,自行创建空物体挂上去
                if (instance == null)
                {
                    new GameObject("IMgrTo" +typeof(T)).AddComponent<T>();
                }
                else instance.Init(); //保证Init只执行一次

                return instance;
            }
        }

        private void Awake()
        {
            instance = this as T;
            
            Init();
        }

        protected virtual void Init()
        {
            
        }
    }
}

 下面随便写一个例子看看加载出来的效果:

 

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            OnLoadScene();
        }

        if (Input.GetKeyDown(KeyCode.A))
        {
            var go = GameObject.Find("Canvas")?.gameObject;
            //加载资源
            var testui = Instantiate(ABMgr.Instance.LoadResources<GameObject>("testui.unity3d", "testui"));
            if (testui != null && go != null)
            {
                testui.transform.SetParent(go.transform);
                testui.transform.localPosition = Vector3.zero;
                testui.transform.localScale = Vector3.one;
            }
        }
    }

    private void OnLoadScene()
    {
        var ab = AssetBundle.LoadFromFile($"{Application.persistentDataPath}/assetbundle_orgin/scene1.unity3d");

        Debug.LogError(ab.name);
        SceneManager.LoadScene("scene1", LoadSceneMode.Additive);
    }
(3)AB包的卸载

        在AssetBundle的资源使用完成后,需要对其进行卸载,以释放其占用的内存空间。AssetBundle的卸载主要靠AssetBundle.Unload这个API实现。该方法需要传入一个bool类型的参数,如果传入的是true,则会卸载AssetBundle本身及从AssetBundle加载的全部资源。如果传入的是false,则会保留已经加载的资源。
在大多数情况下都推荐使用AssetBundle.Unload(true),因为如果传入false会
造成内存资源的浪费。

如果不得不使用AssetBundle.Unload(false),则只能用以下两种方式卸载单个对象:

在场景和代码中消除对不需要的对象的所有引用。完成此操作后,调用Resources.UnloadUnusedAssets。
以非附加方式加载场景。这样会销毁当前场景中的所有对象并自动调用Resources.UnloadUnusedAssets。

// 1.解除引用后调用
Resources.UnloadUnusedAssets();
// 2.上文提到的,卸载ab所加载的所有asset
ab.Unload(true);

3、结语

        这篇文章到这里就结束了,主要是记录一下自己在项目中使用到的对场景和UI打AB包用法,后续还会进行更深入的研究资源加密、解密、分类管理等等。希望这篇文章对你有帮助,喜欢的朋友点个赞吧。谢谢。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1164223.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

项目实战:编辑页面加载库存信息

1、前端编辑页面加载水果库存信息逻辑edit.js let queryString window.location.search.substring(1) if(queryString){var fid queryString.split("")[1]window.onloadfunction(){loadFruit(fid)}loadFruit function(fid){axios({method:get,url:edit,params:{fi…

【IIS搭建网站】在本地电脑上搭建web服务器并实现外网访问

文章目录 1.前言2.Windows网页设置2.1 Windows IIS功能设置2.2 IIS网页访问测试 3. Cpolar内网穿透3.1 下载安装Cpolar内网穿透3.2 Cpolar云端设置3.3 Cpolar本地设置 4.公网访问测试5.结语 1.前言 在网上各种教程和介绍中&#xff0c;搭建网页都会借助各种软件的帮助&#xf…

【APP】go-musicfox - 一款网易云音乐命令行客户端, 文件很小Mac版本只有16.5M

go-musicfox 是用 Go 写的又一款网易云音乐命令行客户端&#xff0c;支持各种音质级别、UnblockNeteaseMusic、Last.fm、MPRIS 和 macOS 交互响应&#xff08;睡眠暂停、蓝牙耳机连接断开响应和菜单栏控制等&#xff09;等功能特性。 预览 启动 启动界面 主界面 主界面 通…

【docker】安装 showdoc

1. 下载镜像 2.新建存放showdoc数据的目录 3.启动showdoc容器 4.打开网页 1. 下载镜像 # 原版官方镜像安装命令(中国大陆用户不建议直接使用原版镜像&#xff0c;可以用后面的加速镜像) docker pull star7th/showdoc # 中国大陆镜像安装命令&#xff08;安装后记得执行docke…

数组反转(LeetCode)

凑数 ... 描述 : 给你一个 32 位的有符号整数 x &#xff0c;返回将 x 中的数字部分反转后的结果。 如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1] &#xff0c;就返回 0。 假设环境不允许存储 64 位整数&#xff08;有符号或无符号&#xff09;。 题目…

Dubbo中的负载均衡算法之一致性哈希算法

Dubbo中的负载均衡算法之一致性哈希算法 哈希算法 假设这样一个场景&#xff0c;我们申请一台服务器来缓存100万的数据&#xff0c;这个时候是单机环境&#xff0c;所有的请求都命中到这台服务器。后来业务量上涨&#xff0c;我们的数据也从100万上升到了300万&#xff0c;原…

VS使用小技巧——如何让别人看不到你写的代码,却能够运行你的代码

VS使用小技巧 前言方法使用静态库的示例如何创建静态库如何导入静态库Xcode里导入静态库VS2022导入静态库 前言 在实际生活中&#xff0c;作为程序员偶尔会因为资金不够用了选择去兼职写代码&#xff0c;当我们写完一个代码&#xff0c;将他发给某个公司的时候&#xff0c;我们…

C++——定义一个 Box(盒子)类,在该类定义中包括数据成员和成员函数

完整代码&#xff1a; /*定义一个 Box(盒子)类&#xff0c;在该类定义中包括数据成员和成员函数。 数据成员&#xff1a;length &#xff08;长&#xff09;、width&#xff08;宽&#xff09;和 height&#xff08;高&#xff09;&#xff1b; 成员函数&#xff1a;构造函数 …

阿里云双十一大促:云服务器1年99元,新老同享,续费同价!

2023年双十一购物狂欢节来临&#xff0c;阿里云推出了金秋云创季活动&#xff0c;活动力度很大&#xff0c;不再是老用户与狗不得入内&#xff0c;2核2G3M云服务器1年99元&#xff0c;新老同享&#xff0c;续费同价&#xff01; 活动地址&#xff1a;传送门>>> 活动详…

【C++】C++11【下】lambda表达式|thread线程库

目录 1、lambda表达式 1.1 lambda表达式的引入 1.2 lambda表达式的语法 1.3 lambda表达式的原理 2、线程库 2.1thread类的介绍 2.2 线程函数参数 2.3 原子性操作库(atomic) 2.4 使用场景 应用场景1&#xff1a; 应用场景2: 应用场景3&#xff1a; 应用场景4&#xf…

SAFe大规模敏捷框架

Leangoo领歌是一款永久免费的专业的敏捷开发管理工具&#xff0c;提供端到端敏捷研发管理解决方案&#xff0c;涵盖敏捷需求管理、任务协同、进展跟踪、统计度量等。 
 Leangoo领歌上手快、实施成本低&#xff0c;可帮助企业快速落地敏捷&#xff0c;提质增效、缩短周期、加速…

与AI对话的艺术:如何优化Prompt以获得更好的响应反馈

前言 在当今数字化时代&#xff0c;人工智能系统已经成为我们生活的一部分。我们可以在智能助手、聊天机器人、搜索引擎等各种场合与AI进行对话。然而&#xff0c;要获得有益的回应&#xff0c;我们需要学会与AI进行有效的沟通&#xff0c;这就涉及到如何编写好的Prompt。 与…

【Linux学习笔记】进程概念(下)

进程地址空间1. 虚拟地址2. 什么是进程地址空间3. 进程地址空间的映射。4. 地址空间存在的意义5. 写时拷贝 进程地址空间 1. 虚拟地址 来看这样一段代码。 #include <stdio.h> #include <unistd.h>int global_value 100;int main() {pid_t id fork();if(id &l…

3206. 拼图

给出一个 nm 的方格图&#xff0c;现在要用如下 L 型的积木拼到这个图中&#xff0c;使得方格图正好被拼满&#xff0c;请问总共有多少种拼法。 其中&#xff0c;方格图的每一个方格正好能放积木中的一块。 积木可以任意旋转。 输入格式 输入的第一行包含两个整数 n,m&#xff…

scrapy+selenium框架模拟登录

目录 一、cookie和session实现登录原理 二、模拟登录方法-Requests模块Cookie实现登录 三、cookiesession实现登录并获取数据 四、selenium使用基本代码 五、scrapyselenium实现登录 一、cookie和session实现登录原理 cookie:1.网站持久保存在浏览器中的数据2.可以是长期…

3D视觉引导工业机器人上下料,助力汽车制造业实现智能化生产

在工业制造领域&#xff0c;机器人技术一直是推动生产效率和质量提升的重要力量。近年来&#xff0c;随着3D视觉技术的快速发展&#xff0c;工业机器人在处理复杂任务方面迈出了重要的一步。特别是在汽车制造行业&#xff0c;3D视觉引导工业机器人的应用已经取得了令人瞩目的成…

dockefile

文章目录 应用的部署MySql的部署Tomcat的部署 dockerfileDocker原理镜像的制作容器转镜像Dockerfile 服务编排Docker Compose Docker 私有仓库 应用的部署 搜索app的镜像拉去app的镜像创建容器操作容器中的app MySql的部署 容器内的网络服务和外部机器无法直接通信外部机器和…

软件测试 —— 移动端测试

1. 移动端 指移动设备&#xff08;如智能手机、平板电脑、智能手表等&#xff09;上的操作系统和应用程序。移动设备具有便携性和多功能性&#xff0c;可以随时随地连接互联网&#xff0c;提供丰富的应用和服务。 2. 移动端应用分类 (1) 原生应用&#xff08;Native App&…

访问单通道Mat中的值之at()、ptr()、iscontinuous()【C++的OpenCV 第十四课-OpenCV基础强化(二)】

&#x1f389;&#x1f389;&#x1f389; 欢迎各位来到小白 p i a o 的学习空间&#xff01; \color{red}{欢迎各位来到小白piao的学习空间&#xff01;} 欢迎各位来到小白piao的学习空间&#xff01;&#x1f389;&#x1f389;&#x1f389; 目录 一、访问的方法 \color{blu…

voronoi diagram(泰森多边形) 应用 - Empire Strikes Back

欢迎关注更多精彩 关注我&#xff0c;学习常用算法与数据结构&#xff0c;一题多解&#xff0c;降维打击。 voronoi 图求解点击前往 题目链接&#xff1a;https://vjudge.net/problem/URAL-1520 题目大意 有一个城市&#xff0c;形状是圆形。 城市里有很多化工场。 现在想…