Loading and creating objects for UE4 resources

Posted by homchz on Tue, 09 Nov 2021 19:07:09 +0100

Reference resources

Recommend: Resource Management for UE4

Several ways to load UE4 resources

UE4 Static/Dynamic Loading of Resources

Aery's UE4 C++ Game Development Tour (4) Loading Resources & Creating Objects

Custom Blueprint Gallery for reading pictures: Ue4 Simple Plugin Writing Teaching

Doubt

What are the resources in UE?

https://docs.unrealengine.com/4.26/zh-CN/Basics/AssetsAndPackages/
In UE4, all resource files in the project can be considered UObject s serialized into files

Relationship between resource files and in-memory objects in UE

Come from
Resource Management for UE4

UE4 resources are those non-code files in the project folder, such as the grid, materials, blueprints, etc. under Content. Most of the resources are suffixed with uasset, and others are suffixed with umap for map levels.

When packaging, these files may be made smaller platform-specific by cook, depending on the platform, and then placed in a compressed package with a suffix of pak. When the game runs, the program will mount and unzip the Pak packages, then load the resource files in the packages to use. For example, a Pak package is like Unity's AssettBundle package, and a uasset file is like a unit's. meta-managed resource file.

Programs do not use these files directly when they use resources, but instead convert them into UObjects or memory objects that other programs can use. For example, a grid resource file is actually a UStaticMesh object. Turning a resource file into a UObject object in memory is what resource management does.

For UE4, there are probably several steps to this process:

  1. Read data from resource file to memory
  2. Deserialize empty shell objects into actual objects based on in-memory binary data
  3. If the object has dependencies on other objects, do 1 and 2 operations recursively until the object is fully available
  4. Initialization function of the object is called and the object is added to the engine's object management

UE4 associates index resources through paths, and each resource has its own unique path.
Example

  • Red part: (StaticMesh)
    Is the type of resource, normally when loading resources, you can also not do it. Loading functions intercept this part of the path only in single quotes, so in fact this StaticMesh or Blueprint can not write before. Right-click resource copy path in editor Content Browser is this part, I guess the engine may keep this part for the user to know what his type is. More easily recognizable, this is just for people to see, the program does not look at this.

  • Green part: (/Game)
    Is the partition of the resource. Most resource paths start with / Game, which means the resource is under the Content directory of the game, or / Engine means under the engine, such as the following image, or the corresponding plug-in directory

  • Blue part: (/Geometry/Meshes)
    As in the Operating System File Manager, which folder is the resource under

  • Yellow part: (1M_Cube)
    The Package name of the resource, which is the name of the real resource file (uasset/umap) in which the resource resides. A package is actually a file that UE4 serializes objects to disk according to its own rules. Every file seen in Content Browser is a package

  • Purple part: (1M_Cube)
    The object name of the resource, because there may be multiple objects in the physical resource file, which uniquely identifies the unique name of each object within the package. For example, there are multiple UObject s in a blueprint resource, multiple Actor s in a level file, and multiple controls in a UI blueprint. If not written, some interfaces of UE4 will be appended with package names by default, that is, the same object name as the package name is used by default, but some interfaces may not be processed, so it is recommended to write. If you are not using the default object, but a class of resource objects, add a _ C, if it is a CDO object, add Default_u before

  • Section after colon
    Some resource paths are followed by a colon: followed by a file name, which is actually the child object name of the object. Some resource objects have child objects inside, such as subclasses in the C++ class. This is not commonly used and you can just know it

The difference between loading resources and creating objects?

Loading resources is actually deserializing to construct objects in memory

To create an object is to call the constructor of a class to construct it in memory

Resource Loading and Creating Objects

In UE4, all resource files in a project can be thought of as UObject s serialized into files. Loading resources is actually deserializing to construct objects in memory

Hard and soft references to resources

Reference resources:
Reference to <10>Resource

Resource hard reference:
That is, Object A references Object B and causes Object B to load when Object A loads. It is common to say that resources represented by a hard reference are loaded into memory when the reference is initialized, so there is little need for a hard-referenced resource to load a method.

Resource soft references:
Soft references, that is, Object A references Object B through indirect mechanisms such as object paths in the form of strings.

The problem with hard references is that it is easy to load all the resources represented by hard references at the beginning, which can lead to long resource load times. Soft references are references that allow you to load resources flexibly at any time without having to start hard.

UE mainly involves FSoftObjectPath, FSoftClassPath, TSoftObjectPtrr < T >, TSoftClassPtr < T >, when using these methods, you need to load resources manually (synchronous/asynchronous loading: LoadObject, StaticLoadObject, FStreamingManager)

FSoftObjectPath

FSoftObjectPath is a simple structure with a string containing the full name of the resource. It essentially represents the corresponding resource with a string, which allows you to find the target resource on the hard disk and load it into memory at any time.

FSoftObjectPath.SolveObject() :You can check to see if the resource it references is already loaded in memory and return the resource object pointer if it is loaded or null if it is not.
FSoftObjectPath.Reset() :Reset soft reference to null

AllowedClasses meta tag filters resource types

Example
// .h file
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "SoftObject", meta = (AllowedClasses = "SkeletalMesh, StaticMesh" ))
	FSoftObjectPath SoftObjectPath1;

UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "SoftObject", meta = (AllowedClasses = "Texture2D"))
	FSoftObjectPath SoftObjectPath2;

UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "SoftObject", meta = (AllowedClasses = "Blueprint Class"))
	FSoftObjectPath SoftObjectPath3;

UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "SoftObject", meta = (AllowedClasses = "Drone")) //Custom types are not recommended
	FSoftObjectPath SoftObjectPath4;
// .cpp file
void ADrone::BeginPlay()
{
	Super::BeginPlay();

	if (SoftObjectPath1.IsValid()){ /* Handle*/ }
	if (SoftObjectPath2.IsNull()){ /* Handle*/ }
	if (SoftObjectPath3.IsAsset()){ /* Handle*/ }
	FString SoftObjectPath4_AssetName = SoftObjectPath4.GetAssetName();			
	FString SoftObjectPath3_AssetPath = SoftObjectPath3.GetAssetPathString();
}

FStringAssetReference: It's actually just an alias that sounds easier to understand. It's actually like this in the UE4 source code:

typedef FSoftObjectPath FStringAssetReference;

FSoftClassPath
FSoftClassPath inherits from FSoftObjectPath and stores a type of soft reference, similar in usage to FSoftObjectPath
MetaClass meta tags filter class types

// .h file
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "SoftObjectClass")
	FSoftClassPath SoftClassPath;

UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "SoftObjectClass", meta = ( MetaClass= "Pawn"))
	FSoftClassPath SoftClassPath_Pawn;

TSoftObjectPtr< T >:
TSoftObjectPtr is a TWeakObjectPtr that contains the FSoftObjectPath and is a smart pointer that allows you to set specific resource types through template parameters, thereby limiting the editor UI to allow only specific resource types to be selected.

TSoftObjectPtr is the same as the SoftObjectReference in the blueprint

Can be used to get the object pointer corresponding to the resource when the callback function is triggered after asynchronous loading completes

TSoftObjectPtr.IsPending() : Method checks if a resource has been loaded into memory,Not loaded into memory, then true;Loaded, returned false

TSoftObjectPtr.Get() : If the resource it references is already loaded in memory, then the resource object pointer is returned.
Otherwise return empty.
To load resources into memory, you can call ToSoftObjectPath()To get FSoftObjectPaths For loading.

TSoftClassPtr< T >
Get a soft reference to the class and convert to UClass*

Example
// .h
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "SoftClassPtr")
	TSoftClassPtr<AActor> SoftClassPtr_Actor;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "SoftClassPtr")
	TSoftClassPtr<UUserWidget> SoftClassPtr_UserWidget;
// .cpp 

if (SoftClassPtr_Actor.IsPending())
{
	UClass* MyActor = SoftClassPtr_Actor.Get();
}

Several ways to load resources

Reference resources:
Reference Resources

UE4 Asynchronous Resource Loading - Reference

With regard to resource loading, I think there should be three types of requirements:

  • Resources are loaded into memory at the start of the game
  • Resources should be loaded synchronously on demand through a given path
  • Resources should be loaded asynchronously on demand through a given path

Here are some ways to reference resources in UE

  1. Direct Attribute Reference

Define a pointer to the resource type in the header file and set it to UPROPERTY(EditDefaultsOnly), assign the variable to the resource in the blueprint, and then set the resource directly by referencing the variable.

/** construction start sound stinger */
UPROPERTY(EditDefaultsOnly, Category=Mesh)
UStaticMesh* MeshData;

Clicking the Play button this way will load all by default.

  1. Load on Construction
#include "UObject/ConstructorHelpers.h" //Header file required to include

FObjectFinder<T> / FClassFinder<T>

Be careful:

  • Can only be used in class constructors, crash of the entire compiler can result if you try to create a method yourself and use FObjectFinder / FClassFinder to reference resources in this method. (
  • Second, the FObjectFinder/FClassFinder variable must be static to ensure that there is only one resource instance

FObjectFinder< T >:
Typically used to load non-blueprint resources, such as StaticMesh, Material, SoundWave, ParticlesSystem, AnimSequence, SkeletalMesh, and so on:

//Load material example
static ConstructorHelpers::FObjectFinder<UMaterialInterface> WoodMaterial(TEXT("Material'/Game/StarterContent/Materials/M_Wood_Walnut.M_Wood_Walnut'"));
if (WoodMaterial.Object)
{
    GetMesh()->SetMaterial(0, WoodMaterial.Object);
}

//Load Texture Example
static ConstructorHelpers::FObjectFinder<UTexture2D> ObjectFinder(TEXT("Texture2D'/Game/Textures/tex1.tex1'"));
UTexture2D* Texture2D = ObjectFinder.Object;

FClassFinder< T >:
It is typically used to load blueprint resources and obtain blueprint classes. This is because if C++ wants to create objects with blueprints, it must first get the Class of the blueprints, and then generate the blueprint objects from the Class:

static ConstructorHelpers::FClassFinder<AActor> BPClassFinder(TEXT("/Game/Blueprints/MyBP"));
TSubclassOf<AActor> BPClass = BPClassFinder.Class;
...//Generating blueprint objects using Class

Be careful:

  • The template name for FClassFinder cannot be written directly to UBlueprint, for example: FClassFinder < UBlueprint > is incorrect.
    What parent class was chosen to create the blueprint, write the corresponding parent name, if it is Actor, then write: FClassFinder < AActor>, otherwise it will not load successfully.

  • FClassFinder must have the same template name as the TSubclassOf variable, or UClass can be used instead of TSubclassOf. In fact, TSubclassOf < T > is also UClass, just to emphasize that this Class is derived from T.

  • Crash when starting a game when an error prompts you to not find a file (e.g. Default property warnings and errors:Error: COD Constructor (MyGameMode): Failed to find/Game/MyProject/MyBP.MyBP)
    This is due to a specification problem of the UE4 resource path, which can be solved in two ways:
    1. Add _to the file path of the copy reference C, for example, "Blueprint'/Game/Blueprints/MyBP.MyBP_C'" (_C can be understood as getting the meaning of Class).
    2. Remove the path prefix, for example: "/Game/Blueprints/MyBP"

  1. Indirect attribute reference

Indirect attribute references use the TSoftObjectPtr described earlier in Soft References.

This way you can control when the resource is loaded, and the load requires manual code loading, which is checked to see if it is loaded before loading. If it is not loaded, you need to use FStreamingManager to perform synchronous loading, and if it is already loaded, you can return the pointer to the resource object directly

Example

//You also need to manually determine if you need to load after you have set up resources in the blueprint
TSoftObjectPtr< UStaticMesh > MeshData;

void ASoftActor::DynamicUpdateStaticMesh()
{
	FStreamableManager& StreamableManager = UAssetManager::Get().GetStreamableManager();
	//TSoftObjectPtr.IsPending(): Method checks if a resource has been loaded into memory, not into memory, and then true; Loaded, returning false
	if (MeshData.IsPending())
	{
	    //TSoftObjectPtr.ToSoftObjectPath() to get FSoftObjectPaths for loading
		const FSoftObjectPath& AssetRef = MeshData.ToSoftObjectPath();
		//Synchronous loading of resources using FStreamableManager
		MeshData = Cast<UStaticMesh>(StreamableManager.LoadSynchronous(AssetRef));
		//TSoftObjectPtr..Get() Returns the resource object pointer if the resource it references is already loaded in memory, or null if it is already loaded.
		if (MeshData.Get())
		{
			BaseMesh->SetStaticMesh(MeshData.Get());
		}
	}
	else
	{
		if (MeshData.Get())
		{
			BaseMesh->SetStaticMesh(MeshData.Get());
		}
	}
}

in addition
TryLoad/LoadSynchronous

LoadSynchronous(): TSoftObjectPtr<T>The method also loads resources directly from the path.
TryLoad(): FSoftObjectPaths Method to load resources directly from a path.
  1. Runtime synchronous loading

[UE4]C++ implementation of dynamic loading issues: LoadClass() and LoadObject()

LoadObject<T>(): Load UObject,Usually used to load non-blueprint resources, such as animation, textures, sound effects, etc.

LoadClass<T>(): Load UClass,Usually used to load blueprint resources and obtain blueprints Class. Such as role blueprints.
                If you want to create objects with blueprints, you must first pass the LoadClass Obtain class,Then pass SpawnActor Generate objects.

Both are in UObjectGlobals.h Medium.

There are two other functions called: StaticLoadObject()and StaticLoadClass(),yes LoadObject()and LoadClass()Earlier versions,
The first two require manual override and filling in redundant parameters, while the second two are packages of the first two, which are more convenient to use and recommended for use.

This is the opposite of the construction-time reference mentioned in point 2, which allows you to load a resource at run time with the specified path.

The following is an example code:

//Example LoadObject usage
UMaterial *mt = LoadObject<UMaterial>(nullptr, TEXT("/Game/Map/Materials/grass.grass"));

//Example StaticLoadObject usage
UMaterial *mt = Cast<UMaterial>(StaticLoadObject(UMaterial::StaticClass(), nullptr, TEXT("/Game/Map/Materials/grass.grass")));

UStaticMesh* AMyActor::GetStaticMeshByName(const FString& AssetName){
	//"StaticMesh'/Engine/BasicShapes/Cube.Cube'"
	FString Paths = "StaticMesh'/Game/TestFolder/Product1/Geometries/";
	Paths.Append(AssetName).Append(".").Append(AssetName).Append("'");
	UStaticMesh* ReturnMesh = Cast<UStaticMesh>(StaticLoadObject(UStaticMesh::StaticClass(), NULL, *Paths));
	return ReturnMesh;
}

In addition,

There is also a potentially common global function, FindObject(), that queries whether a resource is loaded into memory and, if it exists, returns the resource object pointer or otherwise returns null. But we don't need to query before we use LoadXXX, because FindObject is inherently useful in LoadXXX to check for existence.

This approach does not provide a blueprint interface and calls in the blueprint need to be encapsulated in C++.
Examples of C++ encapsulation:
Create a FunctionLibrary and write the following code:

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "Engine/Texture2D.h"
#include "LoadFile.generated.h"

/**
 * 
 */
UCLASS()
class TWODTEST_API ULoadFile : public UBlueprintFunctionLibrary
{
    GENERATED_BODY()

    UFUNCTION(BlueprintCallable, meta = (DisplayName = "LoadTextureFromPath", keywords = "Load"), Category = "LoadFile")    
    UTexture2D* LoadTextureFromPath(const FString& Path)
    {
        if (Path.IsEmpty()) return NULL;

        return Cast<UTexture2D>(StaticLoadObject(UTexture2D::StaticClass(), NULL, *(Path)));
    }
};

Click on VS generation and UE4 compile to invoke in the blueprint:

  1. Asynchronous Loading

Asynchronous loading is required to avoid Karton when too many resources are loaded

Since the soft reference contains the full path name of the resource, instead of writing the path name again, call the member method as above to load the resource into memory.

FStreamableManager.RequestAsyncLoad()
First, the FStreamableManager needs to be created, and it is officially recommended that it be placed in a certain type of global game singleton object, such as one specified in DefaultEngine.ini using GameSingletonClassName.

FStreamableManager.RequestAsyncLoad(): Loads a set of resources asynchronously and invokes the delegate when finished.

void UGameCheatManager::GrantItems()
{      
       //Get a single object reference for FStreamableManager
       FStreamableManager& Streamable = ...;

       //Get a set of soft references
       TArray<FSoftObjectPath> ItemsToStream;
       for(int32 i = 0; i < ItemList.Num(); ++i)
           ItemsToStream.AddUnique(ItemList[i].ToStringReference());

       //Loads a set of resources asynchronously based on a set of soft references, and invokes the delegate after loading
       Streamable.RequestAsyncLoad(ItemsToStream, FStreamableDelegate::CreateUObject(this, &UGameCheatManager::GrantItemsDeferred));
}

void UGameCheatManager::GrantItemsDeferred()
{
       //do something....
}

The FStreamableManager also has a synchronous loading method: the SynchronousLoad() method performs a simple block loading and returns the object.

  1. Uninstall Resources

[UE4 C++] UObject creation, destruction, memory management
[UE4]UObject ConditionalBeginDestroy correlation

If the resource is never used and you want to uninstall the resource object from memory, the code is as follows:

Texture2D* mytex; //This assumes that mytex is valid and legal  

mytex->ConditionalBeginDestroy();  
mytex = NULL;  
GetWorld()->ForceGarbageCollection(true); 

create object

Objects in UE4 (i.e. class objects derived from UObjects) are best not to use C++ new/delete, but to use the object generation method provided by UE4, otherwise inheriting the garbage collection capabilities of UObjects is useless.

  1. Create General Object

If you have a derived class of UObject (non-Actor, non-Component), you can use the NewObject() template function to create its instance object:

UMyObject* MyObject = NewObject<UMyObject>();
  1. Create Actor Derived Class Object

Generating AActor derived class objects does not use NewObject or new, but UWorld::SpawnActor()

UWorld* World = GetWorld();
FVector pos(150, 0, 20);
AMyActor* MyActor = World->SpawnActor<AMyActor>(pos,FRotator::ZeroRotator);

Be careful SpawnActor It cannot be placed in a constructor, but it can be placed in functions of other periods, such as BeginPlay(),Tick()...Otherwise, it may be compiled crash. 
  1. Create Component Derived Class Object

To create components for an Actor, use the UObject::CreateDefaultSubobject() template function

UCameraComponent* Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera0"));

CreateDefaultSubobject Must be written in Actor In a parameterless constructor, otherwise crash. 
CreateDefaultSubobject In TEXT perhaps FName Parameters in the same Actor Cannot repeat in, otherwise crash. 
Be sure to add RegisterComponent(),Otherwise, the editor will not be displayed.
  1. Create Blueprint Object

Because blueprints are scripts in nature and not direct C++ classes, they often need to be generated by dynamic types. All ways to load resources and create them in a scene are dependent on the SpawnActor code.

  • Generate blueprint objects from identified parent classes
AMyActor* spawnActor = GetWorld()->SpawnActor<AMyActor>(AMyActor::StaticClass());  
If your blueprint derives from one C++class,Then you can directly access the StaticClass()And used for SpawnActor To create a blueprint object.
  • Generating blueprint objects from UClass
UClass* BPClass = LoadClass<AActor>(nullptr, TEXT("/Game/Blueprints/MyBP"));    //TSubclassOf<AActor>Same
AActor* spawnActor = GetWorld()->SpawnActor<AActor>(BPClass);
  • Generating blueprint objects from UObject s
If you get it UObject You need to convert to UBlueprint,Pass Again GeneratedClass Obtain UClass To generate blueprint objects

FStringAssetReference asset = "Blueprint'/Game/BluePrint/TestObj.TestObj'";  
UObject* itemObj = asset.ResolveObject();  
UBlueprint* gen = Cast<UBlueprint>(itemObj); 
if (gen != NULL){  
    AActor* spawnActor = GetWorld()->SpawnActor<AActor>(gen->GeneratedClass);  
}
  1. Copy Actor Derived Class Objects
AActor* CloneActor(AActor* InputActor)
{
   UWorld * World = InputActor->GetWorld();
   FActorSpawnParameters params;
   params.Template = InputActor;

	UClass* ItemClass = InputActor->GetClass();
	AActor* const SpawnedActor = World->SpawnActor<AActor>(ItemClass, params);
	return SpawnedActor;
}

UE4 AssetManager

AssetManager is a singleton UObject that UE4 provides developers with a resource management class for querying and reading Assets while runtime is in progress.

Usually UE4 automatically loads and unloads resources, but sometimes a project needs more control over resources and needs help.

Specific:

[UE4] Usage of UAssetManager for Resource Management

Unreal Engine 4 - Introduction to Asset Manager

Unreal Engine 4-Asset Manager and Resource Control in Fortnite

Resource Lookup

(UE4 4.20)UE4 gets the resource path for all the specific resources (FAssetData)

To be continued.

practice

C++

To be continued.

blueprint

  1. The blueprint dynamically loads the image texture onto the UI's Image control based on the path name (string)

Reference resources:

UE4 Image Asset From String
Make a real-time loading interface for UE4 (blueprint direction)

First: Where is the picture located? Web, computer files, or uasset resources for UE projects?

  • Pictures are in the uasset resource of the UE project
  • Pictures are in files on your computer

    There is also an example of loading multiple pictures by name (where Victory Load Texture 2D from File is a node provided by the plug-in and here is just a screenshot example), no plug-in can be replaced with Import File as Texture 2D.
  • Pictures on the Web
  1. The blueprint dynamically loads the media source according to the video name (string) to achieve dynamic loading during video playback

Walk through the folder first to get a dictionary with the video name string mapped to SoftObjectPath

Then load the video on demand based on the dictionary mapped to SoftObjectPath by the video name string above to generate the media file source used to play the video in UE

Topics: UE4