Assets in Unity
Overview
Quantum generates a second part for your asset that is a partial class. This part inherits from the AssetObject
ScriptableObject.
AssetGuids
of all the available assets have to be known to the simulation when it starts.
The process for this to happen is:
In the Editor:
- All
AssetObject
assets collected from the locations defined inQuantumEditorSettings.AssetSearchPaths
(by default, theAssets
folder and all child folders). - Each
AssetObject
has a generated entry containing theAssetGuid
and the information needed to load theAssetObject
at runtime. - Entries are saved into the
QuantumUnityDB
asset at defined in theQuantumUnityDB.DefaultPath
(by defaultAssets/QuantumUser/Resources/QuantumUnityDB.qunitydb
).
- All
At runtime:
- The list of asset entries from
QuantumUnityDB
is used to initialize the simulation'sIResourceManager
along with the information needed to load each asset dynamically.
- The list of asset entries from
To browse the list of Asset Objects currently part of the database, use the AssetDB Inspector window accessible via Quantum/Window/Quantum Unity DB
.
Finding Quantum Assets in Unity scripts
Use the QuantumUnityDB
class in order to find assets in the Unity side. Here is a complete snippet of a Quantum asset declaration, and how to access it's fields on Unity:
Anywhere in QuantumUser:
C#
// in a .cs file:
public class CharacterData : AssetObject {
public FP MaximumHealth;
}
In the Unity side:
C#
var characterData = QuantumUnityDB.GetGlobalAsset<CharacterDataAsset>(myAssetRef.Id);
FP maximumHealth = characterData.MaximumHealth;
Finding Assets In the Inspector
It's important to note that when attempting to load a Quantum asset from an editor script, GetGlobalAssetEditorInstance
should be used instead.
Usage:
C#
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
var characterData = QuantumUnityDB.GetGlobalAssetEditorInstance<CharacterData>(myAssetRef.Id);
FP maximumHealth = characterData.MaximumHealth;
// do something
EditorUtility.SetDirty(characterData);
}
Overwriting AssetGuids
In some cases, it might be necessary to overwrite the AssetGuid
of an asset.
This can be done by navigating to your asset object, clicking the dropdown named Quantum Unity DB
and then enabling Guid Override
. You will be provided a field to enter your custom AssetGuid.
These overrides are saved in QuantumEditorSettings
.
Resources and Addressables
Quantum never forms hard-references to AssetObject
assets. This enables the use of any dynamic content delivery.
The following methods of loading assets are supported out of the box:
- Resources
- Addressables (needs to be explicitly enabled)
There are not any extra steps needed for AssetObject
to be loadable dynamically using any of the methods above.
The details on how to load each asset are stored in QuantumUnityDB
. This information is accessed when a simulation calls Frame.FindAsset
or when QuantumUnityDB.GetGlobalAsset
is called and leads to an appropriate method of loading being used.
- If an asset is in a
Resource
folder, it will be loaded using theResources
API. - If an asset has an address (explicit or implicit), it will be loaded using the
Addressables
API.
To make the list of the assets (QuantumUnityDB
) dynamic itself some extra code is needed; pleasr refer to the Updating Quantum Assets At Runtime section for more information.
User scripts can avoid hard references by using AssetRef<T>
. (e.g. AssetRef<SimulationConfig>
) instead of AssetObject
references (e.g. SimulationConfig
) to reference Quantum assets.
C#
public class TestScript : MonoBehaviour {
// hard reference
public SimulationConfig HardRef;
// soft reference
public AssetRef<SimulationConfig> SoftRef;
void Start() {
// depending on the target asset's settings, this call may result in
// any of the supported loading methods being used
SimulationConfig config = UnityDB.GetGlobalAsset<SimulationConfig>(SoftRef);
}
}
Drag-And-Dropping Assets In Unity
Adding asset instances and searching them through the Frame class from inside simulation Systems can only go so far. At convenient solution arises from the ability to have asset instances point to database references and being able to drag-and-drop these references inside Unity Editor.
One common use is to extend the pre-build RuntimePlayer
class to include an AssetRef
to a particular CharacterSpec
asset chosen by a player. The generated and type-safe asset_ref
type is used for linking references between assets or other configuration objects.
C#
// this is added to the RuntimePlayer.User.cs file
namespace Quantum {
partial class RuntimePlayer {
public AssetRef<CharacterSpec> CharacterSpec;
partial void SerializeUserData(BitStream stream) {
stream.Serialize(ref CharacterSpec);
}
}
}
This snippet will generate an asset_ref
which only accepts a link to an asset of type CharacterSpec
. This field will show up in the Unity inspector and can be populated by drag-and-dropping an asset into the slot.
Map Asset Baking Pipeline
Another entry point for generating custom data in Quantum is the map baking pipeline.
The Map
asset is required by a Quantum simulation and contains basic information such as NavMeshes and static colliders; additional custom data can be saved as part of the asset placed in its custom asset slot - this can be an instance of any custom data asset. The custom asset can be used to store any static data meant to be used during initialization or at runtime. A typical example would be an array of spawn point data such as position, spawned type, etc.
In order for a Unity scene to be associated with a Map
, the MapData
MonoBehaviour
component needs to be present on a GameObject
in the scene. Once MapData.Asset
points to a valid Map
, the baking process can take place. By default, Quantum bakes navmeshes, static colliders and scene prototypes automatically as a scene is saved or when entering play mode; this behaviour can be changed in QuantumEditorSettings
.
To assign a custom piece of code to be called every time the a bake happens, create a class inheriting from the abstract MapDataBakerCallback
class.
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) { }
}
Then override the mandatory OnBake(MapData data)
and OnBakeBefore(MapData data)
methods.
C#
[assembly: QuantumMapBakeAssembly]
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) {
}
}
Preloading Addressable Assets
Quantum needs assets to be loadable synchronously.
v3 or newer
WaitForCompletion
was addded in Addressables 1.17 which added the ability to load assets synchronously.
Although asynchronous loading is possible, there are situations in which preloading assets might still be preferable; the QuantumRunnerLocalDebug.cs
script demonstrates how to achieve this.
Baking AssetBase Load Information
Every time the menu option Quantum > Generate Asset Resources
is used or an asset in one of QuantumEditorSettings.AssetSearchPaths
is imported, the AssetResourceContainer
is recreated in full at the location specified by the QuantumEditorSettings.AssetResourcePath
.
During the creation of the QuantumUnityDB
, each AssetObject
located in any QuantumEditorSettings.AssetSearchPaths
is assigned to a group. By default, two groups exist:
ResourcesGroup
;AddressablesGroup
.
The process of deciding which group an asset is assigned to is shown in the diagram below.
An asset is considered Addressable if:
- it has an address assigned;
- any of its parent folders is Addressable; or,
- it is nested in another Addressable asset.
The same logic applies to deciding whether an asset is a part of an Asset Bundle.
To disable baking AssetObject
when assets are imported, untick QuantumEditorSettings.UseAssetBasePostprocessor
.
Updating Quantum Assets in Build
It is possible for an external CMS to provide data assets; this is particularly useful for providing balancing updates to an already released game without making create a new build to which players would have to update.
This approach allows balancing sheets containing information about data-driven aspects such as character specs, maps, NPC specs, etc... to be updated independently from the game build itself. In this case, game clients would always try to connect to the CMS service, check for whether there is an update and (if necessary) upgrade their game data to the most recent version before starting or joining online matches.
Updating Existing Assets
The use of Addressables is recommended as these are supported out of the box. Any AssetObject
that is an Addressable will get loaded at runtime using the appropriate methods.
To avoid unpredictable lag spikes resulting from downloading assets during the game simulation, consider downloading and preloading your assets as discussed here: Preloading Addressable Assets.
Adding New Assets
The QuantumUnityDB
generated in the editor will contain the list of all the assets present at its creation. If a project's dynamic content includes adding new Quantum assets during without creating a new build, a way to update the list needs to be implemented.
Adding New Assets With DynamicAssetDB
If new assets can be created in a deterministic way, the DynamicAssetDB
can be used as discussed here: Dynamic Assets.