[UE4]C++实现动态加载的问题:LoadClass<T>()和LoadObject<T>() 及 静态加载问题:ConstructorHelpers::FClassFinder()和FObjectFinder()

转自:http://aigo.iteye.com/blog/2281558

动态加载UObject和动态加载UClass分别用LoadObject<T>(),和LoadClass<T>() ,两者均在在UObjectGlobals.h中。

另外注意:LoadClass<T>的模版名称,不能直接写UBlueprint,例如:LoadClass<UBlueprint>是错误的,创建蓝图时选择的是什么父类,则写对应的父类名,假如是Actor,那么要写成:LoadClass<AActor>,否则无法加载成功。

路径名也必须带_C后缀(LoadObject不需要带_C后缀),例如,蓝图路径是:Blueprint'/Game/Blueprints/MyBP.MyBP'

加后缀以后,则是:Blueprint'/Game/Blueprints/MyBP.MyBP_C',

例子:

UClass* Test = LoadClass<AActor>(NULL, TEXT("Blueprint'/Game/Blueprints/MapPathBrush_BP.MapPathBrush_BP_C'"));  

官方还没出文档,只能先看代码注释:

// Load an object.  
template< class T >   
inline T* LoadObject( UObject* Outer, const TCHAR* Name, const TCHAR* Filename=nullptr, uint32 LoadFlags=LOAD_None, UPackageMap* Sandbox=nullptr )  
{  
    return (T*)StaticLoadObject( T::StaticClass(), Outer, Name, Filename, LoadFlags, Sandbox );  
}  
// Load a class object.  
template< class T >   
inline UClass* LoadClass( UObject* Outer, const TCHAR* Name, const TCHAR* Filename=nullptr, uint32 LoadFlags=LOAD_None, UPackageMap* Sandbox=nullptr )  
{  
    return StaticLoadClass( T::StaticClass(), Outer, Name, Filename, LoadFlags, Sandbox );  
}  
/** 
 * Find or load an object by string name with optional outer and filename specifications. 
 * These are optional because the InName can contain all of the necessary information. 
 * 
 * @param ObjectClass   The class (or a superclass) of the object to be loaded. 
 * @param InOuter       An optional object to narrow where to find/load the object from 
 * @param InName        String name of the object. If it's not fully qualified, InOuter and/or Filename will be needed 
 * @param Filename      An optional file to load from (or find in the file's package object) 
 * @param LoadFlags     Flags controlling how to handle loading from disk 
 * @param Sandbox       A list of packages to restrict the search for the object 
 * @param bAllowObjectReconciliation    Whether to allow the object to be found via FindObject in the case of seek free loading 
 * 
 * @return The object that was loaded or found. NULL for a failure. 
 */  
COREUOBJECT_API UObject* StaticLoadObject( UClass* Class, UObject* InOuter, const TCHAR* Name, const TCHAR* Filename = NULL, uint32 LoadFlags = LOAD_None, UPackageMap* Sandbox = NULL, bool bAllowObjectReconciliation = true );  
COREUOBJECT_API UClass* StaticLoadClass(UClass* BaseClass, UObject* InOuter, const TCHAR* Name, const TCHAR* Filename = NULL, uint32 LoadFlags = LOAD_None, UPackageMap* Sandbox = NULL);  

 LoadObject加载例子,不需要添加后缀:

UTexture2D* Tex = LoadObject<UTexture2D>(NULL, TEXT("Texture2D'/Game/Textures/UI/tex_test001.tex_test001'"));  

可以用LoadObject加载的文件包括:

Texture、Material、SoundWave、SoundCue、ParticlesSystem、AnimMontage、BlendSpace(1D,2D,3D)、AnimSequence、AnimBlueprint、SkeletalMesh等等。这些文件的父类都是UObject,所以也可以先加载为UObject*然后再强转为具体的类型,例如:

UObject* Obj = LoadObject<UObject>(NULL, TEXT("SkeletalMesh'/Game/MyMesh.MyMesh'"));  
USkeletalMesh* MyMesh = Cast<USkeletalMesh*>(Obj);  

实现动态加载UObject:StaticLoadObject();以Texture和Material为例

示例1:

动态加载Object的工具方法

UTexture2D* MyTextureLoader::LoadTextureFromPath(const FString& Path)  
{  
    if (Path.IsEmpty()) return NULL;  
  
    return Cast<UTexture2D>(StaticLoadObject(UTexture2D::StaticClass(), NULL, *(Path)));  
}  
调用:
FString PathToLoad = "/Game/Textures/YourStructureHere";  
UTexture2D* tmpTexture = LoadTextureFromPath(PathToLoad);  

示例2:
加载MaterialTexture

struct FConstructorStatics  
 {  
     ConstructorHelpers::FObjectFinderOptional<UTexture> TextureFinder;  
     ConstructorHelpers::FObjectFinderOptional<UMaterial> MaterialFinder;  
     FConstructorStatics()  
         : TextureFinder(TEXT("Texture2D'/Game/Textures/2DBackground.2DBackground'"))  
         , MaterialFinder(TEXT("Material'/Game/Materials/DynamicTextureMaterial.DynamicTextureMaterial'"))  
     {  
     }  
 };  
 static FConstructorStatics ConstructorStatics;  
   
 Texture = ConstructorStatics.TextureFinder.Get();  
 UMaterial* Material = ConstructorStatics.MaterialFinder.Get();  
 DynamicMaterial = UMaterialInstanceDynamic::Create(Material, this);  

设置调用加载好的Material和Texture:

DynamicMaterial->SetTextureParameterValue(FName("DynamicTexture"), Texture);  
Mesh->SetMaterial(0, DynamicMaterial);  
 

如果资源永不再使用,想销毁资源对象,代码如下:

Texture2D* mytex; //这里假设mytex合法有效  
  
mytex->ConditionalBeginDestroy();  
mytex = NULL;  
GetWorld()->ForceGarbageCollection(true);  

Dynamic Asset Loading with C++

https://www.youtube.com/watch?v=pJIAmSGxfmQ

Dynamic Load Object

https://wiki.unrealengine.com/Dynamic_Load_Object

[UE4]C++静态加载问题:ConstructorHelpers::FClassFinder()和FObjectFinder()

这里说的静态加载指的是必须在构造函数中完成的加载方式,动态加载值得是可以在Runtime期间加载的方式,UE4源码里面,前者其实是对后者的一层封装,即FObjectFinder()是对LoadObject()的封装。But,FClassFinder()不是对LoadClass()的封装,FClassFinder()内部调用的是LoadObject()。

如果要获取某个蓝图BP的类型class,可以通过ConstructorHelpers::FClassFinder()来获取,例如:

 
static ConstructorHelpers::FClassFinder<AActor> UnitSelector(TEXT("Blueprint'/Game/MyProject/MyBlueprint.MyBlueprint'"));  
TSubclassOf<AActor> UnitSelectorClass = UnitSelector.Class;  

但是在启动游戏时会报错提示找不到文件,例如:

Default property warnings and errors:

Error: COD Constructor (MyGameMode): Failed to find /Game/MyProject/MyBlueprint.MyBlueprint

解决办法有两种(这是UE4的一个坑,浪费了我很长时间。。。):

A,在copy reference出来的文件路径后面加_C,例如:Blueprint'/Game/Blueprints/MyBlueprint.MyBlueprint_C'

static ConstructorHelpers::FClassFinder<AActor> UnitSelector(TEXT("Blueprint'/Game/Blueprints/MyBlueprint.MyBlueprint_C'"));  
TSubclassOf<AActor> UnitSelectorClass = UnitSelector.Class;  

B,去掉路径前缀:/Game/Blueprints/MyBlueprint

static ConstructorHelpers::FClassFinder<AActor> UnitSelector(TEXT("/Game/Blueprints/MyBlueprint"));  
TSubclassOf<AActor> UnitSelectorClass = UnitSelector.Class;  

另外注意:FClassFinder<T>的模版名称,不能直接写UBlueprint,例如:FClassFinder<UBlueprint>是错误的。创建蓝图时选择的是什么父类,则写对应的父类名,假如是Actor,那么要写成:FClassFinder<AActor>,否则无法加载成功。

使用TSubclassOf<T>时模板名必须相同

另外, FClassFinder<T>()函数中的模版名必须和TSubclassOf<T>变量的模版名一样,例如上面的都是AActor,如果不一样,也会出现上面的错误。
再给个例子:

static ConstructorHelpers::FClassFinder<UUserWidget> TestBP(TEXT("/Game/Blueprints/MyWidget_BP"));  
TSubclassOf<UUserWidget> MyWidgetClass = TestBP.Class;  

也可使用UClass*替换TSubclassOf<T>

例如:

static ConstructorHelpers::FClassFinder<UUserWidget> TestBP(TEXT("/Game/Blueprints/MyWidget_BP"));  
UClass* MyWidgetClass = TestBP.Class;  

之前看到很多例子是通过FObjectFinder()来获取class,现在想想感觉是无奈之举,UE4的文档比较坑,不仅蓝图的文档更新不同步,C++的文档更是少得可怜。

static ConstructorHelpers::FObjectFinder<UBlueprint> UnitSelector(TEXT("Blueprint'/Game/MyProject/MyBlueprint.MyBlueprint'"));  
TSubclassOf<AActor> UnitSelectorClass = (UClass*)UnitSelector.Object->GeneratedClass;  

其他参考:

CDO Constructor: Failed to find Blueprint

https://answers.unrealengine.com/questions/84880/cdo-constructor-failed-to-find-blueprint-ue-44.html

原文地址:https://www.cnblogs.com/sevenyuan/p/7728194.html