在游戲開發中,常常需要用到熱更新技術。比如:一個手機游戲開發好后,用戶安裝到手機上。如果此時我們要更新一個新的功能,如果沒有熱更新,那么需要用戶卸載掉手機上的游戲,然后安裝新的包,這樣做十分麻煩,而且容易流失用戶。這個時候就需要使用熱更新技術,讓用戶在手機上下載新的游戲功能,不用重新下載就行了。下面使用的AssetBundle,嚴格意義上來說不算是熱更新,但是,它也能減少初始包體的大小。比如我們開發一款棋牌游戲,最初的包體只放出了斗地主,在游戲中,如果玩家還想玩麻將,那么此時再下載麻將的相關資源。
AssetBundle的定義和作用:
1,AssetBundle是一個壓縮包包含模型、貼圖、預制體、聲音、甚至整個場景,可以在游戲運行的時候被加載;
2,AssetBundle自身保存着互相的依賴關系;
3,壓縮包可以使用LZMA和LZ4壓縮算法,減少包大小,更快的進行網絡傳輸;
4,把一些可以下載內容放在AssetBundle里面,可以減少安裝包的大小;
什么是AssetBundle:
1,它是一個存在於硬盤上的文件。可以稱之為壓縮包。這個壓縮包可以認為是一個文件夾,里面包含了多個文件。這些文件可以分為兩類:serialized file 和 resource files。(序列化文件和源文件)
serialized file:資源被打碎放在一個對象中,最后統一被寫進一個單獨的文件(只有一個)
resource files:某些二進制資源(圖片、聲音)被單獨保存,方便快速加載
2,它是一個AssetBundle對象,我們可以通過代碼從一個特定的壓縮包加載出來的對象。這個對象包含了所有我們當初添加到這個壓縮包里面的內容,我們可以通過這個對象加載出來使用。
AssetBundle使用流程圖
打包成AsserBundle,放到服務器

玩家用到相應的功能,再從服務器下載

AssetBundle使用流程
1,指定資源的AssetBundle屬性
2,構建AssetBundle包
3,上傳AB包
4,加載AB包和包里面的資源
實際使用:
1:首先我們隨便做一個需要打包的資源,然后指定該資源的AssetBundle屬性。其中包名是需要指定的,后綴名可以隨便寫,在學習的過程中沒有什么實際作用,在實際工作中根據公司需要來寫吧。
注意:如果包名寫成aaa,那么會直接創建以aaa為名的包。如果包名寫成aaa/bbb,那么會創建名為aaa的文件夾,在此文件夾下創建名為bbb的包

2:打包之前,我們要明白,打包只是在Edidor模式下運行,在游戲運行過程中沒有這個步驟。所以,創建一個文件夾名為“Editor”,特別注意只能為這個名字,然后在此文件夾下寫代碼來打包AssetBundle。在代碼中寫好方法后,將此方法放到Unity的菜單下來手動調用。
using UnityEditor;
using System.IO;
public class CreateAssetBundles {
[MenuItem("Assets/Build AssetBundles")]
static void BuildAllAssetBundles()
{
string dir = "AssetBundles";
if(Directory.Exists(dir) == false)
{
Directory.CreateDirectory(dir);
}
// BuildPipeline.BuildAssetBundles:打包的方法
// 參數:打包的路徑,Build的選項(下面專門說),打包的目標平台
BuildPipeline.BuildAssetBundles(dir, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64);
}
}
3:在Unity菜單下,點擊該選項,進行打包,打包好后資源就存在了


加載AB包和包里的資源:
我們將場景中打包用到的資源都刪除,因為我們這些資源已經打包好了,可以直接加載這些資源。
注意:在加載資源的時候填入的名字,和包的名字可能不一樣,要看自己創建模型的時候是取得什么名字
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LoadFromFile : MonoBehaviour {
void Start () {
// 加載ab包
AssetBundle ab = AssetBundle.LoadFromFile("AssetBundles/wood.unity3d");
// 加載資源
GameObject wallPrefab = ab.LoadAsset<GameObject>("Wood");
Instantiate(wallPrefab);
}
}
可以看到,運行后也加載好了資源

AssetBundle分組策略(僅供參考)
1,邏輯實體分組
a,一個UI界面或者所有UI界面一個包(這個界面里面的貼圖和布局信息一個包)
b,一個角色或者所有角色一個包(這個角色里面的模型和動畫一個包)
c,所有的場景所共享的部分一個包(包括貼圖和模型)
2,按照類型分組
所有聲音資源打成一個包,所有shader打成一個包,所有模型打成一個包,所有材質打成一個包
3,按照使用分組
把在某一時間內使用的所有資源打成一個包。可以按照關卡分,一個關卡所需要的所有資源包括角色、貼圖、聲音等打成一個包。也可以按照場景分,一個場景所需要的資源一個包
總結:
1,把經常更新的資源放在一個單獨的包里面,跟不經常更新的包分離
2,把需要同時加載的資源放在一個包里面
3,可以把其他包共享的資源放在一個單獨的包里面 (依賴打包)
4,把一些需要同時加載的小資源打包成一個包
5,如果對於一個同一個資源有兩個版本,可以考慮通過后綴來區分
依賴打包:
如果我們有一份圖片資源,有兩個物體同時用到了這份資源,當單獨對這兩個物體進行打包的時候,打出的包中都會包含圖片資源。但是當我們首先對圖片資源進行打包后,再對兩個物體進行打包,在打包的時候,引擎會自動檢索依賴,這個時候檢測到自身所依賴的圖片資源已經打包了,那么這個時候自身就不會再對這個圖片資源進行打包。這樣,就減少了包體的大小。注意:在使用依賴打包后,如果A依賴了B的資源,那么在使用A的時候,必須加載B,否則A實例化出來后材質會丟失。
打包選項(AssetBundle壓縮方式)
在上面打包的時候函數有3個參數,其中第二個參數就是打包選項,用來控制打包時的壓縮方式。
1:BuildAssetBundleOptions.None:使用LZMA算法壓縮,壓縮的包更小,但是加載時間更長。使用之前需要整體解壓。一旦被解壓,這個包會使用LZ4重新壓縮。使用資源的時候不需要整體解壓。在下載的時候可以使用LZMA算法,一旦它被下載了之后,它會使用LZ4算法保存到本地上。

2:BuildAssetBundleOptions.UncompressedAssetBundle:不壓縮,包大,加載快

3:BuildAssetBundleOptions.ChunkBasedCompression:使用LZ4壓縮,壓縮率沒有LZMA高,但是我們可以加載指定資源而不用解壓全部

注意:使用LZ4壓縮,可以獲得可以跟不壓縮想媲美的加載速度,而且比不壓縮文件要小。
AssetBundles的使用
1,AssetBundle.LoadFromMemoryAsync :從內存加載(異步加載)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
public class LoadFromFile : MonoBehaviour {
IEnumerator Start () {
string path = "AssetBundles/wood.unity3d";
// 第一種加載AB的方式 LoadFromMemoryAsync
AssetBundleCreateRequest request = AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(path));
yield return request;
AssetBundle ab = request.assetBundle;
// 加載資源
GameObject wallPrefab = ab.LoadAsset<GameObject>("Wood");
Instantiate(wallPrefab);
}
}
另一種寫法:(同步加載)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
public class LoadFromFile : MonoBehaviour {
void Start () {
string path = "AssetBundles/wood.unity3d";
// 第一種加載AB的方式 LoadFromMemoryAsync
AssetBundle ab = AssetBundle.LoadFromMemory(File.ReadAllBytes(path));
// 加載資源
GameObject wallPrefab = ab.LoadAsset<GameObject>("Wood");
Instantiate(wallPrefab);
}
}
注意:我們上面保存到本地,所以最好直接用文件加載。演示從內存加載的時候,我們首先把本地文件轉成字節流后再加載,在實際工作中不需要多這一步,怎么合適怎么做。
2,AssetBundle.LoadFromFile :從文件加載
下面是異步加載,同步加載在最上面開始的時候就寫過了
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
public class LoadFromFile : MonoBehaviour {
IEnumerator Start () {
string path = "AssetBundles/wood.unity3d";
// 第二種加載AB的方式 LoadFromFile
AssetBundleCreateRequest request = AssetBundle.LoadFromFileAsync(path);
yield return request;
AssetBundle ab = request.assetBundle;
// 加載資源
GameObject wallPrefab = ab.LoadAsset<GameObject>("Wood");
Instantiate(wallPrefab);
}
}
3,WWW.LoadFromCacheOrDownload (在unity2017后已廢棄,分成2和4)
從本地加載
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
public class LoadFromFile : MonoBehaviour {
IEnumerator Start () {
string path = "AssetBundles/wood.unity3d";
//第三種加載AB的方式 WWW
while (Caching.ready == false)
{
yield return null;
}
//file:// file:///
WWW www = WWW.LoadFromCacheOrDownload(@"file:/H:\Unity Project WorkSpace\AssetBundleProject\39_AssetBundle\AssetBundles\wood.unity3d", 1);
yield return www;
if (string.IsNullOrEmpty(www.error) == false)
{
Debug.Log(www.error); yield break;
}
AssetBundle ab = www.assetBundle;
// 加載資源
GameObject wallPrefab = ab.LoadAsset<GameObject>("Wood");
Instantiate(wallPrefab);
}
}
從服務器加載:這里用的是本地服務器,本地服務器使用“NetBox2.exe”雙擊創建
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
public class LoadFromFile : MonoBehaviour {
IEnumerator Start () {
string path = "AssetBundles/wood.unity3d";
//第三種加載AB的方式 WWW
while (Caching.ready == false)
{
yield return null;
}
//file:// file:///
WWW www = WWW.LoadFromCacheOrDownload(@"http://localhost/AssetBundles/wood.unity3d", 1);
yield return www;
if (string.IsNullOrEmpty(www.error) == false)
{
Debug.Log(www.error); yield break;
}
AssetBundle ab = www.assetBundle;
// 加載資源
GameObject wallPrefab = ab.LoadAsset<GameObject>("Wood");
Instantiate(wallPrefab);
}
}
4,UnityWebRequest:從服務器加載
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using UnityEngine.Networking;
public class LoadFromFile : MonoBehaviour {
IEnumerator Start () {
//第四種方式 使用UnityWebRequest
// 下面2個一個從本地,一個從服務器
//string uri = @"file:///E:\Unity Project Workspace\AssetBundleProject\AssetBundles\cubewall.unity3d";
string uri = @"http://localhost/AssetBundles/cubewall.unity3d";
UnityWebRequest request = UnityWebRequest.GetAssetBundle(uri);
yield return request.Send();
// 下面兩種方式都行
//AssetBundle ab = DownloadHandlerAssetBundle.GetContent(request);
AssetBundle ab = (request.downloadHandler as DownloadHandlerAssetBundle).assetBundle;
// 加載資源
GameObject wallPrefab = ab.LoadAsset<GameObject>("Wood");
Instantiate(wallPrefab);
}
}
以上只是簡單的使用方法,更多的使用方法需要看官方手冊!!!
