Unity熱更新 AssetBundle


在游戲開發中,常常需要用到熱更新技術。比如:一個手機游戲開發好后,用戶安裝到手機上。如果此時我們要更新一個新的功能,如果沒有熱更新,那么需要用戶卸載掉手機上的游戲,然后安裝新的包,這樣做十分麻煩,而且容易流失用戶。這個時候就需要使用熱更新技術,讓用戶在手機上下載新的游戲功能,不用重新下載就行了。下面使用的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);
	}
}

 

以上只是簡單的使用方法,更多的使用方法需要看官方手冊!!!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM