Happy to share what I’ve figured out, I need to do a bit more testing to make sure it’s exactly how I think it’s working but for now it’s working well enough. There is 1 scrap of documentation I found for this from some old fortnight code but it’s out of date as of 4.26.1 and really not clear regardless. so this is as a result of me digging through source, might need a bit of refinement but it works.
- the big piece that is not clear anywhere, if you want to load assets from outside the cooked project dynamically the asset type can’t already be being managed by the asset manager or it will fail. So you need to have the asset ID and item type not in asset manager, here’s where I commented out my DefaultGame.ini file to that effect when testing this.
Then Load the asset you want, in my case I’ve not done anything special, just created a new primary asset object, modified the values and serialized it. Here’s code snippet but I just create the object then return that. The GetSerializedAssetPath just makes a generic path string for me using the assetID and type, I don’t think you actualy need a path here but I’ve just done it for completeness. the path will look something like “/Game/Gy/GameState/assetType/assetID” in my case.
template< typename T >
T* UGyAssetMan::CreatePackagedAssetObject(const FName& assetID, const FPrimaryAssetType& assetType)
{
FString packagePath = GetSerializedAssetPath(assetID, assetType);
UPackage* package = CreatePackage(*packagePath);
return NewObject<T>(package, T::StaticClass(), *assetID.ToString(), EObjectFlags::RF_Public);
}
In the asset I’ve overridden the GetPrimaryAssetID() so it returns the asset ID in the format I want that then works with all my asset management code.
FPrimaryAssetId UGyPlayerOwner::GetPrimaryAssetId() const
{
Super::GetPrimaryAssetId();
return FPrimaryAssetId(ItemType, UniquePlayerID);
}
Then using pretty standard boiler plate code load the serrilized data I’ve loaded into the dynamic asset
template< typename T >
T* UGyAssetMan::LoadSerializedAsset(const FPrimaryAssetId& assetID)
{
FGySaveData dataRecord;
bool bLoadedData = LoadSerializedAsset(assetID, dataRecord);
FMemoryReader memoryReader(dataRecord.ObjectSerializedData);
FGySaveGameArchive archive(memoryReader);
T* obj = nullptr;
if (bLoadedData)
{
obj = CreatePackagedAssetObject<T>(assetID);
if (obj != nullptr)
{
obj->Serialize(archive);
}
}
return obj;
}
So now I have the dynamic asset created, if I want it around in memory so I can use the asset manager to get it in the future etc. I need to register it dynamically with the asset manager, this is the bit where there is no current documentation, I’ll type what I think is going on but I do need to test a bit more for loading bundles of assets, this example is just loading one asset.
So I get the PrimaryAssetId from the object I just created which is via the override I posted earlier.
I then create a FSoftObjectPath from the object, this just creates a string representation of the relevant bits of the Object that the asset manager knows how to deal with from what I can see. (I’ve also stated using these soft pointers in my serialized assets they are great for that as well)
FAssetBundleData you can just add multiple FSoftObjectPath it’s just a struct with an array in it so you can add multiple at once but you do the add dynamic call slightly differently.
Then I call AddDynamicAsset with those details which adds it to the asset manager (but doesn’t load it in the asset manager so you can’t search for it yet.)
If you want to add bundles instead you call it with an empty soft object path and it then looks at the FAssetBundleData instead from what I can see (I need to test that properly) so in my case it would be something like AddDynamic(playerAssetID, FAssetBundleData(), gB) to do that
Then you load the asset into the asset manager, the bundles in this case are your asset meta tags, now I’m not sure if these work dynamic or not this needs testing as well but I’ve really just kept my dynamic objects light with GUID references in them to anything else I might want to load and managing loading like that which is what the tags are for as well but I just found it easier, I might try tags again later. (something like UPROPERTY(…, meta = (AssetBundles = “UI”)) in the asset object)
bool UGyAssetMan::RegisterDynamicAsset(UObject* assetToLoad)
{
if (assetToLoad == nullptr)
{
return false;
}
UGyAssetManager& AssetManager = UGyAssetManager::Get();
if (!AssetManager.IsValid())
{
UE_LOG(GyLog, Warning, TEXT("UGyAssetMan::RegisterDynamicAsset AssetManagerNullPointer"))
return false;
}
//get the primary asset ID
FPrimaryAssetId playerAssetID = assetToLoad->GetPrimaryAssetId();
//get all the data from where the current object is
FSoftObjectPath assetPath(assetToLoad);
FAssetBundleData gB;
AssetManager.AddDynamicAsset(playerAssetID, assetPath, gB);
TArray<FName> bundles;
AssetManager.LoadPrimaryAsset(playerAssetID, bundles);
return true;
}
then it’s in memory and you can just reference it whenever you like with a one liner from anywhere in your code where UObject is whatever you object you want to cast to is.
UObject* obj = Cast<UObject>(AssetManager.GetPrimaryAssetObject(primaryAssetID));
I need to do a bit more work on it but that’s the gist of it from what I can see!
hope that’s helpful
