Assets in Unity
概述
針對在Unity中可用的各個資產類型,Quantum生成一個以ScriptableObject
為基礎的包裝部分類別。針對該包裝的基礎類別是AssetBase
。管理AssetBase
執行個體的主要類別稱為UnityDB
。
所有可用的資產的AssetGuids
必須在模擬開始時為模擬所知。
實現這一點的過程是:
在編輯器中:
- 從
QuantumEditorSettings.AssetSearchPaths
中定義的位置收集的所有AssetBase
資產。(預設為Assets/Resources/DB
)。 - 各個
AssetBase
有一個被生成的輸入項目,其含有AssetGuid
以及在運行階段時載入AssetBase
所需的資訊。 - 輸入項目被儲存到
QuantumEditorSettings.AssetResourcePath
中定義的AssetResourceContainer
資產之中(預設為Assets/Resources/AssetResources.asset
)。
- 從
在運行階段:
- 第一次使用任何
UnityDB
成員時,使用Resources.Load
載入AssetResourceContainer
(基於QuantumEditorSettings.AssetResourcePath
)。 - 使用輸入項目清單以初始化模擬的
IResourceManager
及動態地載入各個資產所需的資訊。
- 第一次使用任何
為了瀏覽目前的資料庫的一部分的資產物件的清單,使用資產DB偵測器視窗,可透過Quantum/Show AssetDB Inspector
及Window/Quantum/AssetDB Inspector
選單項目來取用。
在Unity指令碼中尋找Quantum資產
使用者建立的每個實體資產類別都取得一個在Unity側生成的相對應的類別,以啟用它們的具現化,如實際的Unity可指令碼物件。
為了舉例說明:一個名為CharacterData
的Quantum資產在Unity中取得一個名為CharacterDataAsset
的類別,其中單字Asset
始終是被新增的尾碼。Unity類別總是含有一個名為AssetObject
的屬性,其可被投放到Quantum類別,以存取模擬特定欄位。
使用UnityDB
類別,以在Unity側中尋找資產。這裡是一個Quantum資產宣告的一個完整的程式碼片段,以及如何在Unity上存取其欄位的方式:
在Quantum側:
C#
// in any .qtn file:
asset CharacterData;
// in a .cs file:
public unsafe partial class CharacterData
{
public FP MaximumHealth;
}
在Unity側:
C#
var characterDataAsset = UnityDB.FindAsset<CharacterDataAsset>(myAssetRef.Id);
var characterData = characterDataAsset.Settings;
FP maximumHealth = characterData.MaximumHealth;
資源、可定址及資產套件
Quantum永不硬參照到AssetBase
資產。這啟用了任何動態內容傳遞的使用。以下載入資產的方法立即可用:
- 資源
- 可定址(需要明確地被啟用)
- 資產套件(作為一個概念證明,因為資產套件要求高度自訂,及針對各個專案的方法)
動態地使用上述任何方法來讓AssetBase
成為可載入時,不需要任何額外的步驟。如何載入各個資產的細節儲存於AssetResourceContainer
之中。當一個模擬調用Frame.FindAsset
,或當調用UnityDB.FindAsset
時,將存取這個資訊,以使用一個合適的載入方法。
- 如果一個資產是在一個
Resource
資料夾之中,將使用Resources
API載入它。 - 如果一個資產有一個地址(明確或隱含),將使用
Addressables
API載入它。 - 如果一個資產屬於一個資產套件(明確或隱含),將會嘗試使用
AssetBundle
API載入它。
為了使資產清單(AssetResourceContainer
)成為動態,需要一些額外的程式碼;請參見在運行階段更新Quantum資產章節以取得更多資訊。
使用者指令碼可以透過使用AssetRef
類型(比如AssetRefSimulationConfig
)而非AssetBase
參照(比如SimulationConfig
)來避免硬參照,以參照Quantum資產。
C#
public class TestScript : MonoBehaviour {
// hard reference
public SimulationConfigAsset HardRef;
// soft reference
public AssetRefSimulationConfig SoftRef;
void Start() {
// depending on the target asset's settings, this call may result in
// any of the supported loading methods being used
SimulationConfigAsset config = UnityDB.FindAsset<SimulationConfigAsset>(SoftRef.Id);
}
}
在Unity中拖放資產
從模擬系統之中新增資產執行個體及透過 幀 類別搜尋它們,只能做到如此。方便的解決方案是能夠讓資產執行個體指向資料庫參照,並且在Unity編輯器中拖放這些參照。
一個常見的使用是擴展預先組建的RuntimePlayer
類別,以包含一個AssetRef
到一個玩家所選擇的一個特定的CharacterSpec
資產。將使用被生成的及對於類型安全的asset_ref
類型來在資產或其他組態物件之間連接參照。
C#
// this is added to the RuntimePlayer.User.cs file
namespace Quantum {
partial class RuntimePlayer {
public AssetRefCharacterSpec CharacterSpec;
partial void SerializeUserData(BitStream stream) {
stream.Serialize(ref CharacterSpec);
}
}
}
這個程式碼片段將允許生成asset_ref
欄位,其只接受一個到類型CharacterSpec
的資產的一個連結。這個欄位將在Unity偵測器中顯示,並且可以透過拖放一個資產到槽來填入。
地圖資產內嵌管道
在Quantum中針對生成自訂資料的另一個入口,是地圖內嵌管道。MapAsset
是針對Map
資產的以AssetBase
為基礎的包裝。
Quantum模擬要求Map
資產,其含有基本的資訊,比如導航網格及靜態碰撞器;額外的自訂資料可以保存為放置在其自訂資產槽中的資產的一部分——這可以是任何自訂資料資產的一個執行個體。自訂資產可用於儲存初始化時或在運行階段時使用的任何靜態資料。一個典型的例子是繁衍點資料的一個陣列,比如位置、被繁衍的類型等等。
為了使一個Unity場景與一個MapAsset
相關聯,MapData
MonoBehaviour
元件需要在場景之中在一個GameObject
上被呈現。當MapData.Asset
指向一個有效的MapAsset
,內嵌流程將發生。預設下,當一個場景被儲存或當進入遊玩模式時,Quantum自動地內嵌導航網格、靜態碰撞器及場景原型;可在QuantumEditorSettings
中更改這個行為。
為了指派一個自訂程式碼片段,讓其在每次內嵌發生時被調用,建立一個繼承於抽象MapDataBakerCallback
類別的類別。
C#
public abstract class MapDataBakerCallback {
public abstract void OnBake(MapData data);
public abstract void OnBeforeBake(MapData data);
public virtual void OnBakeNavMesh(MapData data) { }
public virtual void OnBeforeBakeNavMesh(MapData data) { }
}
然後覆寫強制性OnBake(MapData data)
及OnBakeBefore(MapData data)
方法。
C#
public class MyCustomDataBaker: MapDataBakerCallback {
public void OnBake(MapData data) {
// any custom code to live-load data from scene to be baked into a custom asset
// generated custom asset can then be assigned to data.Asset.Settings.UserAsset
}
public void OnBeforeBake(MapData data) {
}
}
預先載入可定址資產
Quantum需要資產可被同步地載入。
第1.16版或更舊版本
在可定址版本1.17版之前的版本,除了在模擬開始之前預先載入,或使用Unity的SyncAddressables
範例,沒有其他方法可以同步地載入可定址資產。
第1.17版或更新版本
在可定址1.17版中新增了WaitForCompletion
,其新增了同步地載入資產的能力。為了針對Quantum啟用它,請定義QUANTUM_ADDRESSABLES_USE_WAIT_FOR_COMPLETION
或在QuantumEditorSettings
資產的Build Features
段落中使用切換。
雖然同步載入是可能的,但在某些情況下,預先載入資產仍然可能是更好的方法;QuantumRunnerLocalDebug.cs
指令碼展示了如何達成這點。
內嵌資產基礎載入資訊
AssetResourceContainer
是一個ScriptableObject
,其含有如何載入各個AssetBase
及對應它們到AssetGuids
的資訊。
每次使用選單選項Quantum > Generate Asset Resources
或匯入在QuantumEditorSettings.AssetSearchPaths
的其中之一的一個資產時,將在QuantumEditorSettings.AssetResourcePath
所特定的位置完整地重新建立AssetResourceContainer
。
在建立AssetResourceContainer
時,位於任何QuantumEditorSettings.AssetSearchPaths
中的各個AssetBase
將被指派到一個群組。預設存在三個群組:
ResourcesGroup
;AssetBundlesGroup
;以及,AddressablesGroup
。
以下圖表展示了決定一個資產被指派到哪個群組的過程。
一個資產被認為是可定址的 前提是:
- 它被指派一個地址;
- 其任何的上層資料夾都是可定址的;或是,
- 它巢狀於另一個可定址的資產。
同樣的邏輯適用於決定一個資產是否屬於一個資產套件的一部分。
為了在匯入資產時停用內嵌AssetBase
,請取消勾選QuantumEditorSettings.UseAssetBasePostprocessor
。
在組建中更新Quantum資產
一個外部CMS可以提供資料資產;這在提供平衡更新到一個已經發布的遊戲時特別有用,而不需要建立一個玩家必須更新的新組建。
這個方法允許含有關於由資料驅動的方面的資訊,比如角色規格、地圖、NPC規格等等的資訊的平衡表,獨立地從遊戲組件被更新。在這個情況下,遊戲客戶端將總是嘗試連接到CMS服務,檢查那裡是否有更新,並且(如果需要的話)在開始或加入線上對戰之前,升級它們的遊戲資料到最新版本。
更新已存在的資產
建議使用可定址或資產套件,因為這些都是開箱即用的。任何屬於一個可定址或屬於一個資產套件的一部分的AssetBase
將使用適合的方法在運行階段被載入。
為了避免在遊戲模擬時下載資產而產生的不可預測的延隔高峰,請考慮在此下載及預先載入您的資產: 預先載入可定址的資產.
新增新的資產
在編輯器中生成的AssetResourceContainer
將含有在其建立時所呈現的所有資產的清單。如果一個專案的動態內容包含新增新的Quantum資產而不建立一個新的組建,則需要執行一個方法以更新該清單。
為了達成這個,建議的方法是透過一個部分UnityDB.LoadAssetResourceContainerUser
方法的擴展。當第一個模擬開始或任何UnityDB
方法被調用時,Quantum將嘗試載入AssetResourceContainer
。預設是假設AssetResourcesContainer
是一個資源並且位於QuantumEditorSettings.AssetResourcePath
。為了覆寫這個行為,UnityDB.LoadAssetResourceContainerUser
需要被執行。
實例執行方式
首先,AssetResourceContainer
需要被移出Resources
資料夾之外。透過設定QuantumEditorSettings.AssetResourcePath
來完成這個動作:
第二,新的AssetResourceContainer
需要被做成一個可定址:
最後,程式碼片段執行部分方法:
C#
partial class UnityDB {
static partial void LoadAssetResourceContainerUser(ref AssetResourceContainer container) {
var path = QuantumEditorSettings.Instance.AssetResourcesPath;
#if UNITY_EDITOR
if (!UnityEditor.EditorApplication.isPlaying) {
container = UnityEditor.AssetDatabase.LoadAssetAtPath<AssetResourceContainer>(path);
Debug.Assert(container != null);
return;
}
#endif
var op = Addressables.LoadAssetAsync<AssetResourceContainer>(path);
container = op.WaitForCompletion();
Debug.Assert(container != null);
}
}
以動態資產DB來新增新的資產
如果新的資產可以在一個確定性的方式下被建立,可使用此處所討論的DynamicAssetDB
: 動態資產.