Unity——资产包(Asset Bundles)

对很多单机游戏来说,游戏的所有资源往往是与游戏本体一同发布的,资源部西药独立出来。但对于大型商业项目来说,游戏产品还需要再发布之后进行维护和更新,这就引出了Unity资产包的概念


一、资产包(Asset Bundles)

其实一般来说,只要把资源放在合适的文件夹下,Unity就会妥善处理。但是,对于大型商业项目来说,资源的打包和管理又是不得不考虑的问题,其主要原因有以下几点:

  1. 资源文件有必要与程序主体解绑。在程序主体不变的情况下可以方便地单独修改资源
  2. 资源文件有必要单独压缩和加密
  3. 资源文件单独存在后,可以不随程序主体一起发布,而是在用户使用时再联网下载。这样有利于减小安装文件统计,也有利于后续的版本更新

Asset Bundles(资产包)就是为了更好地解决资源管理问题而存在的。下面用一个简单例子说明其使用方法。

1.创建资产包

  1. 新建一个工程
  2. 创建一个球和一个方块,再创建一个材质,并把材质赋给球和方块
  3. 导入任意一张图片,新建两个精灵物体Sprite,指定为同样的图片,分别命名为icon1和icon2
  4. 将4个物体分别拖入Project窗口,作为4个预制。这样操作后,就有了4个预制、1个材质和一张图片
  5. 删除场景中的4个物体,稍后会利用资产包来还原它们
  6. 在Assets文件夹下创建一个文件夹,命名为Editor。注意大小写和拼音应完全一致,这样该文件夹才能被识别为编辑器专用文件夹。在该文件夹这种创建脚本BuildAssetBundle。

脚本BuildAssetBundle:

using UnityEditor;
using System.IO;

public class BuildAssetBundle 
{
    //在主菜单中增加选项
    [MenuItem("Asset Bundles/Build AssetBundles")]
    static void BuildAllAssetBundles()
    {
        //要创建的文件夹名称
        string dir = "AssetBundles";
        //如果不存在该文件夹则新建它
        if (Directory.Exists(dir) == false) 
        {
            Directory.CreateDirectory(dir);
        }
       // 该资产包是发布用于Windows平台的资源用的
            BuildPipeline.BuildAssetBundles(dir, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64);
    }
}

这个脚本与一般的脚本组件不同,它是一个编辑器脚本。首先它应当出现在编辑器专用的文件夹中(即Editor中),其次它引用了UnityEdittor。而代码开头括号中的MenyItem特性,会修改Unity编辑器的主菜单。

保存后切换到Unity,正确编译后,在主菜单中会多出一个Asset Bundles选项,如图

然后点Build AssetBundles,就会在Assets文件夹之外创建一个AssetBundles新文件夹。

 但现在还没有设置哪些资源打入哪些包,因此还没有实际效果。接下来设置每个资产所属的资产包。

选中Cube,会在编辑器右下角看到预览窗口,预览窗口下方有两个菜单,这两个菜单斗鱼资产包相关。单击左侧的按钮,选择New为资产包命名,如命为ab。然后确认prefab所属的资产包已经设置为ab资产包。

同样,分别选择另外3个预制,也将它们所属的资产包设置为ab。但这里不需要设置图片和材料的资产包,设不设置都不影响。

有了工具脚本,设置好了资产包,准备工作就完成了。接着再点击主菜单中的AssetBundles->BuildAssetBundles,编辑器就会自动打包。

打包后在资源管理器的工程文件夹中找到AssetBundles文件夹(在Assets文件夹外面),可以看到有两个无扩展名的文件,其中ab就是指定的资产包,而ab.mainfest则是资产的相关信息。注意与META文件类似,两者也需要一起使用。

Unity默认将资产包的根目录放在Assets文件之外,侧面说明资产包已经可以独立存在,不再像普通资源那样需要被引擎管理,而可以将它放在硬盘中的任意位置。

2.加载和使用资产包

之前使用预制时只有两种方案,要么用公开变量直接引用预制,要么用Resources.Load动态加载预制,而现在有了第三种更灵活的方法——从资产包中加载资源。

新建脚本TestLoadAB,内容如下:

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

public class TestLoadAB : MonoBehaviour
{
  
    void Start()
    {
        //从文件中加载到AssetBundles
        AssetBundle ab = AssetBundle.LoadFromFile(E:/unitytest/Asset Bundles/AssetBundles/ab);//注意,这里要是你自己的文件路径
        //分别加载4个加载预制资源
        GameObject prefab1 = ab.LoadAsset<GameObject>("Cube");
        GameObject prefab2 = ab.LoadAsset<GameObject>("Sphere");
        GameObject prefab3 = ab.LoadAsset<GameObject>("icon1");
        GameObject prefab4 = ab.LoadAsset<GameObject>("icon2");
        //以上4句话可以用下面一句话代替
        //GameObject[] prefabs = ab.LoadAllAssets<GameObject>();

        Instantiate(prefab1, new Vector3(0, 0, 0), Quaternion.identity);
        Instantiate(prefab2, new Vector3(1, 0, 0), Quaternion.identity);
        Instantiate(prefab3, new Vector3(2, 0, 0), Quaternion.identity);
        Instantiate(prefab4, new Vector3(3, 0, 0), Quaternion.identity);

    }

   
}

以上代码从文件中加载到资产包ab,注意路径要与实际位置完全一致。

加载到资源包后,需要把资源从包里获取出来,可以使用LoadAsset方法单独获取,也可以用LoadAllAssets方法获取一批资源。

二、资源的常用路径

在上面的的代码中,把路径写成了绝对路径。但在实际游戏开发中不能使用绝对路径,因为每台计算机、每部手机的文件夹结构都可能有所不同,所以必须采用间接的方式指定路径。而且,由于Windows、Mac、Android和IOS等操作系统的文件结构都有所不同,因此应当使用Unity提供的方法来间接指定文件夹。常用的系统路径主要有以下三种。

  1. Application.dataPath,数据路径。它指的是程序的数据所在的路径,如在开发阶段,Assets就是数据文件夹。在移动平台上此路径作用不大。
  2. Application.streamingDataPath,原始数据路径。它对应的就是特殊文件夹StreamingAssets,用于存放不需要压缩处理的数据文件,但在移动端中,StreamingAssets文件夹也是只读文件夹,不能写入数据。
  3. Application.persistentDataPath,持久化数据路径。这是一个可读、可写的路径,所有游戏的档案、下载的资产包都应当放在此路径中

如果游戏需要发布到移动端,而且有需要保存数据,那么常用路径的作用就必须要了解。但是,对于商业项目来说还会遇到更多复杂的问题,例如,安卓系统的可写文件夹分为主存储区SD卡区两部分,苹果系统的持久化数据文件夹会被iCloud自动备份。

三、资产的依赖关系

在上面的例子中,无论是否设置图片和材质文件所属的资产包,都不会影响打包和加载的效果。为了确认这一点,可以删除Assets文件夹中的原始资源文件,已确认是否所有资源都是从资产包中动态获得的。

从表面上看,需要打资产包的资源都应该设置资产包,但实际上,很多没有设置资产包的资源也会进入资产包,这是因为Unity可以识别出资产包依赖哪些资源。例如,立方体和球体需要材质资源,两个精灵prefab需要图片,因此图片和材质会被一起放到资产包中。

如果所有的资产进入同一个资产包,那么每个资产最多只存在一份。但实际上,一个游戏不可能只有一个资产包,很可能有很多个资产包哦,这时候再考虑依赖关系,会出现一个棘手的问题。

例如,在上面的例子中,把4个预制分别打入4个资产包:立方体、球体、精灵1和精灵2。其中立方体和球体都依赖材质文件,精灵1和精灵2都依赖图片文件,而材质和图片不打包,这时Unity会怎么办呢?

答案是,为了保证资产包可以独立使用,必须在每个资产包中加入所有依赖项。也就是说,立方体和球体的资产包都要包含一份材质,两个图片资产包都需要分别包含那张图片。这样的方案解决了资产包独立使用的问题,但又造成了资源重复的新问题。在某些大型项目中,资产之间的依赖关系错综复杂,错误的设置可能造成资源重复打包多次、资源体积翻倍的情况。

解决此问题需要更合理的打包策略,其中解决问题的关键是,把公用的资源也打到单独的资源包中去。例如,在上面的例子中,如果立方体和球体预制体都需要单独打一个包,那么材质也应该单独打一个包。这样操作后,Unity会发现依赖项已经单独打包了,就不会把材质再分别打包到立方体和球体资产包中,也就不会造成资源重复打包的问题。

注意:使用的时候,在实例化球体之前,务必把材质资产包加载好。如果没有加载材质而直接实例化球体,就会出现球体丢失材质的现象(显示为紫红色)。

总之,为了更好地管理资产,就不得不使用资产包,一旦单独管理资产,又会引出很多问题。针对资产管理问题,很多有经验的开发团队已经积累了完整的解决方案可供借鉴,另外Unity官方也在积极改进资产包管理方式

四、从网络加载资源

在上面加载资产包的例子中,用到了AssetBundle.LoadFromFile()方法,它是一种常用的从本地文件加载资产包的方法。如果加载的文件较大,该方法会造成主线程卡顿,因此Unity提供了另一种利用协程的加载方法AssetBundle.LoadFromFileAsync(),用法如下

  IEnumerator LoadFileAsync()
    {
        AssetBundleCreateRequest request = AssetBundle.LoadFromFileAsync(path);
        yield return request;
        AssetBundle ab = request.assetBundle;
        Instantiate(ab.LoadAsset<GameObject>("CubeWall"));
    }

使用异步函数时,应当专门编写一个协程函数,如LoadFromFileAsync()。只需要开启携程的StartCoroutine()方法启动它,在加载完成之前协程函数不会运行到第一个yield return后面,但同时也不会影响程序的运行。

既然能从磁盘加载资产包,自然也可以利用网络加载资产包。推荐使用UnityWebRequest.GetAssetBundle获取网络资源,它也是一个异步函数,使用方法如下

 IEnumerator UseUnityWebRequest(string path)
    {
        //使用UnityWebRequest.GetAssetBundle(路径)加载资源。这个路径时资源地址URL,同时也支持本地文件
        UnityWebRequest request = UnityWebRequest.GetAssetBundle(path);
        yield return request.SenWebRequest();

        //运行到这里表示加载完成,从request对象中获得资产包
        AssetBundle ab = DownloadHandlerAssetBundle.GetContent(request);

        //之后的使用方法与本地加载没有区别
        GameObject obj = ab.LoadAsset<GameObject>("Wall");
        Instantiate(obj);
    }

可以看到,虽然获取资产包的方法和路径格式有所区别,但使用资产包的方法是一样的。网络资源的路径要用URL表示。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux Input子系统

一、基本概念 按键、鼠标、键盘、触摸屏等都属于输入(input)设备&#xff0c;Linux 内核为此专门做了一个叫做 input子系统的框架来处理输入事件。本质属于字符设备。 1. input子系统结构如下&#xff1a; input 子系统分为 input 驱动层、input 核心层、input 事件处理层&…

[Linux]进程

文章目录 1. 进程控制1.1 进程概述1.1.1 并行和并发1.1.2 PCB1.1.4 进程状态1.1.5 进程命令 1.2 进程创建1.2.1 函数1.2.2 fork() 剖析 1.3 父子进程1.3.1 进程执行位置1.3.2 循环创建子进程1.3.3 终端显示问题1.3.4 进程数数 1.4 execl和execlp函数1.4.1 execl()1.4.2 execlp(…

什么是浏览器缓存(browser caching)?如何使用HTTP头来控制缓存?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 浏览器缓存和HTTP头控制缓存⭐ HTTP头控制缓存1. Cache-Control2. Expires3. Last-Modified 和 If-Modified-Since4. ETag 和 If-None-Match ⭐ 缓存策略⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击…

攻防世界-web-fakebook

1. 题目描述 打开链接&#xff0c;这样一个界面 貌似没啥特殊的。。。没关系&#xff0c;我们每个页面都点击一下 login页面&#xff1a;一个简单的登录页面 join界面&#xff1a;不出意外&#xff0c;这应该是一个注册界面 目前&#xff0c;我们什么都不做&#xff0c;能获…

Jmeter性能综合实战 —— 签到及批量签到

提取性能测试的三个方面&#xff1a;核心、高频、基础功能 签 到 请 求 步 骤 1、准备工作&#xff1a; 签到线程组n HTTP请求默认值n HTTP cookie 管理器n 首页访问请求n 登录请求n 查看结果树n 调试取样器l HTTP代理服务器 &#xff08;1&#xff09;创建线程组 &#xf…

多线程使用HashMap,HashMap和HashTable和ConcurrentHashMap区别(面试题常考),硬盘IO,顺便回顾volatile

一、回顾&#x1f49b; 谈谈volatile关键字用法 volatile能够保证内存可见性&#xff0c;会强制从主内存中读取数据&#xff0c;此时如果其他线程修改被volatile修饰的变量&#xff0c;可以第一时间读取到最新的值。 二、&#x1f499; HashMap线程不安全没有锁,HashTable线程…

【Kafka】Kafka Stream简单使用

一、实时流式计算 1. 概念 一般流式计算会与批量计算相比较。在流式计算模型中&#xff0c;输入是持续的&#xff0c;可以认为在时间上是无界的&#xff0c;也就意味着&#xff0c;永远拿不到全量数据去做计算。同时&#xff0c;计算结果是持续输出的&#xff0c;也即计算结果…

跳槽面试:如何转换工作场所而不失去优势

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

【数学建模】清风数模正课7 多元线性回归模型

多元线性回归分析 回归分析就是&#xff0c;通过研究自变量X和因变量Y的相关关系&#xff0c;来解释Y的形成机制&#xff0c;从而达到通过X去预测Y的目的。 所以回归分析需要完成三个使命&#xff0c;首先是识别重要变量&#xff0c;其次是判断正负相关&#xff0c;最后是估计…

【大数据知识】大数据平台和数据中台的定义、区别以及联系

数据行业有太多数据名词&#xff0c;例如大数据、大数据平台、数据中台、数据仓库等等。但大家很容易混淆&#xff0c;也很容易产生疑问&#xff0c;今天我们就来简单聊聊大数据平台和数据中台的定义、区别以及联系。 大数据平台和数据中台的定义 大数据平台&#xff1a;一个…

Gradio入门(1)输入输出、表格、文本高亮

本文将会介绍gradio的入门使用&#xff0c;并结合大模型&#xff08;LLM&#xff09;&#xff0c;给出三个使用例子。   Gradio 是通过友好的 Web 界面演示机器学习模型的最快方式&#xff0c;以便任何人都可以在任何地方使用它。其官网网址为&#xff1a;https://www.gradio…

手写一个简单爬虫--手刃豆瓣top250排行榜

#拿到页面面源代码 request #通过re来提取想要的有效信息 re import requests import re url"https://movie.douban.com/top250"headers{"user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/11…

Qt 查找文件夹下指定类型的文件及删除特定文件

一 查找文件 bool MyXML::findFolderFileNames() {//指定文件夹名QDir dir("xml");if(!dir.exists()){qDebug()<<"folder does not exist!";return false;}//指定文件后缀名&#xff0c;可指定多种类型QStringList filter("*.xml");//指定…

蓝蓝设计ui设计公司作品案例-中节能现金流抗压测试软件交互及界面设计

中国节能是以节能环保为主业的中央企业。中国节能以生态文明建设为己任&#xff0c;长期致力于让天更蓝、山更绿、水更清&#xff0c;让生活更美好。经过多年发展&#xff0c;中国节能已构建起以节能、环保、清洁能源、健康和节能环保综合服务为主业的41产业格局&#xff0c;成…

数据结构 day6

1->xmind 2->递归实现程序&#xff1a;输入一个数&#xff0c;输出该数的每一位

lab11 net

background 在开始写代码之前&#xff0c;回顾一下xv6book的第五章会有帮助你将使用E1000去处理网络通信 E1000会和qemu模拟的lan通信在qemu模拟的lan中 xv6的地址是10.0.2.15qemu模拟的计算机的地址是10.0.2.2 qemu会将所有的网络包都记录在packets.pcap中文件kernel/e1000.…

JVM 之字节码(.class)文件

本文中的内容参考B站尚硅谷宋红康JVM全套教程 你将获得&#xff1a; 1、掌握字节码文件的结构 2、掌握Java源代码如何在JVM中执行 3、掌握一些虚拟机指令 4、回答一些面试题 课程介绍 通过几个面试题初始字节码文件为什么学习class字节码文件什么是class字节码文件分析c…

做区块链卡牌游戏有什么好处?

区块链卡牌游戏是一种基于区块链技术的创新性游戏形式&#xff0c;它将传统的卡牌游戏与区块链技术相结合&#xff0c;实现了去中心化、数字化资产的交易和收集。这种新型游戏形式正逐渐在游戏行业引起了广泛的关注和热潮。本文将深入探讨区块链卡牌游戏的定义、特点以及其在未…

App与小程序工具总结

文章目录 前言Burpsuite抓包问题LPosedJustTrustMe 绕过 SSL Pining小程序的反编译APP脱壳&#xff0c;反射大师、frida反射大师Frida 总结 前言 在进行渗透工作的时候&#xff0c;遇到过的App、小程序也不少了&#xff0c;有简单的&#xff0c;也有加固的比较不错的&#xff…

Servlet与过滤器

目录 Servlet 过滤器 Servlet Servlet做了什么 本身不做任何业务处理,只是接收请求并决定调用哪个JavaBean去处理请求,确定用哪个页面来显示处理返回的数据 Servlet是什么 ServerApplet&#xff0c;是一种服务器端的Java应用程序 只有当一个服务器端的程序使用了Servlet…