[UE4]C++静态加载问题的解决方法 : TSubclassOf<> 蓝图赋值为nullptr

本文介绍了在UE4中使用ConstructorHelpers::FClassFinder正确加载蓝图的方法,包括如何避免常见的路径错误,并强调了模板参数的重要性。
部署运行你感兴趣的模型镜像

原文地址:http://www.52vr.com/article-1014-1.html

我遇到了同样的问题,深有体会.


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

 

 

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

Cpp代码  
  1. static ConstructorHelpers::FClassFinder<AActor> UnitSelector(TEXT("Blueprint'/Game/MyProject/MyBlueprint.MyBlueprint'"));  
  2. 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'

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

   

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

 

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

 

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

 

 

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

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

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

 

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

例如:

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

 

 

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

Cpp代码  
  1. static ConstructorHelpers::FObjectFinder<UBlueprint> UnitSelector(TEXT("Blueprint'/Game/MyProject/MyBlueprint.MyBlueprint'"));  
  2. 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

您可能感兴趣的与本文相关的镜像

Qwen-Image-Edit-2509

Qwen-Image-Edit-2509

图片编辑
Qwen

Qwen-Image-Edit-2509 是阿里巴巴通义千问团队于2025年9月发布的最新图像编辑AI模型,主要支持多图编辑,包括“人物+人物”、“人物+商品”等组合玩法

// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "UObject/Class.h" #include <type_traits> class FStructuredArchiveSlot; template <typename T> class TSubclassOf; template <typename T> struct TIsTSubclassOf { enum { Value = false }; }; template <typename T> struct TIsTSubclassOf< TSubclassOf<T>> { enum { Value = true }; }; template <typename T> struct TIsTSubclassOf<const TSubclassOf<T>> { enum { Value = true }; }; template <typename T> struct TIsTSubclassOf< volatile TSubclassOf<T>> { enum { Value = true }; }; template <typename T> struct TIsTSubclassOf<const volatile TSubclassOf<T>> { enum { Value = true }; }; /** * Template to allow UClass types to be passed around with type safety */ template <typename T> class TSubclassOf { private: template <typename U> friend class TSubclassOf; public: using ElementType = T; [[nodiscard]] TSubclassOf() = default; [[nodiscard]] TSubclassOf(TSubclassOf&&) = default; [[nodiscard]] TSubclassOf(const TSubclassOf&) = default; TSubclassOf& operator=(TSubclassOf&&) = default; TSubclassOf& operator=(const TSubclassOf&) = default; ~TSubclassOf() = default; /** Constructor that takes a UClass*. */ [[nodiscard]] FORCEINLINE TSubclassOf(UClass* From) : Class(From) { } /** Construct from a UClass* (or something implicitly convertible to it) */ template < typename U UE_REQUIRES( !TIsTSubclassOf<std::decay_t<U>>::Value && std::is_convertible_v<U, UClass*> ) > [[nodiscard]] FORCEINLINE TSubclassOf(U&& From) : Class(From) { } /** Construct from another TSubclassOf, only if types are compatible */ template < typename OtherT UE_REQUIRES(std::is_convertible_v<OtherT*, T*>) > [[nodiscard]] FORCEINLINE TSubclassOf(const TSubclassOf<OtherT>& Other) : Class(Other.Class) { IWYU_MARKUP_IMPLICIT_CAST(OtherT, T); } /** Assign from another TSubclassOf, only if types are compatible */ template < typename OtherT UE_REQUIRES(std::is_convertible_v<OtherT*, T*>) > FORCEINLINE TSubclassOf& operator=(const TSubclassOf<OtherT>& Other) { IWYU_MARKUP_IMPLICIT_CAST(OtherT, T); Class = Other.Class; return *this; } /** Assign from a UClass*. */ FORCEINLINE TSubclassOf& operator=(UClass* From) { Class = From; return *this; } /** Assign from a UClass* (or something implicitly convertible to it). */ template < typename U UE_REQUIRES( !TIsTSubclassOf<std::decay_t<U>>::Value && std::is_convertible_v<U, UClass*> ) > FORCEINLINE TSubclassOf& operator=(U&& From) { Class = From; return *this; } /** Dereference back into a UClass*, does runtime type checking. */ [[nodiscard]] FORCEINLINE UClass* operator*() const { if (!Class || !Class->IsChildOf(T::StaticClass())) { return nullptr; } return Class; } /** Dereference back into a UClass*, does runtime type checking. */ [[nodiscard]] FORCEINLINE UClass* Get() const { return **this; } /** Dereference back into a UClass*, does runtime type checking. */ [[nodiscard]] FORCEINLINE UClass* operator->() const { return **this; } /** Implicit conversion to UClass*, does runtime type checking. */ [[nodiscard]] FORCEINLINE operator UClass*() const { return **this; } /** * Get the CDO if we are referencing a valid class * * @return the CDO, or null if class is null */ [[nodiscard]] FORCEINLINE T* GetDefaultObject() const { UObject* Result = nullptr; if (Class) { Result = Class->GetDefaultObject(); check(Result && Result->IsA(T::StaticClass())); } return (T*)Result; } FORCEINLINE void Serialize(FArchive& Ar) { Ar << Class; } FORCEINLINE void Serialize(FStructuredArchiveSlot& Slot) { Slot << Class; } [[nodiscard]] friend uint32 GetTypeHash(const TSubclassOf& SubclassOf) { return GetTypeHash(SubclassOf.Class); } [[nodiscard]] TObjectPtr<UClass>& GetGCPtr() { return Class; } #if DO_CHECK // This is a DEVELOPMENT ONLY debugging function and should not be relied upon. Client // systems should never require unsafe access to the referenced UClass [[nodiscard]] UClass* DebugAccessRawClassPtr() const { return Class; } #endif private: TObjectPtr<UClass> Class = nullptr; }; template <typename T> struct TCallTraits<TSubclassOf<T>> : public TCallTraitsBase<TSubclassOf<T>> { using ConstPointerType = TSubclassOf<const T>; }; template <typename T> FArchive& operator<<(FArchive& Ar, TSubclassOf<T>& SubclassOf) { SubclassOf.Serialize(Ar); return Ar; } template <typename T> void operator<<(FStructuredArchiveSlot Slot, TSubclassOf<T>& SubclassOf) { SubclassOf.Serialize(Slot); } 白话讲解功能作用 意义 示例说明用法
07-03
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值