FStreamableManager Question

I have PrimaryDataAsset:

class ODWIRKSRPG_API UItemDataAsset : public UPrimaryDataAsset
{
	GENERATED_BODY()

public:
        UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI", meta=(AssetBundles="UI"))
	TSoftObjectPtr<UTexture2D> ItemIcon;
...
};

and in the inventory component, I implement the method:

void UInventoryComponentBase::GetAllItemsInCategory(TEnumAsByte<EItemCategory> Category, bool& bSuccess,
	TArray<FPrimaryAssetId>& ItemIDs)
{
	UAssetManager& AssetManager = UAssetManager::Get();
	for (const FInventorySlot& Slot: Items)
	{
		const FPrimaryAssetId& ItemID = Slot.ItemId;

		FStreamableManager& StreamableManager = UAssetManager::Get().GetStreamableManager();

		TSharedPtr<FStreamableHandle> Handle = StreamableManager.RequestSyncLoad(
			{AssetManager.GetPrimaryAssetPath(ItemID)}
		);
		
		if (Handle.IsValid() && Handle->HasLoadCompleted())
		{
			if (UItemDataAsset* Item = Cast<UItemDataAsset>(Handle->GetLoadedAsset()))
			{
				if (Item->ItemCategory == Category)
				{	
					ItemIDs.Add(ItemID);
				}
			}
			Handle->ReleaseHandle();
		}
		
		bSuccess = !ItemIDs.IsEmpty();
	}
}

QUESTION: when I use the StreamableManager.RequestSyncLoad method, does it load the bandles as well or are they left unloaded?

if there are any tips on how to improve the method/code they are welcome

Thx.

Bundles default to unloaded and loading through the StreamableManager will not change any bundle states.

You can call:

  1. UAssetManager.LoadPrimaryAsset/LoadPrimaryAssets/LoadPrimaryAssetsWithType (which take a list of bundles to load along with the asset)
  2. UAssetManager.PreloadPrimaryAssets (also takes a list of bundles)
  3. UAssetManager.ChangeBundleStateForPrimaryAssets/ChangeBundleStateForMatchingPrimaryAssets (which can load or unload bundles on an already loaded primary asset).

These are all async operations that don’t have synchronous versions. (but you can use the handle’s WaitUntilComplete function to make it synchronous on your own)
PreloadPrimaryAssets works similar to the call you’re already making to the StreamableManager, where the assets are keep alive through references and the stream handle. The three other LoadPrimaryAsset functions keep the assets loaded through the AssetManager itself and they will remain loaded until you call UnloadPrimaryAsset/UnloadPrimaryAssets/UnloadPrimaryAssetsWithType.

1 Like

Thanks for the reply, I’m the opposite, I don’t need to load the bundles

Cool beans.

A couple things:

  1. Don’t use TEnumAsByte. Declare your enum as an enum class instead.
  2. You get the AssetManager before the loop, but then you get it every iteration to get the StreamableManager.
  3. Frankly this is a terrifying function. Either you should make 1 load request for all the elements you want, then loop over them. Or cache the category on the slot so that you can limit the assets you load to only the ones you actually need (and not just to check that member). Or just keep the asset loaded all the time (the expensive stuff is behind bundles anyway).
  4. You shouldn’t need to manually release the stream handle. That’ll release when it goes out of scope (the loop iteration, or the function body if you were to take my #3 to heart).
1 Like

WOW, thanks for the comments, I didn’t even notice that I was getting a StreamableManager every iteration.

void UInventoryComponentBase::GetAllItemsInCategory(TEnumAsByte<EItemCategory> Category, bool& bSuccess,
	TArray<FPrimaryAssetId>& ItemIDs)
{
	UAssetManager& AssetManager = UAssetManager::Get();
	TArray<FSoftObjectPath> PrimaryAssetsSoftObjectPaths;
	for (const FPrimaryAssetId& PrimaryAssetId : GetPrimaryAssetIds())
	{
		PrimaryAssetsSoftObjectPaths.Add(AssetManager.GetPrimaryAssetPath(PrimaryAssetId));
	}
	
	FStreamableManager& StreamableManager = UAssetManager::Get().GetStreamableManager();
	TSharedPtr<FStreamableHandle> Handle = StreamableManager.RequestSyncLoad(PrimaryAssetsSoftObjectPaths);
	if (Handle.IsValid() && Handle->HasLoadCompleted())
	{
		TArray<UObject*> LoadedAssets;
		Handle->GetLoadedAssets(LoadedAssets);

		for (UObject* const& LoadedAsset : LoadedAssets)
		{
			if (UItemDataAsset* Item = Cast<UItemDataAsset>(LoadedAsset))
			{
				if (Item->ItemCategory == Category)
				{
					ItemIDs.Add(Item->GetPrimaryAssetId());
				}
			}
		}

		Handle->ReleaseHandle();
	}
	
	bSuccess = !ItemIDs.IsEmpty();
}

What about this option?

It’s not something I’d like to see in one of my projects, but it’s probably fine for you and yours right now.

Thank you for your feedback