因爲是公司內作的項目,不方便開源,就只分享優化過程吧。html
逐日是一個移動端單機小遊戲,使用Unity開發,目前已將項目使用的Unity升級到2019.4.14f1c1 (3e5991a5f6ba)
版本。git
在進行優化前,項目資源目錄以下,能夠看到,項目目錄命名雜亂,包含不少需求迭代產生的舊資源、無用場景、未壓縮的音視頻等內容。github
因爲此次主要是對於安裝包大小的一些嘗試,因此就不會特別關注遊戲邏輯,總體能加載完成,不Crash就OK,沒有對遊戲邏輯上花費過多精力,後面的過程當中可能有小部分圖被壓糊了,有視頻沒法加載等問題,不影響縮包的最終結果。編輯器
如圖所示,使用新版UnityEditor直接打包以後,生成的遊戲Apk大小爲218MB,這對於一個只有兩個關卡的小遊戲而言,明顯過於臃腫了,假如上架到GooglePlay以後,光下載內容所消耗的時間和流量就勸退了一部分用戶(更況且,如今GooglePlay限制了安裝包大小不能大於100M!)。因此,對於安裝包大小的優化是勢在必行的。ide
- Mono使用即時(JIT)編譯,並在運行時按需編譯代碼。
- IL2CPP使用提早(AOT)編譯並在運行以前編譯整個應用程序。
使用IL2CPP進行構建有助於提升運行速度,並減小包體大小,IL2CPP的工做流程以下圖:工具
將ScriptingBackend設置爲IL2CPP後,執行構建獲得的安裝包大小爲210MB,比使用Mono少了8MB(這個項目裏遊戲邏輯很少,腳本比較少,對於較大的項目來講,使用IL的優點更大)性能
Unity有提供一個Managed Stripping Level
選項,能夠在構建時裁減掉未使用的代碼,這個選項又是那個可選級別,IL2CPP不能關閉這個選項而且默認級別爲Low
。不一樣的裁剪級別對應的規則不一樣,裁減掉游泳的代碼可能性越高,而且有可能沒法檢測到經過反射引用其餘代碼的狀況,致使程序崩潰。測試
設置Managed Stripping Level=High
後,安裝包大小爲209M。優化
對於Android Platform,我對於一個空的Unity3D項目進行過以下打包測試:動畫
構建設置 | ApkSize |
---|---|
默認(Mono_Release_DotNetStandard2.0) | 16.9 MB |
ScriptBackend使用IL2CPP | 6.38 MB |
StrippingLevel=High | 6.02 MB |
CompressMethod=LZ4HC | 5.96 MB |
GraphicApi只使用OpenGLES2(Android 2.2+都支持) | 5.73 MB |
再往下就很難減少了......
在Editor中構建完成後,能夠打開EditorLog,裏面列出了在未壓縮的狀況下,不一樣類型資源的佔比以及每一個資源體積從大到小的排列,咱們能夠直接根據Log查找哪些資源比較大,針對性地處理:
爲何把這一步放在資源優化的第一位?由於先去掉無用的資源後面打包會快不少嘛。。。測試一個屬性就要從新打個包有時候甚至須要從新Import整個項目半小時的時間傷不起啊~
在GitHub上隨便找了個資源清理工具UnityAssetCleaner,對項目中的無用資源進行了清理:
這個步驟直接讓Assets目錄的大小從1.27GB
減少到192MB
,因爲資源沒有放在Resource下,因此未引用的資源並無打包到Apk中,這個步驟以後僅僅讓包體大小減小到了208MB。
可是從新Import項目會快不少啊有木有~!這也看出來當時作的時候走了多少彎路。。。
第一次用Unity作遊戲時,關卡切換、各類UI面板都是使用獨立的場景作的,明明能夠用Prefab的> _ <
看了下游戲裏的場景,沒有須要刪掉的。。。若是有無用的場景,刪除掉也能夠減小包體大小。
在項目中,儘量下降圖片和音視頻的質量,使用低質量高壓縮率的壓縮格式,不只內存佔用低、性能也更好;當出現質量不知足要求時,再逐步的提高尺寸和壓縮格式來知足須要。Link→官方文檔:不一樣平臺下支持的壓縮格式
要在不修改源文件的狀況下修改尺寸,能夠直接在Unity裏調整:
如上圖所示,遊戲裏的紋理都是按照最大尺寸來導入的,而且因爲使用的是Sprite動畫,致使一個動畫就要使用不少紋理,因此首要的問題是把不一樣的圖片調整到適當的尺寸,好比場景地圖使用2048或1024,較大的Boss角色和樹木使用512,小怪、小火球等紋理使用256或更小。
音效以及視頻,也按照相似的方法,適當調低分辨率和碼率(視頻其實能夠直接放CDN)。
調整完以後,從新打了個包,emmm...只有52MB了(固然,有一小部分紋理被我壓糊了,適當調整以後最終結果其實相差不大)。
多個NPTO的紋理拼合到一塊兒組成一個POT型的圖集有利於Unity壓縮圖片,更能節省空間。建立方式以下(Unity2019+版本默認不使用給紋理打Tag的方式建立圖集,而是須要手動建立)
將遊戲中大小、用途相似的紋理,添加到各自的圖集中,從新打包,包體大小33M。
這多是縮小Apk大小的最好方案了......
若是前面的一系列縮包方法都不能達到想要的效果,就能夠考慮把資源單獨打包拿出來,用戶啓動App時進行下載。
首先在Unity裏給資源打標籤,把資源分到對應的Bundle中。
而後執行構建AssetBundle腳本(腳本官網文檔有提供),把AssetBundle包輸出到指定路徑。
在優化的過程當中我發現,Unity經過BuildSetting中的場景列表來查找全部依賴的資源,因此爲了打出來獨立的AssetBundle包並斷開AssetBundle內的資源與Apk之間的依賴關係,就須要對場景作優化。
將場景中的內容簡單粗暴地整合到一個GameObject內,製做成Prefab,用掛載Prefab的方式代替掛載場景,並刪掉原來的場景,清理BuildSetting中的場景列表。這樣就斷開了AssetBundle內的資源與Apk之間的依賴。
個人辦法是建立一個Init場景做爲整個App的入口,裏面只掛一個GameFramework腳原本執行一系列必要的AssetBundle加載過程、管理各個用於代替場景的Prefab。
這樣修改以後,咱們構建出來的Apk就只會包含一個空的場景,以及C#腳本構建產生的dll,大小最小能有前面所說的5MB!!!
前面咱們把資源和Apk之間的依賴關係切斷了,可是咱們仍是要想辦法把它們動態加載回去的,這種方法就是AssetBundle包。
給資源打標籤的過程是很是繁瑣的,因此就使用腳本自動化處理了,會自動給ResForBundle下的直接子目錄按照目錄名遞歸添加標籤。
運行了BuildAssetBundle命令以後,咱們就能在輸出目錄獲得一系列相互依賴的AssetBundle文件了(也包括他們的manifest描述文件)。
咱們可使用Unity提供的AssetBundle Api來動態把AssetBundle文件加載到內存裏,並根據路徑讀取它們包含的資源,因爲遊戲內的資源比較少,大約20~30MB,因此我就直接放在StreamingAssets目錄下了(關於這個目錄,是和Resource同樣特殊的存在,感興趣的話能夠直接查一下)
在GameFramework啓動時就加載全部的AssetBundle,並掛載Scene_Start.prefab到場景中,這樣咱們的遊戲流程又能正常走下去了。
固然這個過程當中還有好多好多細節要處理:
好比須要用宏來判斷是在編輯器中仍是在移動端,從而採用不一樣的自定義ResourceLoad來加載資源(若是是編輯器的話直接使用AssetDatabase Api經過路徑加載資源,移動端則採用上面提到的AssetBundle內的資源);
如何遞歸地給指定目錄下的內容添加標籤並自動維護它們;
對場景進行精簡優化,只保留一個入口場景加載AssetBundle和加載入口,構建後,Apk大小隻有7M。
打出的AssetBundle能夠在App啓動時經過http拉取到本地,若是不嫌資源包大的話也能夠直接放在StreamingAssets目錄打到apk內。
直接上圖好了:
KeyWorkds:Unity安裝包體積優化,Unity減少安裝包體積,Unity構建更小的Apk,Unity減少構建的安裝包大小,Unity縮小安裝包