1. 程式人生 > >Unity自動更新、AssetBundle整理

Unity自動更新、AssetBundle整理

        這篇文章梳理了我現在所理解的Unity的AssetBundle、自動更新的流程和知識點。

一、關於AssetBundle

        如果不是為了自動更新的話,其實完全可以避免掉AssetBundle,全部資源放到Resources目錄下或者在Scene中直接引用。這樣可以避免掉很多坑。但是考慮到手遊的自動更新,AssetBundle就非常有必要了。它相當於一個小的資源包,提供了在遊戲完整包之外額外載入資源的能力。但是由於Unity內部資源載入和資源依賴引用的黑科技,Unity所提供的打包和使用AssetBundle的介面都非常蹩腳。關於AssetBundle基礎使用和坑,我之前寫文章分享過(

http://blog.csdn.net/langresser_king/article/details/44208585),這裡會對部分知識點進行修正,以及進行拾遺補缺。

        1、關於資源規劃。

             如果專案資源非常大的情況下,可以把資源獨立一個專案專門用於資源匯出。這樣程式在開發和除錯的時候不會因為專案資源過大造成啟動慢、卡頓的情況。程式的資源載入順序分開發和實際釋出版本兩種情況,開發模式下應該優先載入Resources目錄下的資源,如果沒有的話,再載入AssetBundle的資源,這樣如果有資源需要修改或者測試的話,直接把對應的資源拷貝到Resources目錄下就可以了,不一定需要打包才能測試。而實際釋出的版本則相反,優先載入AssetBundle,如果沒有的話再載入Resources目錄下的資源,這樣可以保證自動更新的邏輯。

             之前我有考慮統一打包成AssetBundle然後放到StreamingAssets目錄下,這樣統一都用AssetBundle的邏輯來處理資源。後來發現其實沒有太大必要。只要我們資源規劃是合理的,那麼無論是配置中,還是邏輯程式碼中是完全可以相容Resources.Load和AssetBundle的載入方式的。而且就我現在的理解來看,Resources.Load的載入速度要比AssetBundle要快,因為AssetBundle是壓縮的,而Resources.Load是未壓縮的(當然這意味者iOS遊戲安裝後實際佔用的檔案大小會幾倍於安裝包,Android的遊戲安裝並不會解壓apk包,所以不受影響)。

              上一段的結論尚未驗證,我在Editor下測試了一下AssetBundle和Resource.Load的載入速度,反而是AssetBundle要快。實際結論如何要在手機平臺上測試過才能確定。

              在我的規劃中,所有帶動畫的、需要繫結指令碼的、需要設定包圍盒的模型都要建立prefab,然後給這個prefab設定AssetBundle的名字(即匯出這個AssetBundle)。如果存在同一個模型可以替換多個紋理的情況(一種簡單的換裝),那麼所有的紋理都要匯出。除此之外的模型可以直接匯出。

              總結一下AssetBundle的步驟就是,給需要的模型建立Prefab,設定AssetBundle的匯出名字,匯出AssetBundle。

         2、如何設定AssetBundle的匯出名字

             之前我是直接修改meta檔案的,後來發現完全可以在匯入資源的時候就設定好。程式碼如下:

using UnityEngine;
using UnityEditor;
using System.Collections;
using System.IO;
using System.Linq;

// 設定固定資料夾下面的assetbundle的名字
public class AssetBundleImporter : AssetPostprocessor
{
    static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
    {
        if (!EditorConfig.AUTO_SET_ASSETBUNDLE_NAME) {
            return;
        }

        string[] extList = new[] {".png", ".jpg", ".tga"};

        foreach (var item in importedAssets) {
            if (item.IndexOf(".") == -1) {
                // 資料夾
                continue;
            }
            if (item.IndexOf(EditorConfig.MODEL_PATH) != -1) {
                // 打包Model資料夾下的模型或者貼圖或者prefab
                string basePath = item.Substring(item.IndexOf(EditorConfig.MODEL_PATH) + EditorConfig.MODEL_PATH.Length);
                basePath = basePath.Substring(0, basePath.IndexOf("/"));
                string ext = Path.GetExtension(item);
                if (EditorConfig.EXPORT_MODEL_DIRECTLY.Contains(basePath)) {
                    if (extList.Contains(ext) || ext == ".fbx") {
                        // 此資料夾下只匯出貼圖和模型
                        string relativePath = item.Substring(item.IndexOf(EditorConfig.PATH_TAG) + EditorConfig.PATH_TAG.Length);
                        string prefabName = relativePath.Substring(0, relativePath.IndexOf('.')) + EditorConfig.ASSETBUNDLE_EXT;
                        SetAssetBundleName(item, prefabName.ToLower());
                    } else {
                        SetAssetBundleName(item, null);
                    }
                } else {
                    if (extList.Contains(ext) || ext == ".prefab") {
                        // 此資料夾下只匯出貼圖和prefab
                        string relativePath = item.Substring(item.IndexOf(EditorConfig.PATH_TAG) + EditorConfig.PATH_TAG.Length);
                        string prefabName = relativePath.Substring(0, relativePath.IndexOf('.')) + EditorConfig.ASSETBUNDLE_EXT;
                        SetAssetBundleName(item, prefabName.ToLower());
                    } else {
                        SetAssetBundleName(item, null);
                    }
                }
            }
        }
    }

    static void SetAssetBundleName(string path, string abName)
    {
        AssetImporter importer = AssetImporter.GetAtPath(path);
        if (abName != null) {
            if (importer.assetBundleName != abName) {
                importer.assetBundleName = abName;
            }
        } else {
            importer.assetBundleName = null;
        }
    }
}

        3、如何匯出AssetBundle

             之前我沒有指定匯出資源包的目標平臺,但是在最新測試的時候發現bug--------匯出資源的目標平臺並不是專案當前選定的平臺。現在的處理程式碼如下:

using UnityEngine;
using UnityEditor;
using System;
using System.IO;

public class ExportAssetBundles : Editor
{
    public static string GetAssetBundlePath(string path)
    {
        string dataPath = Application.dataPath.Replace("\\", "/");
        string outputPath = dataPath.Substring(0, dataPath.IndexOf("/") + 1);
        return Path.Combine(outputPath, path);
    }

    public static void OnCreateAssetBundleAndroid()
    {
        string path = GetAssetBundlePath("AssetsBundle/android/");
        if (!Directory.Exists(path)) {
            Directory.CreateDirectory(path);
        }

        BuildPipeline.BuildAssetBundles(path, BuildAssetBundleOptions.None, BuildTarget.Android);
    }

    public static void OnCreateAssetBundleIOS()
    {
        string path = GetAssetBundlePath("AssetsBundle/ios/");
        if (!Directory.Exists(path)) {
            Directory.CreateDirectory(path);
        }

        BuildPipeline.BuildAssetBundles(path, BuildAssetBundleOptions.None, BuildTarget.iOS);
    }

    public static void OnCreateAssetBundleWindows()
    {
        string path = GetAssetBundlePath("AssetsBundle/windows/");
        if (!Directory.Exists(path)) {
            Directory.CreateDirectory(path);
        }

        BuildPipeline.BuildAssetBundles(path, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64);
    }
}
          這裡需要注意,如果匯出的資源的目標平臺跟專案當前平臺不一致的話,Unity會先轉換紋理資源到目標平臺,然後匯出資源包,最後再轉換回來。所以如果資源量非常大的話,轉換所消耗的時間非常恐怖,所以建議還是每個平臺一個資料夾,設定好專案平臺,專門匯出此平臺的資源。

          另外,在Editor模式下,無論當前平臺選擇的是什麼,都只能使用windows平臺的AssetBundle,否則即便模型匯入正常,shader也會出問題,提示某某屬性找不到。

          4、關於如何載入AssetBundle

             載入AssetBundle可以直接使用WWW.LoadFromCacheOrDownload來載入,使用這個函式而不直接使用new WWW,是因為Load函式會有檔案對映處理,只加載檔案頭,而不是所有檔案都載入到記憶體中,這樣可以節約記憶體。

             我寫了一個指令碼可以自動生成一個資料夾下assetbundle的資源列表,並解析manifest設定檔案依賴。我定義的格式是這樣的:檔案路徑=hash碼,依賴項1,依賴項2

             hash碼可以解析每個assetbundle對應的manifest獲取。依賴項可以解析總的manifest來獲取。這裡稍微注意一下,如果多個資源專案生成assetbundle到同一個目錄的話,這個總的manifest檔案是會被覆蓋的,這裡要自行處理一下。

             鑑於我的專案需求,我對AssetBundle的載入處理略微複雜一些(其實還是很簡單的,200行程式碼而已),關鍵處理了一個細節,如果我想要載入的assetbundle正在載入中,那麼不重複載入,等待正在載入的assetbundle載入完畢通知所有的上層回撥。如果想要載入的assetbundle已經載入完畢,那麼可以直接使用。

             這裡額外說明一下,AssetBundle有一個CreateFromFile的介面,可以同步處理AssetBundle的建立,並且速度是最快的。不過它只能載入未壓縮的資源包。所以如果確實需要的話,是可以這麼做的(但是個人並不推薦),AssetBundle自行壓縮,解壓,然後使用這個介面來進行載入,缺點是會浪費磁碟空間。

        5、關於shader丟失的問題

             所有的shader都把assetbundle的匯出名字設定為"shader",這樣模型會依賴這個shader的ab包,當修改shader的時候不需要重新打包所有的模型。客戶端在初始化的時候要載入這個shader的ab包,否則所有模型的shader都會發生錯誤,即便客戶端專案中有一模一樣的shader,但是Unity也無法將模型與之關聯。如果不載入這個預設包的話,那麼就要手工指定Material的shader(使用Shader.Find)。暫時我還沒有找到理想的處理方式。

二、關於自動更新

          之前做端遊的時候,包括後來用cocos2d做專案的時候,自動更新是這麼處理的,對比新舊版本的資源差異,提取出來作為更新包,可以直接用版本號命名,如1000.zip,更新伺服器存放一個更新包列表的檔案。客戶端解析這個列表檔案,順序獲取到最新版本之間的更新包,並逐一解壓。最後把最新的版本號寫到客戶端。隔一定時間生成一個1000-1009.zip這樣的跨版本更新包,這樣可以防止多個小版本之間的重複資源。

          現在鑑於Unity的AssetBundle本身就是壓縮的了,並且AssetBundle是多個關聯的資源整合到一個包裡面的,所以資源粒度本身不會太小。所以這裡我的更新處理方式是這樣的。維護一個版本號,這個版本號僅僅用來判定是否需要更新。維護一個資源列表,這個在上面已經說明過了,它維護了當前所有assetbundle的檔案和hash碼。客戶端先判斷版本號是否需要更新。如果安裝包內的版本號更新的話,那麼就意味者剛剛安裝完一個大版本的更新包,此時要清理所有之前下載的assetbundle。如果伺服器版本號更新的話,那麼就需要進行自動更新。

         客戶端下載資源列表檔案,與本地的進行比照,獲取需要下載更新的assetbundle。即便我們這是一個新的安裝包,資源都放到Resources目錄下,沒有assetbundle,我們也會在釋出時打包assetbundle,並生成資源列表,隨客戶端釋出。

         獲取到assetbundle的列表後,逐一下載。更新完畢後把新的資源列表資料和版本號寫入到客戶端。

         這裡插一些關於熱更新的看法,如果要進行程式碼級別的更新,使用uLua是最好的選擇了,速度很快,使用也簡單。但是在我看來還是應該優先考慮效能、開發效率,然後再考慮熱更新。即便不使用lua,不更新程式碼,我們依然可以更新配置、更新資源。一味的想著使用lua,更新程式碼,可以快速的消除致命bug,其實不是可取的態度。如介面邏輯,活動任務等是比較適合使用lua來寫的,這些功能相對獨立,並且與效能沒有太大關係。

相關推薦

Unity自動更新AssetBundle整理

        這篇文章梳理了我現在所理解的Unity的AssetBundle、自動更新的流程和知識點。 一、關於AssetBundle         如果不是為了自動更新的話,其實完全可以避免掉AssetBundle,全部資源放到Resources目錄下或者在Scene

Unity更新技術整理

nil 支持 -s 運行 ram 創建 color 腳本語言 更改 一、熱更新學習介紹 1、什麽是熱更新 舉例來說: 遊戲上線後,玩家下載第一個版本(70M左右或者更大),在運營的過程中,如果需要更換UI顯示,或者修改遊戲的邏輯,這個時候,如果不使用熱更新,就需要重新打

centos7更新更新每天更新每天自動更新

ron 配置 .com crond load idt etc 狀態 con 每一天都有成千上萬的黑客在世界各地尋找 Linux 系統和常見軟件的安全漏洞,一有發現便會發動規模龐大而迅速的網絡攻擊,務求在我們來得及反應前把系統攻陷。不要以為黑客都只是十來歲的年輕小毛頭,大部分

Spring整理系列(11)——@Configuration註解@Bean註解以及配置自動掃描bean作用域

1、@Configuration標註在類上,相當於把該類作為spring的xml配置檔案中的<beans>,作用為:配置spring容器(應用上下文) package com.test.spring.support.configuration; @Configuration pub

SpringBoot整合Redis實現自動快取更新刪除

1:引入springboot redis的maven依賴(建議使用spring-boot-dependencies或者使用spring-io-platform進行構建專案) <dependency> <g

基於OkHttp Retrofit RxJava 多執行緒下載。請求快取自動更新.限制佇列數.封裝庫

XDownload介紹 本庫封裝基於Okhttp3,Retrofit2,RxJava2.0,Greendao3.2 ps : 當然當然,都封裝好了,你也可以無視 GitHub地址 如果你覺得好用,對你有幫助,請給個star 介面

Spring整理系列(10)——@Autowired自動裝配結合@Qualifier過濾及與
Android應用程式的自動更新升級(自身升級通過tomcat)

剛入手android一個多月,因公司需要提交技術文件,才寫了這個demo測試,想儲存下來,以備後用!有什麼不對的地方歡迎大家指正,這個示例也是參考了網上別人的demo自己做的。                                               

Hibernate學習-11:持久化物件狀態及狀態轉換持久態物件自動更新資料庫

持久化類:就是一個實體類 與 資料庫表建立了對映. Hibernate為了方便管理持久化類,將持久化類分成了三種狀態. 瞬時態 transient  :(臨時態)特點:持久化物件沒有唯一標識OID.沒有納入Session的管理 持久態 persistent :特點:持久化物

Bootstrap常用實用整理(bootstrap踩過的坑),持續更新......

bootstrap是一個響應式前段框架、豐富的外掛。可以提高開發效率,前段時間專案中用到了bootstrap在這裡我記錄下專案中用到的東西及我的理解(我只是一個小白),希望大家多多指正、共同學習。 在這裡我要介紹下常用的、比如bootstrap suggest、及boots

Android應用的自動升級更新模組的實現

我們看到很多Android應用都具有自動更新功能,使用者一鍵就可以完成軟體的升級更新。得益於Android系統的軟體包管理和安裝機制,這一功能實現起來相當簡單,下面我們就來實踐一下。首先給出介面效果: 1. 準備知識 在AndroidManifest.xml裡定義了每個

1 Springboot中使用redis,自動快取更新刪除

第一篇記錄一下在springboot中,redis的基礎用法,自動快取新增的資料,自動修改及刪除。 在本機安裝好mysql和redis。新建一個springboot的web專案,在新建專案時勾選redis,mysql。 pom檔案如下: <?xml version="

數據更新視圖的創建與改動

none 記錄 text 分析器 計算機 ext 使用 margin border 一、實驗目的 1、學會使用INSERT、UPDATE、DELETE等SQL語句進行數據更新; 2、學會使用CREATE、DROP等SQL語句創建和刪除視圖。 二、實驗內容 1、在

Mysql實現級聯操作(級聯更新級聯刪除)

刪除表 null weight .cn eat 失敗 bsp src 成績 一、首先創建兩張表stu,sc create table stu( sid int UNSIGNED primary key auto_increment, name varchar(20) no

C# 利用FTP自動下載xml文件後利用 FileSystemWatcher 監控目錄下文件變化並自動更新數據庫

use img div 進行 ssa reg c# col without using FtpLib; using System; using System.Collections.Generic; using System.ComponentModel;

mysql-插入更新刪除數據

sta row code ica update tab -- affect nbsp 1、插入: ① mysql中有三種插入:insert into、replace into、insert ignore insert into:表示插

odoo自動更新表中數據

too data font .sql info 復數 del 頁面 cti 這是追蹤信息用的查詢語句,__init__方法初始化作用_order ="hpartner_id desc"def init(self,cr): tools.sql.drop_view_if_

Unity資源打包之Assetbundle

nas rar 啟用 大致 ebp 掛載 href 交叉 bundle 本文原創版權歸 csdn janeky 全部。轉載請具體註明原創作者及出處,以示尊重。 作者:janeky 原文:http://blog.csdn.net/janeky/article/detail

svn提交後利用hooks自動更新web服務器

whoami 報錯 roo 出版 自動 scp upd 版本庫 ech #!/bin/shREPOS=”$1″#版本庫REV=”$2″#版本號#export LC_ALL=C#export LANG=zh_CN.UTF-8export LANG=en_US.UTF-8#字符

Unity UGUI——概述長處

mod 分享 pop num 4.6 使用 number his 推出 Unity4.6推出的新UI系統 長處:靈活、高速、可視化、效率高效果好、易於使用和擴展 $(function () {