Unity Addressable Asset System 文檔

簡(jiǎn)介

Unity可尋址資源系統(tǒng)

可尋址資源系統(tǒng)提供了一種簡(jiǎn)單的方法通過“地址”加載資源呻引。簡(jiǎn)化資源包的創(chuàng)建和部署的管理開銷勒奇。
可尋址資源系統(tǒng)允許你從任何地方通過異步加載的方式加載資源包产徊。無論你是通過"直接引用",或者傳統(tǒng)的Assetbundle形式员串,亦或是通過"Resource"文件夾管理,可尋址資源系統(tǒng)提供了一種簡(jiǎn)單的方法來使你的游戲更加靈活多變盔腔。

什么是一個(gè)資源(Asset)

Asset資源是你用來創(chuàng)建游戲或應(yīng)用程序的內(nèi)容喷屋。 資產(chǎn)的常見例子包括預(yù)制件Prefabs琳拨、紋理Textures、材質(zhì)Materials屯曹、音頻剪輯AudioClips和動(dòng)畫Animations狱庇。

什么是一個(gè)可尋址的資源

一個(gè)被做成可尋址的資源,可以從任何地方通過"唯一地址"取訪問它,無論它是打包駐留在本地的資源,還是發(fā)布到遠(yuǎn)程服務(wù)器上的資源,可尋址資源系統(tǒng)都可以定位并返回它,你可以通過地址加載單個(gè)可尋址資源,或者通過自定義的組標(biāo)簽加載多個(gè)可尋址資源

為什么要使用可尋址資源

傳統(tǒng)的游戲資源的結(jié)構(gòu),使得有效加載是一個(gè)非常具有挑戰(zhàn)性的事情.但是可尋址資源系統(tǒng)可以大幅縮短迭代周期,讓你節(jié)省大量的時(shí)間取用于設(shè)計(jì),編碼,和測(cè)試你的應(yīng)用

  • 迭代時(shí)間: 按地址引用內(nèi)容是非常有效的,優(yōu)化內(nèi)容將不再需要更改代碼。
  • 依賴項(xiàng)管理: 當(dāng)你請(qǐng)求一個(gè)內(nèi)容時(shí),系統(tǒng)會(huì)自動(dòng)返回所請(qǐng)求內(nèi)容的所有依賴項(xiàng),在將內(nèi)容返回給您之前先加載所有網(wǎng)格恶耽、著色器密任、動(dòng)畫等等。
  • 內(nèi)存管理: 尋址系統(tǒng)能卸載資源也能加載資源,而且會(huì)自動(dòng)計(jì)算引用,并提供一個(gè)強(qiáng)大分析器來幫助您發(fā)現(xiàn)潛在的內(nèi)存問題偷俭。
  • 內(nèi)容打包: 系統(tǒng)會(huì)自動(dòng)映射和理清的復(fù)雜的依賴關(guān)系,并進(jìn)行有效的打包浪讳,即使移動(dòng)或重命名資源后也是如此. 您可以很容易地為本地和遠(yuǎn)程部署準(zhǔn)備資源,以支持追加或減少應(yīng)用程序的下載內(nèi)容的大小。

概覽

可尋址資產(chǎn)系統(tǒng)由兩個(gè)包組成(在Package Managed中打開):

  • Addressable Assets package (primary package)
  • Scriptable Build Pipeline package (dependency)

當(dāng)您安裝Addressable Assets package涌萤,Scriptable Build Pipeline package將同時(shí)安裝淹遵。

常見概念
  • Address : 運(yùn)行時(shí)檢索資源位置的標(biāo)識(shí)符
  • AddressableAssetData directory : 存儲(chǔ)您的可尋址資源元數(shù)據(jù)在項(xiàng)目的資產(chǎn)目錄
  • Asset group : 資源組,可用于構(gòu)建時(shí)處理一組可尋址資產(chǎn)
  • Asset group schema : 定義一組數(shù)據(jù)可以分配到一個(gè)組,在生成過程中使用
  • AssetReference : 這個(gè)做法就像是一個(gè)直接引用的對(duì)象(類似傳統(tǒng)的拖拽到面板上綁定)负溪,但會(huì)延遲初始化透揣。該AssetReference對(duì)象存儲(chǔ)GUID作為尋址,可以按需加載笙以。
  • Asynchronous loading : 異步加載,允許在整個(gè)應(yīng)用過程中中定位資源及其依賴的位置,而且是在不改變游戲代碼的前提下淌实。異步加載是尋址資產(chǎn)系統(tǒng)的基礎(chǔ).
  • Build script : 構(gòu)建腳本,運(yùn)行資產(chǎn)組處理器進(jìn)行資源打包,并對(duì)資源管理器的提供資源的地址和與可尋址標(biāo)識(shí)之間的映射關(guān)系。
  • Label : 標(biāo)簽,為一個(gè)資源提供額外的可尋址表示符

開始使用

安裝可尋址資源系統(tǒng)的包

重要:可尋址資源系統(tǒng)要求Unity的版本為2018.3及以上
通過Window > Package Manager 打開找到Addressables即可安裝

將一個(gè)資源標(biāo)記為可尋址

Unity中有兩種方式可以將一個(gè)資源標(biāo)記為可尋址的

  1. 在Inspector監(jiān)視面板中
    Project window項(xiàng)目窗口中,選擇需要標(biāo)記的資源,然后在Inspector面板中,勾選上Address的復(fù)選框,并輸入一個(gè)名稱標(biāo)記該資源

    Inspector中標(biāo)記.png

  2. 在單獨(dú)的可尋址資源管理窗口中標(biāo)記
    選擇Window > Asset Management > Addressables打開Addressables窗口拆祈。接下來恨闪,將所需的資產(chǎn)從Project window窗口拖拽到Addressables窗口中的資產(chǎn)組之一。

    Addressables中標(biāo)記.png

指定地址

默認(rèn)地址為該資源在你項(xiàng)目中的路徑(例如放坏,Assets/images/myImage.png),若要通過 Addressables window 改變?cè)撡Y源的地址咙咽,用鼠標(biāo)右鍵單擊該資源,并選擇重命名淤年。 當(dāng)你第一次開始使用可尋址的資源钧敞,系統(tǒng)會(huì)保存的一些編輯時(shí)和運(yùn)行時(shí)的數(shù)據(jù)資源在Assets/AddressableAssetsData文件中,該文件應(yīng)該被添加到您的版本控制的允許列表中麸粮。

打包構(gòu)建可尋址內(nèi)容

資源管理系統(tǒng)需要你在使用之前打包構(gòu)建所有的可尋址資源,該步驟不是自動(dòng)的,你可以通過編輯器來構(gòu)建或者通過的API來構(gòu)建

  • 通過編輯器構(gòu)建:在Addressables window, 然后選擇 Build > Build Player Content
  • 通過API來構(gòu)建:AddressableAssetSettings.BuildPlayerContent()
使用可尋址資源
通過address加載或者實(shí)例化資源

您可以在運(yùn)行時(shí)加載或?qū)嵗粋€(gè)尋址資源,并加載資源的所有依賴項(xiàng)到內(nèi)存中(包括可用的綁定數(shù)據(jù))溉苛,允許你在需要的時(shí)候使用它,但這不會(huì)直接將其添加到場(chǎng)景中,若要將資源添加到您的場(chǎng)景你還需要instantiate實(shí)例化。但是你也可以直接通過Addressables的Instantiate接口立即加載并實(shí)例化添加到您的場(chǎng)景弄诲。

通過address訪問可尋址資源,需要先應(yīng)用命名空間UnityEngine.AddressableAssets,然后調(diào)用如下方法:

//這將使用指定的地址加載資產(chǎn)愚战。
Addressables.LoadAssetAsync<GameObject>("AssetAddress");

或者

//這將直接實(shí)例化指定地址的資源并添加到場(chǎng)景中
Addressables.InstantiateAsync("AssetAddress");

Note: LoadAssetAsyncInstantiateAsync 是異步操作方式. 你可以通過回調(diào)函數(shù)當(dāng)加載完成時(shí)后進(jìn)行后續(xù)的操作 (具體請(qǐng)查看文檔 Async operation handling)

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

public class AddressablesExample : MonoBehaviour {
    
    GameObject myGameObject;
    void Start()
    {
        Addressables.LoadAssetAsync<GameObject>("AddressTest").Completed += OnLoadDone;
    }

    private void OnLoadDone(UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle<GameObject> obj)
    {
        // In a production environment, you should add exception handling to catch scenarios such as a null result.
        myGameObject = obj.Result;
    }
}

子資源和組件

子資源和組件對(duì)資源加載來說是一個(gè)特殊情況

組件 : 你不能通過Addressables直接加載一個(gè)組件,你必須先實(shí)例化一個(gè)GameObject,然后獲取這個(gè)GameObject的組件(通用做法是獲取GameObject后通過GetComponent來獲取),這里我們對(duì)Addressables進(jìn)行了擴(kuò)展以支持組件加載,具體方法參見our ComponentReference sample.

子資源 : 系統(tǒng)支持子資源的加載,但是需要特殊的語法,舉個(gè)潛在的例子,子資源包括,圖集中的大量圖片,或者FBX模型文件中的大量動(dòng)畫片段,如何直接加載這些圖篇,參考 our sprite loading sample

//加載所有的子資源:
Addressables.LoadAssetAsync<IList<Sprite>>("MySpriteSheetAddress");

//加載單個(gè)的子資源
Addressables.LoadAssetAsync<Sprite>("MySpriteSheetAddress[MySpriteName]");

資產(chǎn)中可用的名稱是在主Addressables組編輯器窗口中可見型诚。此外羹与,你可以使用一個(gè)AssetReference訪問資源的子對(duì)象,參考下方

使用AssetReference引用類

AssetReference類提供一種方法來訪問尋址的資源,而且無需知道他們的地址冠摄。要使用AssetReference類訪問一個(gè)可尋址的資源:

  1. 在你的Scene Hierarchy界面或者Project窗口中選擇一個(gè)資源
  2. 在Hierarchy中,為其添加組件(Add Component ),組件的類型為任何可序列化的組件都支持AssetReference變量引用(例如梗摇,游戲腳本拓哟、 ScriptableObject 或其他可序列化類)
  3. 在腳本中增加一個(gè)公開public的AssetReference變量
  4. 最后在Inspector窗口中,選擇一個(gè)可尋址的資源關(guān)聯(lián)起來,無論是將資源拖拽引用過來,或者是通過下拉菜單從預(yù)定義的可尋址資源中選擇一個(gè),都是可以的


    Addressable.png

加載或?qū)嵗粋€(gè)AssetRefAsseterence資源,可通過如下方法:

public class Z_Test : MonoBehaviour
{
    public AssetReference assetReference;
    // Start is called before the first frame update
    void Start()
    {
        assetReference.LoadAssetAsync<GameObject>();
    }
}

或者

public class Z_Test : MonoBehaviour
{
    public AssetReference assetReference;
    // Start is called before the first frame update
    void Start()
    {
        assetReference.InstantiateAsync(這里有2個(gè)重載,自己填參數(shù));
    }
}

和一般的資源尋址一樣,LoadAssetAsyncInstantiateAsync都是異步操作,你可以提供加載完成時(shí)的回調(diào)
子資源,如果AssetRefAsseterence中包含了子資源,(例如Sprite或FBX),你可以在該資源的Inspector面板中,AssetReference組件上看到多出了一個(gè)下拉框,下拉框中選擇你需要的是,引用資源本身,還是子類資源.第一個(gè)下拉框選擇的是資源本身,第二個(gè)下拉框,如果你選擇"<none>"的話, 那么資源引用還是指向資源該資源本身,此時(shí)可以獲取到所有的子類資源(例如assetReference.LoadAssetAsync<IList<Sprite>>()),但是如果第二個(gè)下拉框你選擇了某一個(gè)子資源,那么AssetRefAsseterence引用僅指向該子資源

構(gòu)建方面需要考慮的因素
在StreamingAssets中的本地?cái)?shù)據(jù)

可尋址資源系統(tǒng)需要一些文件取幫助識(shí)別一個(gè)資源在什么地方以及怎么取加載它,當(dāng)你構(gòu)建Addressables數(shù)據(jù)(即在Addressables窗口中Build Play Content)時(shí),這些文件會(huì)在StreamingAssets文件夾中生成,StreamingAssets是Unity中的一個(gè)特殊文件夾,它包括了所有的資源尋址信息.當(dāng)你構(gòu)建Addressables數(shù)據(jù)時(shí)候,系統(tǒng)級(jí)的文件會(huì)被生成在庫中,然后當(dāng)你構(gòu)建整個(gè)App時(shí),系統(tǒng)會(huì)拷貝這些所需要的文件到StreamingAssets文件夾中,構(gòu)建,然后再從庫中刪除它們,通過這種方式,你可以為多個(gè)平臺(tái)構(gòu)建每個(gè)平臺(tái)各自關(guān)聯(lián)需要的數(shù)據(jù).

除了特定的Addressables數(shù)據(jù),任何為本地使用構(gòu)建數(shù)據(jù)的組也將使用于特定平臺(tái)的位置,為了驗(yàn)證這項(xiàng)工作,去設(shè)置profile配置文件變量中的構(gòu)建路徑和加載路徑分別是[UnityEngine.AddressableAssets.Addressables.BuildPath]

{UnityEngine.AddressableAssets.Addressables.RuntimePath}
你可以在AddressableAssetSettings文件的Inspector面板中指定這些設(shè)置(默認(rèn)情況下,這個(gè)對(duì)象是位于項(xiàng)目的資產(chǎn)/AddressableAssetsData目錄)

預(yù)先下載

通過調(diào)用Addressables.DownloadDependenciesAsync()方法可以加載指定的address或label的依賴資源,通常情況下,這是一個(gè)assetbundle

通過AsyncOperationHandle的句柄和可以獲取PercentComplete屬性來幫助監(jiān)視和顯示當(dāng)前的下載進(jìn)度,你可以讓應(yīng)用等待直到所有內(nèi)容加載完畢

如果你想征求用戶同意后再下載,那么需要先獲取到下載資源的大小告訴用戶,通過
Addressables.GetDownloadSize()返回需要下載內(nèi)容的大小
注意:這需要考慮到之前下載的bundles是否仍然有緩存

雖然提前為應(yīng)用程序下載資源是有利的伶授,但在某些情況下断序,您可能會(huì)選擇不這樣做。 例如:

  • 如果你的應(yīng)用程序有大量的在線內(nèi)容谎砾,你通常希望用戶只與其中的一部分進(jìn)行交互逢倍。
  • 你有一個(gè)應(yīng)用程序必須連網(wǎng)下載,如果你的應(yīng)用程序的所有內(nèi)容都是小的bundle,你可以根據(jù)需要選擇下載,即按需下載

您可以使用預(yù)加載功能來顯示下載已經(jīng)啟動(dòng)捧颅,然后繼續(xù)景图,而不是使用完成百分比值等待內(nèi)容加載。 此實(shí)現(xiàn)將需要一個(gè)'加載或等待的屏幕'(即UI或交互效果)來處理資源仍在加載時(shí)的等待情況

為多平臺(tái)構(gòu)建

可尋址資源系統(tǒng)在你構(gòu)建可尋址資源內(nèi)容時(shí),其資源是依賴于平臺(tái)的,因而你必須為你每個(gè)需要支持的平臺(tái)進(jìn)行重新構(gòu)建

默認(rèn)情況下碉哑,建立Addressables應(yīng)用數(shù)據(jù)時(shí)挚币,您指定平臺(tái)的數(shù)據(jù)存儲(chǔ)在Addressables的特定平臺(tái)的目錄構(gòu)建路徑中.
運(yùn)行時(shí)路徑負(fù)責(zé)處理這些平臺(tái)文件夾指向適用的應(yīng)用程序數(shù)據(jù)。
注意:當(dāng)你在編輯器模式下使用Addressables BuildScriptPackedPlayMode,Addressables 將嘗試加載數(shù)據(jù)到你當(dāng)前選中的編輯器平臺(tái)(win或ios或mac等等).因此,如果你當(dāng)前構(gòu)建的目標(biāo)數(shù)據(jù)不是您當(dāng)前編輯器平臺(tái),那么兼容性可能會(huì)出現(xiàn)問題.要了解更多詳細(xì)信息,參考 Play mode scripts.

可尋址資源的開發(fā)周期

可尋址資源系統(tǒng)最重要的優(yōu)點(diǎn)之一就是將 準(zhǔn)備內(nèi)容 構(gòu)建內(nèi)容 和加載內(nèi)容 的關(guān)聯(lián)進(jìn)行解耦,事實(shí)上,在之前它們是緊密聯(lián)系在一起的

傳統(tǒng)的資源管理器

如果你之前是使用 Resources文件夾來存放資源的,那么它揮別內(nèi)置到應(yīng)用程序中,你必須通過Resources.Load的方式來加載內(nèi)容,需要提供路徑的資源.如果資源存放在別處,你會(huì)使用直接應(yīng)用或者AssetBundle來加載,如果你使用AssetBundle,你需要再次進(jìn)行路徑,打包以及組織加載策略,如果AssetBundle在遠(yuǎn)程服務(wù)器上,或者對(duì)其他的bundle具有依賴性,那么你必須通過寫代碼來管理下載,加載和卸載所有的Bundles

可尋址資源管理器

給資源一個(gè)'address'名稱作為尋址方式,你就可以使用該地址來加載他,不管它在你的項(xiàng)目中是如何構(gòu)建的,即使你更改這個(gè)資源的路徑名或者文件名都不會(huì)出現(xiàn)問題,你還可以從Resources文件夾中移動(dòng)該資源或者從構(gòu)建路徑,或者一些其他的生成位置(包括遠(yuǎn)程的),而不用去改變你的加載代碼

資源組模式(Asset group schemas)

模式定義了一組數(shù)據(jù),你可以增加模式到資源組中,附加到組的模式可以定義如何構(gòu)建改組中的內(nèi)容.舉個(gè)例子,當(dāng)你構(gòu)建打包模式時(shí),將附加有BundledAssetGroupSchema模式的組用作資產(chǎn)的源,您可以將模式集組合到用于定義新組的模板中
你也可以通過AddressableAssetsSettings添加資源組模式

構(gòu)建腳本

構(gòu)建腳本是一個(gè)繼承了[IDataBuilder] 接口的[ScriptableObject]可編輯腳本資源.
用戶可以自己創(chuàng)建構(gòu)建腳本然后在AddressableAssetSettings的Inspector面板中添加它們.
通過Addressables窗口,即(Window > AssetManagement > Addressables)中選擇Play Mode Script,然后選擇下拉選項(xiàng),當(dāng)前扣典,已實(shí)現(xiàn)三個(gè)腳本來支持完整的應(yīng)用程序構(gòu)建妆毕,并提供三個(gè)用于在編輯器中進(jìn)行迭代的播放模式腳本。

播放模式腳本

Addressable Assets Package默認(rèn)自帶了三個(gè)構(gòu)建腳本贮尖,這些腳本創(chuàng)建播放模式數(shù)據(jù)以幫助您加速應(yīng)用程序開發(fā)笛粘。

  • Fast mode
    快速模式(BuildScriptFastMode)使您可以在游戲流程中快速運(yùn)行游戲。快速模式直接通過資產(chǎn)數(shù)據(jù)庫加載資產(chǎn)薪前,無需分析或創(chuàng)建AssetBundle即可進(jìn)行快速迭代润努。

  • Virtual mode
    虛擬模式(BuildScriptVirtualMode)可以分析內(nèi)容的布局和依賴性,而無需創(chuàng)建AssetBundle示括。資源通過ResourceManager從資源數(shù)據(jù)庫加載铺浇,就好像它們是通過Bundles加載一樣。查看游戲過程中Bundles何時(shí)加載或卸載,通過Addressable Profiler window(Window > Asset Management > Addressable Profiler)
    虛擬模式可幫助您模擬負(fù)載策略并調(diào)整內(nèi)容組以找到生產(chǎn)版本的適當(dāng)平衡垛膝。

  • Packed Play mode
    打包播放模式(BuildScriptPackedPlayMode)使用已構(gòu)建的Bundles鳍侣。此模式與已部署的應(yīng)用程序構(gòu)建最匹配,但是需要你進(jìn)行一步單獨(dú)的數(shù)據(jù)構(gòu)建過程.如果你不修改資源,則此模式是最快的,因?yàn)樗谶M(jìn)入"Play"模式時(shí)是不會(huì)處理任何數(shù)據(jù)的,你必須通過Addressables window (Window > Asset Management > Addressables) 然后選擇Build > Build Player Content,或者可以使用如下代碼進(jìn)行代碼打包:

AddressableAssetSettings.BuildPlayerContent()
選擇編譯腳本

要改變播放模式腳本,請(qǐng)通過Addressables window (Window > Asset Management > Addressables) ,選擇Play Mode Script的下拉框來選擇,在開發(fā)和部署期間,每種模式都有特定的使用時(shí)間和地點(diǎn)吼拥。下表說明了開發(fā)周期的各個(gè)階段倚聚,在這些階段中,特定的模式很有用凿可。

播放模式

分析和調(diào)試

默認(rèn)情況下,可尋址資源只有warnings日志和error錯(cuò)誤日志,你可以開啟更加詳細(xì)的detailed logging,通過Edit > Project Settings > Player,然后到Other Settings > Configuration,增加預(yù)定義宏ADDRESSABLES_LOG_ALL

你可以通過不勾選AddressableAssetSettings面板中的Log Runtime Exceptions來禁用異常捕捉,你可以通過繼承ResourceManager.ExceptionHandler屬性實(shí)現(xiàn)自己的異常捕捉機(jī)制,但是這必須在Addressables運(yùn)行時(shí)初始化成功后

初始化

您可以將對(duì)象附加到“可尋址資源”設(shè)置秉沼,然后在運(yùn)行時(shí)進(jìn)行初始化過程。
CacheInitializationSettings對(duì)象控制Unity在運(yùn)行時(shí)的緩存API,創(chuàng)建自己的初始化對(duì)象,創(chuàng)建一個(gè)可編程對(duì)象ScriptableObject 繼承 IObjectInitializationDataProvider接口,這是系統(tǒng)的編輯器組件矿酵,負(fù)責(zé)創(chuàng)建ObjectInitializationData在運(yùn)行時(shí)序列化數(shù)據(jù)

內(nèi)容更新工作流

Unity建議將游戲內(nèi)容分為兩類:

  • Static靜態(tài)內(nèi)容,你可以不用考慮更新
  • Dynamic動(dòng)態(tài)內(nèi)容,你期望更新的內(nèi)容

在這種結(jié)構(gòu)中唬复,靜態(tài)內(nèi)容隨應(yīng)用程序一起提供(或在安裝后立即下載),并且駐留在很少的Bundles。動(dòng)態(tài)內(nèi)容駐留在線上服務(wù)器全肮,最好放在較小的包中敞咧,以最大程度地減少每次更新所需的數(shù)據(jù)量」枷伲可尋址資源系統(tǒng)的目標(biāo)之一是使這種結(jié)構(gòu)易于使用和修改休建,而無需更改腳本。

但是评疗,"可尋址資源系統(tǒng)"也允許適當(dāng)?shù)母淖兒驼{(diào)整"Static"內(nèi)容,當(dāng)你不想要發(fā)布整個(gè)新的應(yīng)用構(gòu)建時(shí)测砂。

它是怎樣工作的

Addressables 使用內(nèi)容目錄將地址映射到每個(gè)資產(chǎn),以及指定從什么地方加載它,為了提供修改映射的支持,你的原始應(yīng)用必須知道該目錄的在線副本,需要打開這個(gè),在AddressableAssetSettings的inspector的面板中打開Build Remote Catalog選項(xiàng),這可以確保從指定的路徑拷貝和加載該目錄副本,應(yīng)用發(fā)布后,此加載路徑就無法更改。內(nèi)容更新過程將創(chuàng)建目錄的新版本(具有相同的文件名),以覆蓋先前指定的加載路徑上的文件百匆。

構(gòu)建應(yīng)用程序會(huì)生成一個(gè)唯一的應(yīng)用程序內(nèi)容版本字符串砌些,該字符串標(biāo)識(shí)每個(gè)應(yīng)用程序應(yīng)加載的內(nèi)容目錄。給定的服務(wù)器可以包含應(yīng)用程序多個(gè)版本的目錄加匈,而不會(huì)發(fā)生沖突存璃。我們將所需的數(shù)據(jù)存儲(chǔ)在addressables_content_state.bin文件中。包括版本字符串雕拼,以及標(biāo)記為StaticContent的組中包含的任何資產(chǎn)的哈希信息纵东。默認(rèn)情況下,此文件與AddressableAssetSettings.asset文件位于同一文件夾中啥寇。

addressables_content_state.bin文件包含Addressables系統(tǒng)中每個(gè)StaticContent資源組的哈希和依賴項(xiàng)信息偎球。構(gòu)建到StreamingAssets文件夾的所有組都應(yīng)標(biāo)記為StaticContent洒扎,盡管大型遠(yuǎn)程組也可以從該名稱中受益。在下一步(準(zhǔn)備進(jìn)行內(nèi)容更新)期間衰絮,此哈希信息確定任何StaticContent組是否包含已更改的資源逊笆,以及是否需要將這些資產(chǎn)移至其他位置。

準(zhǔn)備更新內(nèi)容

如果您在任何 StaticContent 組中修改了資源岂傲,則需要運(yùn)行 Prepare For Content Update 命令难裆。 這將把任何已修改的資產(chǎn)從靜態(tài)組中移出,并將它們移動(dòng)到新組中镊掖。 產(chǎn)生新的資源組合:

  1. 打開Addressables window (Window > Asset Management > Addressables)
  2. Addressables window窗口中,選擇Build菜單,然后選擇Prepare For Content Update
  3. Build Data File對(duì)話框打開時(shí),選擇addressables_content_state.bin文件 (默認(rèn)情況下,位置在Assets/AddressableAssetsData)

此數(shù)據(jù)用于確定自上次構(gòu)建應(yīng)用程序以來已修改了哪些資產(chǎn)或依賴項(xiàng)乃戈。系統(tǒng)將這些資產(chǎn)移至新的組,以準(zhǔn)備內(nèi)容更新構(gòu)建亩进。

注意:如果所有更改都限于非靜態(tài)組症虑,則此命令將不執(zhí)行任何操作。

重要信息:在運(yùn)行準(zhǔn)備操作之前归薛,Unity建議分支您的版本控制系統(tǒng)谍憔。準(zhǔn)備操作以適合于更新內(nèi)容的方式重新排列資產(chǎn)組。分支機(jī)構(gòu)確保下次發(fā)送新播放器時(shí)主籍,您可以返回到首選的內(nèi)容安排习贫。

用于內(nèi)容更新的構(gòu)建
  1. 打開Addressables window (Window > Asset Management > Addressables)
  2. Addressables window窗口中,選擇Build菜單,然后選擇Build For Content Update
  3. Build Data File對(duì)話框打開時(shí),選擇Build文件夾下已經(jīng)存在需要被當(dāng)前替換的應(yīng)用構(gòu)建文件,構(gòu)建文件夾中必須包含 addressables_content_state.bin 文件

構(gòu)建生成內(nèi)容目錄時(shí),會(huì)有一個(gè)hash文件和assetbundles

生成的內(nèi)容目錄與所選應(yīng)用程序構(gòu)建中的目錄名稱相同,將覆蓋舊的目錄和哈希文件千元。應(yīng)用程序加載哈希文件以確定是否有新的目錄可用苫昌。系統(tǒng)從應(yīng)用程序附帶的或已經(jīng)下載的現(xiàn)有捆綁包中加載未修改的資源

系統(tǒng)通過使用addressables_content_state.bin文件中的內(nèi)容版本信息和位置信息去創(chuàng)建AssetBundles,不包含更新的Assetbundle則與之前的同名,不發(fā)生變化,如果Assetbundle包含更新的內(nèi)容,則生成包含更新內(nèi)容的新Assetbundle,使用一個(gè)新的文件,以便它可以與原來的文件共存,新文件名的Assetbundle必須復(fù)制到承載內(nèi)容的位置(新的內(nèi)容追加到老文件的承載位置)

系統(tǒng)還為靜態(tài)內(nèi)容構(gòu)建資源包幸海,但是你不需要將它們上傳到內(nèi)容托管位置祟身,因?yàn)闆]有 Addressables 資源引用它們。

內(nèi)容更新例子

在這個(gè)例子中物独,一個(gè)已發(fā)布的應(yīng)用程序知道以下組:


由于這個(gè)版本是實(shí)時(shí)的袜硫,有些玩家在他們的設(shè)備上有 Local static,并且有可能在本地緩存這兩個(gè)遠(yuǎn)程捆綁包中的一個(gè)或者兩個(gè)挡篓。
如果你修改了每個(gè)組中的一個(gè)資產(chǎn)(AssetA婉陷,AssetL 和 AssetX) ,然后運(yùn)行Prepare For Content Update瞻凤,在本地 Addressable 設(shè)置中的結(jié)果現(xiàn)在是:

請(qǐng)注意憨攒,prepare 操作實(shí)際上編輯了靜態(tài)組世杀,這可能看起來有悖直覺阀参。 但是,關(guān)鍵是系統(tǒng)構(gòu)建了上面的布局瞻坝,但是丟棄了任何靜態(tài)組的構(gòu)建結(jié)果蛛壳。 因此杏瞻,從玩家的角度來看,你最終會(huì)得到以下結(jié)果:

Local_Static已經(jīng)在播放器設(shè)備上衙荐,不能更改捞挥。 這個(gè)舊版本的 AssetA 不再被引用。 相反忧吟,它被作為死數(shù)據(jù)粘在播放器設(shè)備上砌函。

Remote_Static沒有改變。 如果它還沒有緩存在播放器的設(shè)備上溜族,它會(huì)在請(qǐng)求 AssetM 或 AssetN 時(shí)下載讹俊。 像 AssetA 一樣,這個(gè)舊版本的 AssetL 不再被引用煌抒。

Remotenonstatic 包現(xiàn)在已經(jīng)舊了仍劈。 你可以從服務(wù)器上刪除它,但是無論如何寡壮,從現(xiàn)在開始它都不會(huì)被下載贩疙。 如果被緩存,它最終會(huì)離開緩存况既。 像 AssetA 和 AssetL 一樣这溅,這個(gè)舊版本的 AssetX 不再被引用。

舊的 Remote nonstatic 包被替換為新版本棒仍,其特征是它的散列文件芍躏。 Assetx 的修改版本使用這個(gè)新包進(jìn)行更新。

content_update_group由接下來的過程中修改過的引用資產(chǎn)組成降狠。

請(qǐng)注意对竣,上面的例子有以下含義:

  1. 任何已更改的本地資產(chǎn)將永遠(yuǎn)不會(huì)在用戶的設(shè)備上使用。
  2. 如果用戶已經(jīng)緩存了一個(gè)nonstatic非靜態(tài)包榜配,那么他們將需要重新下載該包否纬,包括未更改的資產(chǎn)(例如,在這個(gè)實(shí)例中蛋褥,AssetYAssetZ)临燃。 理想情況下,用戶沒有緩存包烙心,在這種情況下膜廊,他們只需要下載新的 Remote nonstatic 包。
  3. 如果用戶已經(jīng)緩存了 Remote Static遠(yuǎn)程靜態(tài)包淫茵,那么他們只需要下載更新的資產(chǎn)(在這個(gè)實(shí)例中爪瓜,是content update group中的AssetL )。 在這種情況下匙瘪,這是理想的铆铆。 如果用戶沒有緩存該捆綁包蝶缀,他們必須通過content update group下載新 AssetL,以及通過未觸及的 Remote static 捆綁包下載現(xiàn)已失效的 AssetL薄货。 無論初始緩存狀態(tài)如何翁都,在某個(gè)時(shí)刻,用戶的設(shè)備上都會(huì)有不存在的 AssetL谅猾,盡管它從未被訪問過柄慰,但它會(huì)無限期地緩存下去

資源的主機(jī)服務(wù)

概覽

資源主機(jī)服務(wù)是通過Unity Editor環(huán)境為可尋址資源配置資源打包到本地或者遠(yuǎn)程服務(wù)器的綜合服務(wù),當(dāng)測(cè)試打包的內(nèi)容時(shí),資源的主機(jī)服務(wù)的設(shè)計(jì)目的是為了提高迭代速度,也可以用來向本地和遠(yuǎn)程網(wǎng)絡(luò)連接的客戶端提供內(nèi)容税娜。

打包模式測(cè)試和迭代

從編輯播放模式測(cè)試轉(zhuǎn)移到平臺(tái)應(yīng)用程序構(gòu)建測(cè)試給開發(fā)過程帶來了復(fù)雜性和時(shí)間成本,主機(jī)服務(wù)提供可擴(kuò)展的編輯器內(nèi)嵌的內(nèi)容傳遞服務(wù)先煎,這些服務(wù)直接映射到 Addressables 組配置,使用自定義 Addressables 配置文件,您可以快速配置應(yīng)用程序巧涧,從 Unity 編輯器本身加載所有內(nèi)容薯蝎。這包括部署和構(gòu)建到移動(dòng)設(shè)備或任何其他平臺(tái),這些移動(dòng)設(shè)備或平臺(tái)具有對(duì)開發(fā)系統(tǒng)的網(wǎng)絡(luò)訪問權(quán)限谤绳。

Turn-Key(打開,配置) 內(nèi)容服務(wù)

您可以將 Asset Hosting Services 部署到服務(wù)器環(huán)境中占锯,方法是以批處理模式(headless)運(yùn)行,為面向內(nèi)部網(wǎng)和因特網(wǎng)的 Unity 應(yīng)用程序客戶機(jī)托管內(nèi)容缩筛。

配置

本文詳細(xì)介紹了項(xiàng)目資產(chǎn)托管服務(wù)的初始設(shè)置消略。 雖然安裝指南側(cè)重于編輯器工作流,但是您可以通過設(shè)置 AddressableAssetSettings 類的 HostingServicesManager 屬性使用 API 來配置托管服務(wù)瞎抛。

配置新的主機(jī)服務(wù)

使用 Hosting window 添加艺演、配置和啟用新的 Hosting 服務(wù)。 在編輯器中桐臊,選擇 Window > Asset Management > Hosting Services胎撤,或者單擊 Addressables window菜單中的 Hosting 按鈕以訪問 Hosting window。

要添加新的托管服務(wù)断凶,請(qǐng)單擊Add Service按鈕伤提。

在出現(xiàn)的Add Service對(duì)話框中,可以選擇預(yù)定義的服務(wù)類型或定義自定義服務(wù)類型认烁。 若要使用預(yù)定義的服務(wù)類型肿男,請(qǐng)從Service Type下拉選項(xiàng)中選擇。 使用Descriptive Name字段為服務(wù)輸入名稱却嗡。

注意: 有關(guān)實(shí)現(xiàn)自定義托管服務(wù)類型的更多信息舶沛,請(qǐng)參見關(guān)于自定義服務(wù)的部分。

新添加的服務(wù)出現(xiàn)在Hosting windowHosting Services 部分窗价,默認(rèn)為禁用狀態(tài)如庭。 要啟動(dòng)服務(wù),請(qǐng)單擊Enable Service按鈕舌镶。

Http 主機(jī)服務(wù)啟動(dòng)時(shí)會(huì)自動(dòng)分配一個(gè)端口號(hào)柱彻。 端口號(hào)在 Unity 會(huì)話之間保存和重用豪娜。 要選擇不同的端口餐胀,可以在 Port 字段中分配特定的端口號(hào)技掏,或者使用 Reset 按鈕隨機(jī)分配不同的端口扼褪。
注意: 如果重置端口號(hào),則必須執(zhí)行完整的應(yīng)用程序構(gòu)建,以生成并嵌入正確的 URL坚芜。
現(xiàn)在已經(jīng)啟用了 HTTP 主機(jī)服務(wù),可以為每個(gè)資產(chǎn)組的 BuildPath 中指定的目錄提供內(nèi)容睹耐。

Profile文件配置

在開發(fā)過程中使用 Hosting Services 時(shí)糊探,Unity 建議創(chuàng)建一個(gè)Profile配置文件,將所有資產(chǎn)組配置為使用專門為此目的創(chuàng)建的目錄或從 Hosting Service 加載內(nèi)容扣汪。

Addressables window窗口中選擇(Window > Asset Management > Addressables),選擇Profiles > Inspect Profile Settings,您還可以通過 addresssableassetsettings 訪問這些設(shè)置断楷。

接下來,創(chuàng)建一個(gè)新的Profile文件,如下面的例子,將profile的命名為"Editor Hosted"


修改加載路徑字段,改為從主機(jī)服務(wù)加載崭别。 Httphostingservice 是一個(gè)指定服務(wù)器本地 IP 地址和端口的URL冬筒。 在 Hosting 窗口中,您可以使用名為 PrivateIpAddress 和 HostingServicePort 的配置文件變量來構(gòu)造 URL (例如茅主,http: / / [ PrivateIpAddress ] : [ HostingServicePort ])舞痰。

此外,您應(yīng)該修改所有生成路徑變量诀姚,以指向 Project 的 Assets 文件夾外的一個(gè)公共目錄响牛。

驗(yàn)證每個(gè)組的配置是否正確。 確保它們各自的配置文件profile的 BuildPathLoadPath 路徑都被正確設(shè)置 赫段,這些配置文件profile 信息是針對(duì)對(duì)應(yīng)的主機(jī)服務(wù)所作出的修改呀打。 在這個(gè)示例中,您可以看到如何展開 LoadPath 中的配置文件變量糯笙,以構(gòu)建正確的基礎(chǔ) URL聚磺,從而從 Hosted Services 加載。

最后炬丸,從 Addressables window 選擇新的配置文件瘫寝,新建一個(gè)構(gòu)建,并部署到目標(biāo)設(shè)備稠炬。 Unity 編輯器現(xiàn)在通過 HttpHostingService 服務(wù)處理來自應(yīng)用程序的所有加載請(qǐng)求焕阿。 現(xiàn)在不需要重新部署就可以對(duì)內(nèi)容進(jìn)行添加和更改。 重新構(gòu)建可尋址內(nèi)容首启,并重新啟動(dòng)已部署的應(yīng)用程序以刷新內(nèi)容暮屡。

批處理模式

您還可以使用批處理模式從Unity編輯器啟動(dòng)主機(jī)服務(wù)。 要做到這一點(diǎn)毅桃,從命令行啟動(dòng) Unity褒纲,使用以下選項(xiàng):
-batchMode -executeMethod UnityEditor.AddressableAssets.HostingServicesManager.BatchMode

這將從默認(rèn)的addresssableassetsettings 對(duì)象加載 Hosting Services 配置准夷,并啟動(dòng)所有已配置的服務(wù)。

要使用可選的 addresssableassetsettings 配置莺掠,請(qǐng)創(chuàng)建自己的靜態(tài)方法入口點(diǎn)衫嵌,通過
UnityEditor.AddressableAssets.HostingServicesManager.BatchMode(AddressableAssetSettings settings)重載。

自定義服務(wù)

主機(jī)服務(wù)設(shè)計(jì)為可擴(kuò)展的彻秆,允許您實(shí)現(xiàn)自己的自定義邏輯楔绞,以便從 可尋址資源系統(tǒng) 加載請(qǐng)求服務(wù)內(nèi)容。 例如:

  • 支持使用非 http 協(xié)議下載內(nèi)容的自定義 IResourceProvider唇兑。
  • 管理一個(gè)外部?jī)?nèi)容服務(wù)進(jìn)程酒朵,提供 CDN 匹配的解決方案(例如 Apache HTTP Server 文檔)。
實(shí)現(xiàn)自定義服務(wù)

Hostingservicesmanager 可以管理任何實(shí)現(xiàn) IHostingService 接口的類(有關(guān)方法參數(shù)和返回值的詳細(xì)信息扎附,請(qǐng)參閱 API documentation.

創(chuàng)建一個(gè)新的自定義服務(wù)

  1. 按照上面配置新的主機(jī)服務(wù)部分中概述的步驟來訪問添加服務(wù)對(duì)話框蔫耽。
  2. 選擇 Custom,然后將適用的腳本拖放到字段中留夜,或者從對(duì)象選擇器中選擇它匙铡。 該對(duì)話框驗(yàn)證所選腳本是否實(shí)現(xiàn) IHostingService 接口。
  3. 完成添加服務(wù)香伴,點(diǎn)擊添加按鈕慰枕。

下一步,您的自定義服務(wù)將出現(xiàn)在服務(wù)類型下拉選項(xiàng)中即纲。

內(nèi)存管理

鏡像加載和卸載

當(dāng)使用 Addressable Assets 時(shí)具帮,確保正確內(nèi)存管理的主要方法是鏡像正確地加載和卸載。 這取決于您的資產(chǎn)類型和加載方法低斋。 在任何情況下蜂厅,釋放方法都可以接受已加載的資產(chǎn)或是由加載返回的操作句柄。 例如膊畴,在場(chǎng)景創(chuàng)建期間(如下所述) 掘猿,加載將返回一個(gè) AsyncOperationHandle<Sceneinstance>,您可以通過這個(gè)返回的句柄釋放這個(gè) AsyncOperationHandle sceneinstance唇跨,或者通過handle.Result來釋放 稠通。 結(jié)果(在本例中為 SceneInstance)。

資源加載

通過Addressables.LoadAssetAsync 或者 Addressables.LoadAssetsAsync 來加載資源.
這會(huì)將資源加載到內(nèi)存中买猖,而不會(huì)將其實(shí)例化改橘。 每次執(zhí)行加載調(diào)用時(shí),它都會(huì)為每個(gè)加載的資源的引用計(jì)數(shù)ref-count加1,如果對(duì)同個(gè)資產(chǎn)調(diào)用 LoadAssetAsync 三次玉控,則會(huì)得到 AsyncOperationHandle 結(jié)構(gòu)的三個(gè)引用實(shí)例但是它們都指向相同的底層,該操作對(duì)相應(yīng)資源的的參考計(jì)數(shù)為3飞主。如果加載成功,那么生成的AsyncOperationHandle結(jié)構(gòu)將包含.Result屬性.你也您可以使用 Unity 的內(nèi)置實(shí)例化方法來實(shí)例化加載資源,但是該方法不會(huì)增加資源的引用數(shù)ref-count。

若要卸載資源碌识,請(qǐng)使用 Addressables.Release 方法碾篡,該方法將 ref-count 遞減。 當(dāng)給定資源的 ref-count 為零時(shí)筏餐,就可以卸載該資源开泽,并減少任何依賴項(xiàng)的ref-count引用。

注意: 資產(chǎn)可能會(huì)也可能不會(huì)立即卸載胖烛,這取決于現(xiàn)有的依賴項(xiàng)眼姐。 有關(guān)更多信息诅迷,請(qǐng)閱讀關(guān)于何時(shí)清除內(nèi)存的部分佩番。 when memory is cleared

場(chǎng)景加載

要加載一個(gè)場(chǎng)景,請(qǐng)使用Addressables.LoadSceneAsync. 您可以使用此方法在Single單模式下加載場(chǎng)景罢杉,該模式將關(guān)閉所有打開的場(chǎng)景趟畏,或者在Additive追加模式下加載場(chǎng)景(更多信息,請(qǐng)參閱場(chǎng)景模式加載文檔 Scene mode loading)滩租。

若要卸載一個(gè)場(chǎng)景赋秀,請(qǐng)使用Addressables.UnloadSceneAsync。 或者在Single模式下打開一個(gè)新的場(chǎng)景,那么會(huì)自動(dòng)卸載其他場(chǎng)景.你可以使用 Addressables 的接口或者 SceneManager.LoadSceneSceneManager.LoadSceneAsync, 打開一個(gè)新場(chǎng)景并會(huì)自動(dòng)關(guān)閉當(dāng)前場(chǎng)景律想,引用計(jì)數(shù)ref-count也會(huì)適當(dāng)?shù)剡f減猎莲。

游戲?qū)ο蟮膶?shí)例化

要加載和實(shí)例化一個(gè)游戲?qū)ο筚Y產(chǎn),使用 Addressables.InstantiateAsync 實(shí)例化技即。 這實(shí)例化位于指定location參數(shù)的預(yù)置, Addressables系統(tǒng)將加載預(yù)置及其依賴項(xiàng)著洼,并遞增所有相關(guān)資源的引用計(jì)數(shù)ref-count。

在同一地址上三次調(diào)用 InstantiateAsync 將導(dǎo)致所有依賴資產(chǎn)的 ref-count 為3而叼。 但是身笤,與三次調(diào)用 LoadAssetAsync 不同的是,每次InstantiateAsync異步調(diào)用都返回一個(gè) AsyncOperationHandle,它指向一個(gè)唯一的操作葵陵。 這是因?yàn)槊總€(gè)實(shí)例化 InstantiateAsync的結(jié)果都是唯一的實(shí)例液荸。 Instanateasync 和其他加載調(diào)用之間的另一個(gè)區(qū)別是可選的 trackHandle 參數(shù)(追蹤handle)。 當(dāng)設(shè)置為 false 時(shí)脱篙,在釋放實(shí)例時(shí)娇钱,必須保留 AsyncOperationHandle。 這樣更有效率绊困,但是需要更多的開發(fā)工作文搂。

要銷毀毀一個(gè)實(shí)例化的游戲?qū)ο螅?qǐng)使用 Addressables.ReleaseInstance考抄,或關(guān)閉包含實(shí)例化對(duì)象的場(chǎng)景细疚。 你可以使用 Addressables 的接口或者 SceneManager.LoadSceneSceneManager.LoadSceneAsync, 打開一個(gè)新場(chǎng)景并會(huì)自動(dòng)關(guān)閉當(dāng)前場(chǎng)景。 如上所述,如果將 trackHandle 設(shè)置為false疯兼,則只能調(diào)用 Addressables.ReleaseInstance然遏,而不是實(shí)際的游戲?qū)ο驡ameObject。

注意: 如果您調(diào)用 Addressables.ReleaseInstance吧彪。 在一個(gè)沒有使用 Addressables API 創(chuàng)建,或者創(chuàng)建時(shí)將 trackHandle設(shè)置未false了,系統(tǒng)會(huì)返回 false待侵,以指示該方法無法釋放指定的實(shí)例。 在這種情況下姨裸,實(shí)例不會(huì)被銷毀

Addressables.InstantiateAsync有一些相關(guān)的開銷,所以如果你需要每幀實(shí)例化相同的對(duì)象幾百次,考慮通過 Addressables API 加載,然后通過其他方法實(shí)例化,你可以調(diào)用 Addressables. LoadAssetAsync,然后對(duì)返回結(jié)果調(diào)用 GameObject.Instantiate(),這允許靈活地以同步方式調(diào)用 Instanate,缺點(diǎn)是 Addressables 系統(tǒng)不知道您創(chuàng)建了多少實(shí)例秧倾,如果管理不當(dāng),可能會(huì)導(dǎo)致內(nèi)存問題.例如傀缩,引用紋理的預(yù)置將不再具有可供引用的有效加載紋理那先,從而導(dǎo)致內(nèi)存泄漏問題(或更糟)。 這些類型的問題可能很難跟蹤赡艰,因?yàn)槟赡懿粫?huì)立即觸發(fā)內(nèi)存卸載(請(qǐng)參閱下面關(guān)于清除內(nèi)存的小節(jié)clearing memory

數(shù)據(jù)加載

你不需要使用AsyncOperationHandle.Result來釋放物體,但仍然需要它們本身才能被釋放(這段我也不清楚它在說什么)
舉個(gè)例子可以是Addressables.LoadResourceLocationsAsyncAddressables.GetDownloadSizeAsync
你可以訪問這些數(shù)據(jù),直到它們被釋放.請(qǐng)使用Addressables.Release操作來釋放

后臺(tái)交互

不返回 AsyncOperationHandle 中任何內(nèi)容的操作售淡。 結(jié)果字段具有一個(gè)可選參數(shù),用于在完成時(shí)自動(dòng)釋放操作句柄,如果在操作完成后不再需要這些操作句柄之一慷垮,請(qǐng)將 autoReleaseHandle 參數(shù)設(shè)置為 true揖闸,以確保清除操作句柄,如果需要在操作句柄完成后檢查操作句柄的狀態(tài),則希望 autoReleaseHandle 為 false料身。 這些接口的例子是 Addressables.DownloadDependenciesAsyncAddressables.UnloadScene

Addressable Profiler配置文件

使用 Addressable Profiler 窗口監(jiān)視所有 Addressables 系統(tǒng)操作的引用計(jì)數(shù)汤纸。 訪問“編輯器”中的窗口,
Window > Asset Management > Addressable Profiler

重要:為了查看探查器中的數(shù)據(jù),必須在 Addresssableassetsettings 對(duì)象的檢視面板中啟用 Send Profiler Events 設(shè)置芹血。

分析器提供了以下信息:

  • 白色垂直線表示發(fā)生加載請(qǐng)求的幀贮泞。
  • 藍(lán)色背景表示當(dāng)前加載的資源。
  • 圖中綠色部分表示資產(chǎn)的當(dāng)前引用計(jì)數(shù)祟牲。
內(nèi)存何時(shí)清空

不再被引用的資源(在探查器的藍(lán)色部分的末尾顯示)并不一定意味著已經(jīng)卸載了該資源隙畜。 一個(gè)常見的適用場(chǎng)景涉及一個(gè)資源包中的多個(gè)資產(chǎn)。 例如:

  • 在一個(gè)資產(chǎn)組合'stuff'中有三個(gè)資產(chǎn)(樹tree说贝、坦克tank,和牛cow)议惰。
  • 當(dāng)加載樹tree時(shí),分析器將顯示一個(gè)樹tree引用計(jì)數(shù)乡恕,一個(gè)stuff引用計(jì)數(shù)言询。
  • 稍后,當(dāng) tank 加載時(shí)傲宜,分析器將同時(shí)顯示一個(gè) tree 和 tank 的參考計(jì)數(shù)运杭,以及兩個(gè) stuff 的參考計(jì)數(shù)
  • 如果釋放 tree,它的 ref-count 變?yōu)榱愫洌⑶宜{(lán)條消失辆憔。

在這個(gè)示例中,此時(shí)實(shí)際上并沒有卸載樹資源。 您可以加載資源包或其部分內(nèi)容虱咧,但不能部分卸載資產(chǎn)包.
在stuff本身完全卸載之前熊榛,其中的任何資源都不會(huì)卸載。 但是有一個(gè)例外Resources.UnloadUnusedAssets,
在上述場(chǎng)景中執(zhí)行此方法將導(dǎo)致 tree 卸載腕巡。 因?yàn)?Addressables 系統(tǒng)無法知道這些事件,profiler圖形監(jiān)視器僅反映 Addressables ref-counts (不完全反映內(nèi)存所擁有的內(nèi)容)玄坦。 注意,如果您選擇使用Resources.UnloadUnusedAssets來手動(dòng)釋放資源绘沉,這是一個(gè)非常慢的操作,并且應(yīng)該只在不會(huì)顯示任何故障的屏幕上調(diào)用(即最好是在加載時(shí)調(diào)用,不然可能存在游戲過程中的掉幀卡幀)煎楣。

異步處理句柄

一些Addressables API方法會(huì)返回一個(gè)AsyncOperationHandle句柄,句柄最主要的目的是允許訪問當(dāng)前異步的的狀態(tài)和曹祖哦的結(jié)果,句柄信息知道你調(diào)用Addressables.Release或者Addressables.ReleaseInstance都是有效的.獲取更多的相關(guān)信息,參見memory management.

當(dāng)操作完成之后,AsyncOperationHandle.Status屬性將會(huì)返回AsyncOperationStatus.Succeeded或者AsyncOperationStatus.Failed,如果操作是成功的,那么將會(huì)得到一個(gè)AsyncOperationHandle.Result結(jié)果屬性
你可以定期檢查操作狀態(tài),或者注冊(cè)一個(gè)完成時(shí)的回調(diào)事件AsyncOperationHandle.Complete,當(dāng)你不再需要提供返回AsyncOperationHandle,你應(yīng)該使用Addressables.Release方法來釋放

有類型(確定類型)的句柄 對(duì)比 無類型句柄

大多數(shù) Addressables API 方法返回一個(gè)一般的 AsyncOperationHandle<T>泛型結(jié)構(gòu),允許 AsyncOperationHandle.Completed 事件的類型安全,以及確切的AsyncOperationHandle.Result對(duì)象的類型,還有一個(gè)非通用的 AsyncOperationHandle 結(jié)構(gòu),你可以根據(jù)需要在兩個(gè)句柄之間進(jìn)行轉(zhuǎn)換

請(qǐng)注意,如果嘗試將非泛型句柄強(qiáng)制轉(zhuǎn)換為不正確類型的泛型句柄车伞,則會(huì)發(fā)生運(yùn)行時(shí)異常择懂。 例如

AsyncOperationHandle<Texture2D> textureHandle = Addressables.LoadAssetAsync<Texture2D>("mytexture");

// Convert the AsyncOperationHandle<Texture2D> to an AsyncOperationHandle:
AsyncOperationHandle nonGenericHandle = textureHandle;

// Convert the AsyncOperationHandle to an AsyncOperationHandle<Texture2D>:
AsyncOperationHandle<Texture2D> textureHandle2 = nonGenericHandle.Convert<Texture2D>();

// This will throw and exception because Texture2D is required:
AsyncOperationHandle<Texture> textureHandle3 = nonGenericHandle.Convert<Texture>();

AsyncOperationHandle 使用案例
void Start() {
    //注冊(cè)完成時(shí)的回調(diào)函數(shù)
    AsyncOperationHandle<Texture2D> textureHandle = Addressables.LoadAsset<Texture2D>("mytexture");
    textureHandle.Completed += TextureHandle_Completed;
}

private void TextureHandle_Completed(AsyncOperationHandle<Texture2D> handle) {
    if (handle.Status == AsyncOperationStatus.Succeeded) {
        Texture2D result = handle.Result;
        // The texture is ready for use.
    }
}

Asyncoperationhandle 繼承自 IEnumerator,因此可以在協(xié)同程序中生成它

public IEnumerator Start() {
    AsyncOperationHandle<Texture2D> handle = Addressables.LoadAssetAsync<Texture2D>("mytexture");
    yield return handle;
    if (handle.Status == AsyncOperationStatus.Succeeded) {
        Texture2D texture = handle.Result;
        // The texture is ready for use.
        // ...
        // Release the asset after its use:
        Addressables.Release(handle);
    }
}

Addressables 還支持異步操作await,通過AsyncOperationHandle.Task屬性:

public async Start() {
    AsyncOperationHandle<Texture2D> handle = Addressables.LoadAssetAsync<Texture2D>("mytexture");
    await handle.Task;
    // The task is complete. Be sure to check the Status is successful before storing the Result.
}

注意:
用 SceneManager.LoadSceneAsync加載場(chǎng)景并將allowSceneActivation屬性設(shè)置為false時(shí),或使用 Addressables.LoadSceneAsync并將activateOnLoad 設(shè)置為false時(shí).可能會(huì)導(dǎo)致后續(xù)的異步操作被阻塞無法完成,詳情請(qǐng)參見allowSceneActivation

自定義操作

IResourceProvider API 允許您通過以數(shù)據(jù)驅(qū)動(dòng)的方式定義位置和依賴關(guān)系來擴(kuò)展加載過程帖世。 在某些情況下休蟹,您可能希望創(chuàng)建自定義操作沸枯。Iresourceprovider API 是構(gòu)建這些自定義操作的基礎(chǔ)

創(chuàng)建一個(gè)自定義操作

通過從 AsyncOperationBase 類派生并重寫所需的虛方法來創(chuàng)建自定義操作日矫。 可以將派生的操作傳遞給 ResourceManager.StartOperation啟動(dòng),并接收 一個(gè)AsyncOperationHandle 的返回結(jié)構(gòu)。 以這種方式啟動(dòng)的操作在 ResourceManager 中注冊(cè)绑榴,并顯示在 Addressables Profiler
哪轿。

執(zhí)行操作

Resourcemanager 調(diào)用 AsyncOperationBase。 在依賴性操作完成后翔怎,再執(zhí)行自定義操作方法窃诉。

完成句柄

自定義操作完成后,調(diào)用 AsyncOperationBase.Complete赤套。您可以在 Execute 方法中調(diào)用這個(gè)函數(shù)飘痛,也可以將其推遲到調(diào)用外部。 Asyncoperationbase. Complete 通知 ResourceManager 操作已經(jīng)完成容握,并將調(diào)用關(guān)聯(lián)的 AsyncOperationHandle.Completed 事件已完成活動(dòng)宣脉。

終止操作

當(dāng)你在釋放AsyncOperationHandle時(shí),Resourcemanager會(huì)為你的自定義操作調(diào)用 AsyncOperationBase.Destroy方法,你應(yīng)該在此釋放與自定義操作關(guān)聯(lián)的任何內(nèi)存或資源。

Addressables分析器

Addressables分析器一個(gè)收集你的項(xiàng)目的地址布局信息的工具剔氏。 在某些情況下塑猖,分析人員可能會(huì)采取適當(dāng)?shù)男袆?dòng)來清理項(xiàng)目的狀態(tài)。 在其他情況下谈跛,分析僅僅是一個(gè)信息工具羊苟,它可以讓你對(duì)你的 Addressables 布局做出更明智的決定。

使用分析器

在編輯器中感憾,打開 Addressables Window **(Window Asset Management Addressables) **蜡励,然后單擊菜單中的 Analyze 按鈕以打開 Addressables Window

分析窗口顯示一個(gè)分析規(guī)則列表,以及以下操作:

  • Analyze Selected Rules 分析選擇規(guī)則
  • Fix Selected Rules 修復(fù)選擇規(guī)則
  • Clear Selected Rules 清理選擇規(guī)則
分析操作

分析操作是規(guī)則信息的收集步驟凉倚。運(yùn)行此操作可以收集有關(guān)生成彭则、依賴映射等的數(shù)據(jù), 每個(gè)規(guī)則負(fù)責(zé)收集所需的數(shù)據(jù),并將其作為 AnalyzeResult 對(duì)象返回占遥。

在分析步驟期間俯抖,不應(yīng)采取任何措施修改任何數(shù)據(jù)或項(xiàng)目狀態(tài)。 根據(jù)在此步驟中收集的數(shù)據(jù)瓦胎,修復(fù)操作可能是適當(dāng)?shù)牟僮鬟^程芬萍。 但是,有些規(guī)則只包含分析步驟搔啊,因?yàn)闆]有明確的合適的操作基于收集到的基礎(chǔ)信息柬祠。
例子
Check Scene to Addressable Duplicate Dependencies
Check Resources to Addressable Duplicate Dependencies

純信息性規(guī)則且不包含修復(fù)操作的規(guī)則被歸類為不可修復(fù)規(guī)則。 那些確實(shí)具有修復(fù)操作的規(guī)則被歸類為可修復(fù)規(guī)則负芋。

清除步驟

此操作將刪除分析收集的任何數(shù)據(jù)漫蛔,并相應(yīng)地更新TreeView

修復(fù)操作

對(duì)于可修復(fù)規(guī)則,您可以選擇運(yùn)行修復(fù)操作旧蛾。 這將使用在分析步驟中收集的數(shù)據(jù)來執(zhí)行任何必要的修改并解決問題

例子:
Check Duplicate Group Dependencies

可以采取合理的適當(dāng)行動(dòng)來解決分析中發(fā)現(xiàn)的問題

提供分析規(guī)則
可修復(fù)的規(guī)則

檢查重復(fù)的組依賴項(xiàng)
該規(guī)則通過使用 BundledAssetGroupSchemas 掃描所有組并投影資產(chǎn)組布局來檢查可能重復(fù)的資產(chǎn)莽龟。 這實(shí)際上需要觸發(fā)一個(gè)完整的構(gòu)建,因此這種檢查非常耗時(shí),而且需要大量的性能。

問題: 重復(fù)的資源產(chǎn)生于共享依賴關(guān)系的不同組中的資源猪杭,例如共享不同 Addressable 組中存在的資產(chǎn)的兩個(gè)預(yù)制Prefab。 這些材料(以及它的任何附屬物)將被拉入包含預(yù)制Prefabs的兩個(gè)組搂赋。 為了防止這種情況,材料必須被標(biāo)記為可尋址的益缠,無論是在一個(gè)Prefab脑奠,或在自己的空間,從而把材料和它的依賴關(guān)系在一個(gè)單獨(dú)的可尋址組

解決方案: 如果此檢查發(fā)現(xiàn)任何問題幅慌,在此規(guī)則上運(yùn)行修復(fù)操作將創(chuàng)建一個(gè)新的 Addressable 組宋欺,用于移動(dòng)所有依賴的資產(chǎn)。

異常: 如果您有一個(gè)包含多個(gè)對(duì)象的資源欠痴,那么不同的組可能只提取該資源的一部分迄靠,而不是實(shí)際的重復(fù)。 一個(gè)多網(wǎng)格的 FBX 就是這樣的例子喇辽。 如果一個(gè)網(wǎng)格在“ GroupA”中掌挚,另一個(gè)網(wǎng)格在“ GroupB”中,那么這個(gè)規(guī)則將認(rèn)為 FBX 是共享的菩咨,如果您運(yùn)行修復(fù)操作吠式,則將它提取到它自己的組中陡厘。 在這種情況下,運(yùn)行修復(fù)操作實(shí)際上是有害的特占,因?yàn)閮山M都沒有完整的 FBX 資源糙置。

還要注意,重復(fù)的資源可能并不總是一個(gè)問題是目。 如果同一組用戶永遠(yuǎn)不會(huì)請(qǐng)求資源(例如谤饭,區(qū)域特定的資源) ,那么可能希望重復(fù)依賴懊纳,或者至少是無關(guān)緊要的揉抵。 每個(gè)項(xiàng)目都是唯一的,因此修復(fù)重復(fù)的資資源依賴關(guān)系應(yīng)該根據(jù)具體情況進(jìn)行評(píng)估嗤疯。

不可修復(fù)的規(guī)則

檢查資源以尋址重復(fù)依賴項(xiàng)
此規(guī)則檢測(cè)是否有任何資源或資源依賴項(xiàng)在構(gòu)建的Addressable數(shù)據(jù)和駐留在Resources文件夾中的資源之間重復(fù)冤今。

問題: 這些重復(fù)意味著數(shù)據(jù)將同時(shí)包含在應(yīng)用程序構(gòu)建和 Addressables 構(gòu)建中。

解決方案: 這個(gè)規(guī)則是不可修復(fù)的茂缚,因?yàn)闆]有合適的操作可以自動(dòng)解決戏罢。 它純粹提供信息,提醒您存在重復(fù)和冗余,如果有的話,你必須自行決定如何進(jìn)行和采取什么行動(dòng)脚囊。 可能的手動(dòng)修復(fù)的一個(gè)示例是將有問題的資產(chǎn)移出 Resources 文件夾龟糕,并使其可尋址。

構(gòu)建捆綁布局
此規(guī)則將顯示如何在 Addressable 構(gòu)建中顯式標(biāo)記為 Addressable 的資源凑术。 對(duì)于這些顯式資源翩蘸,我們還顯示了構(gòu)建隱式引用了哪些資源,因此將引入哪些資源淮逊。
此規(guī)則收集的數(shù)據(jù)不表示任何特定問題。 它是純粹的信息扶踊。

檢查精靈地圖集泄鹏,以尋址重復(fù)依賴
給定一個(gè)可尋址的精靈地圖集,這個(gè)規(guī)則將檢測(cè)是否有任何精靈在地圖集被標(biāo)記為可尋址的任何其他地方秧耗。

問題: 這些重復(fù)意味著精靈數(shù)據(jù)將在可尋址構(gòu)建的多個(gè)區(qū)域中重復(fù)备籽。
解決方案:它是純粹的信息,提醒你冗余分井。 如果有的話车猬,你必須決定如何進(jìn)行和采取什么行動(dòng)。 一個(gè)可能的手動(dòng)修復(fù)的例子是從 Addressables 中移除重復(fù)的精靈尺锚,讓你的資產(chǎn)從你的 Addressable 精靈地圖集中引用一個(gè)精靈珠闰,而不是直接引用精靈。

拓展分析

每個(gè)唯一的項(xiàng)目可能需要額外的分析規(guī)則以外的預(yù)先包裝瘫辩。
可尋址資產(chǎn)系統(tǒng)允許您創(chuàng)建自己的自定義規(guī)則類

AnalyzeRule objects
創(chuàng)建 AnalyzeRule 類的一個(gè)新的子類伏嗜,并重寫以下屬性:

  • canFix 告訴分析該規(guī)則是否被認(rèn)為是可修復(fù)的坛悉。
  • ruleName 是您將在Analyze window分析窗口中看到的此規(guī)則的顯示名稱。
    你還需要重寫以下方法:
  • List<AnalyzeResult> RefreshAnalysis(AddressableAssetSettings settings)
  • void FixIssues(AddressableAssetSettings settings)
  • void ClearAnalysis().
    Note: 如果您的規(guī)則被指定為不可修復(fù)承绸,那么您不必重寫 FixIssues方法裸影。

引用分析
這是你的分析操作。 在此方法中军熏,執(zhí)行所需的任何計(jì)算轩猩,并緩存潛在修復(fù)所需的所有數(shù)據(jù)。 返回值是一個(gè) List<Analyzeresult> 列表荡澎。 在收集數(shù)據(jù)之后界轩,為分析中的每個(gè)條目創(chuàng)建一個(gè)新的 AnalyzeResult,其中第一個(gè)參數(shù)的數(shù)據(jù)為字符串衔瓮,第二個(gè)參數(shù)的數(shù)據(jù)為 MessageType (可以選擇將消息類型指定為警告或錯(cuò)誤)浊猾。

返回您創(chuàng)建的對(duì)象列表。 如果需要在 TreeView 中為特定的 AnalyzeResult對(duì)象創(chuàng)建子元素热鞍,則可以使用 kDelimiter 描述父項(xiàng)和任何子項(xiàng)葫慎。 包括父項(xiàng)和子項(xiàng)之間的分隔符。

修復(fù)問題
這是您的修復(fù)操作薇宠。 如果需要對(duì)分析步驟采取適當(dāng)?shù)男袆?dòng)偷办,請(qǐng)?jiān)谶@里執(zhí)行

清除分析
這是你的清除分析行為。 在分析步驟中緩存的任何數(shù)據(jù)都可以在此函數(shù)中清理或刪除澄港。 Treeview 將會(huì)更新以反映數(shù)據(jù)的缺乏椒涯。

追加自定義規(guī)則到GUI
自定義規(guī)則必須使用 1AnalyzeWindow.RegisterNewRule<RuleType>()1 向 GUI 類注冊(cè)自己。 為了在Analyze window.中顯示回梧。 例如:

class MyRule : AnalyzeRule {}
[InitializeOnLoad]
class RegisterMyRule
{
    static RegisterMyRule()
    {
        AnalyzeWindow.RegisterNewRule<MyRule>();
    }
}

(舊項(xiàng)目)更新移植到可尋址資源系統(tǒng)

本文詳細(xì)介紹了如何修改現(xiàn)有項(xiàng)目以利用可尋址資產(chǎn)废岂。 有三種傳統(tǒng)的資產(chǎn)參考方法:

  • Direct References 直接引用類型 : 將資源直接添加到應(yīng)用程序自動(dòng)加載的組件或場(chǎng)景中
  • Resource Folders Resource 文件夾 : 將資源放在Resource文件夾中通過它們的名字加載
  • Asset Bundles 資源打包成資源包 : 打包成AssetBundle然后通過文件路徑加載它們及其依賴項(xiàng)
Direct References直接引用法的移植:

要從這種方法遷移,請(qǐng)遵循以下步驟:

  1. 用AssetReference 替換對(duì)對(duì)象的直接引用
public GameObject directRefMember;
//替換為
public AssetReference AssetRefMember;
  1. 將資源拖到適當(dāng)?shù)慕M件的 Inspector 上狱意,就像直接引用一樣湖苞。
  2. 如果希望基于對(duì)象而不是字符串名稱加載資產(chǎn),請(qǐng)直接從您在設(shè)置中創(chuàng)建的 AssetReference 對(duì)象中實(shí)例化該資產(chǎn).
    AssetRefMember.LoadAssetAsync<GameObject>();
    AssetRefMember.InstantiateAsync(pos, rot);

注意: 可尋址資產(chǎn)系統(tǒng)異步加載資產(chǎn)详囤。 在更新對(duì)資產(chǎn)引用的直接引用時(shí)财骨,還必須更新代碼以異步操作。

Resource folders法的移植:

當(dāng)您將 Resources文件夾中的資產(chǎn)標(biāo)記為Addressable時(shí)藏姐,系統(tǒng)會(huì)自動(dòng)將該資產(chǎn)從Resources文件夾移動(dòng)到 Project 中名為 Resources_moved 的新文件夾中隆箩。 已移動(dòng)資產(chǎn)的默認(rèn)地址是舊路徑,省略了文件夾名稱羔杨。 加載代碼的變化為:

Resources.LoadAsync<GameObject>("desert/tank.prefab"); 
//替換為
Addressables.LoadAssetAsync<GameObject>("desert/tank.prefab");.
Asset Bundles法的移植:

當(dāng)您打開 Addressables window時(shí)捌臊,Unity 提供將所有資源包轉(zhuǎn)換為 Addressable 資產(chǎn)組。
這是遷移代碼最簡(jiǎn)單的方法问畅。 如果選擇手動(dòng)轉(zhuǎn)換資源娃属,請(qǐng)單擊忽略按鈕六荒。
然后,使用前面描述的直接引用或資源文件夾方法矾端。
資產(chǎn)地址的默認(rèn)路徑是其文件路徑掏击。 如果使用路徑作為資源的地址,則裝載資源的方式與從捆綁包裝載資產(chǎn)的方式相同秩铆。 可尋址資源系統(tǒng)處理包及其所有依賴項(xiàng)的加載砚亭。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市殴玛,隨后出現(xiàn)的幾起案子捅膘,更是在濱河造成了極大的恐慌,老刑警劉巖滚粟,帶你破解...
    沈念sama閱讀 216,470評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件寻仗,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡凡壤,警方通過查閱死者的電腦和手機(jī)署尤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來亚侠,“玉大人曹体,你說我怎么就攤上這事∠趵茫” “怎么了箕别?”我有些...
    開封第一講書人閱讀 162,577評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長滞谢。 經(jīng)常有香客問我串稀,道長,這世上最難降的妖魔是什么爹凹? 我笑而不...
    開封第一講書人閱讀 58,176評(píng)論 1 292
  • 正文 為了忘掉前任厨诸,我火速辦了婚禮,結(jié)果婚禮上禾酱,老公的妹妹穿的比我還像新娘。我一直安慰自己绘趋,他們只是感情好颤陶,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著陷遮,像睡著了一般滓走。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上帽馋,一...
    開封第一講書人閱讀 51,155評(píng)論 1 299
  • 那天搅方,我揣著相機(jī)與錄音比吭,去河邊找鬼。 笑死姨涡,一個(gè)胖子當(dāng)著我的面吹牛衩藤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播涛漂,決...
    沈念sama閱讀 40,041評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼赏表,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了匈仗?” 一聲冷哼從身側(cè)響起瓢剿,我...
    開封第一講書人閱讀 38,903評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎悠轩,沒想到半個(gè)月后间狂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,319評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡火架,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評(píng)論 2 332
  • 正文 我和宋清朗相戀三年鉴象,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片距潘。...
    茶點(diǎn)故事閱讀 39,703評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡炼列,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出音比,到底是詐尸還是另有隱情俭尖,我是刑警寧澤,帶...
    沈念sama閱讀 35,417評(píng)論 5 343
  • 正文 年R本政府宣布洞翩,位于F島的核電站稽犁,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏骚亿。R本人自食惡果不足惜已亥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望来屠。 院中可真熱鬧虑椎,春花似錦、人聲如沸俱笛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽迎膜。三九已至泥技,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間磕仅,已是汗流浹背珊豹。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評(píng)論 1 269
  • 我被黑心中介騙來泰國打工簸呈, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人店茶。 一個(gè)月前我還...
    沈念sama閱讀 47,711評(píng)論 2 368
  • 正文 我出身青樓蜕便,卻偏偏與公主長得像,于是被迫代替她去往敵國和親忽妒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子玩裙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容