Composable Assets
In this section, we will discuss an advanced type of asset: Composable Assets. These asset types permit the composition of simple asset operations, allowing the creation of more potent assets such as loot boxes, or assets granted after a delay.
These assets use the same underlying object types (AssetDefinitions and AssetInstances), but they offer additional nesting capabilities and extra operations that players can call on them.
The assets can be composed with a different types of nodes:
- DurationAsset - An asset that becomes available after a set time period.
- TimedAsset - An asset that unlocks at a specific moment in time.
- GrantAsset - A type of asset that can directly release a group of simple assets. It's a leaf node and doesn't wrap any other asset node.
- GroupAsset - This asset can release a group of assets which might themselves be Composable Assets.
- RandomAsset - This asset randomly picks and releases one asset from a predefined list, based on set odds.
- SealedAsset - A special kind of asset that needs an explicit action from the player to reveal the internal asset.
Note that each of these nodes wraps another asset node, other than the GrantAsset which is a terminal node. Using these you could imagine granting a player a special item that drops only after specific time. In pseudo-code you could describe it as DurationAsset(after: "1000s", grant:GrantAsset(item: "Great Sword", quantity: 1))
.
Lifecycle of the composable assets
At their core, Composable Assets are specified via an AssetDefinition and can be granted the exact same way - either via direct grant from the developer, storefront offering, marketplace trade, and so on. Similarly to other assets they will also show in the player's asset holdings. The main difference is in the additional operations that can be run on them. For each asset which has a type COMPOSABLE
, a player can call
- Unwrap - If the node can be unwrapped this will unwrap and destroy the asset. The player will be granted with whatever assets This removes the Composable Asset and adds a new asset to the player's inventory. Unwrapping is usually an implicit action, and shouldn't require Player's input or interaction.
- Unseal - If the node of the asset is
SealedAsset
, this will be unwrapped. It's an explicit action taken by a player. For instance opening of a loot box.
Creating Composable Asset Definitions
Follow these steps to create a Composable Asset Definition:
From the game page in the Subroutine Dashboard, select "Assets" section from the top navigation bar.
Click on "Asset Recipes" and click "Create Asset Recipe".
In this UI, you can create an asset recipe in a form of a simple flowchart. You can replicate this example to create a simple Loot Box:
Once you have your Composable Asset Definition created, you can grant it to a test player, similarly to how we granted the assets in the Asset Holdings section.
Click the "Players" tab on the top navigation bar and locate the test player you'd like to use (or create one). Click on "Grant Asset".
Insert Screenshot
From the drop-down menu, select the Asset Definition for the recipe you just created.
Insert Screenshot
Click "Grant". This will grant the Composable Asset Definition you just created to your test player's inventory.
Unsealing Composable Assets
Once the asset is granted to a player, the next step is to fetch it on the client side and then unseal it. Here, we'll guide you through this process using both Unity and GraphQL.
- Unity
- GraphQL
Client.Player.GetInventory(new PlayerAPI.GetInventoryProps
{
}, (response) =>
{
// We get all the user holdings, and we filter it down to the asset that can be unsealed
var assetToUnseal = response.Holdings.First((asset) =>
{
// Or change to the name you chose for this asset.
return asset.node.assetDefinition.name == "Lootbox";
}).node.instances.edges.First();
if (assetToUnseal != null)
{
// If we have an Asset, we will unseal it and show what's inside
Debug.Log("Found an asset, unsealing!");
Client.Player.UnsealAsset(new UnsealAssetProps
{
AssetId = assetToUnseal.node.id
}, (response) =>
{
Debug.Log(response.status);
/// What are the assets that were unsealed
Debug.Log(response.assets);
});
}
});
In this snippet you'll need to get the assetId for the asset you are unsealing. You can get that from the test player's page in the dashboard, or you can run a separate assetHoldings
query.
mutation UnsealAsset {
unsealAsset(id: "insert asset id") {
status
assets {
fungibleQuantity
instanceQuantity
assetDefinition {
id
name
}
}
}
}
Composable Assets in Action: Use Case Examples
Let's look at two common examples of how Composable Assets can be put to work in a gaming context:
Creating a Randomized Loot Box
Loot boxes are a popular game mechanic. In this scenario, a RandomAsset can be used to simulate the randomness of a loot box. You would create a RandomAsset with a list of possible assets to be granted, each with their assigned odds. You would wrap this in a Sealed Asset so that the player will have a chance to explicitly open the box.
Here's an example of a recipe in the Subroutine Dashboard that would serve that specific purpose:
Celebratory Items Granted on a Specific Date
A great way to celebrate holidays or special events in your game is by granting a unique item on a specific date. Here, a TimedAsset can serve this purpose.
Create a new TimedAsset and set the release date to the date of the special event. The item will then appear in the player's inventory on the specified date. To make it more interactive, you could wrap this TimedAsset in a SealedAsset, so that players must take an explicit action to reveal their special gift.
These are just two examples of how Composable Assets can be utilized to create more engaging and interactive gaming experiences. With the ability to combine these assets in a variety of ways, the possibilities are vast. In the Dashboard's Asset Recipe section you can find a "Common templates" which you can browse from some common scenarios.
Summary
In this section you've learned about one of the most advanced parts of the Asset system. Composable Assets can come handy whenever you want to build a specific flow or lifecycle of an asset.