关于 ue-cpp-foundations
面向 Unreal Engine 5.8 C++ 开发的技能指南,覆盖反射、容器、委托、对象生命周期和子系统等基础 API,并提供版本核验说明与代码模式。
主要功能
- 01反射宏与属性说明
- 02容器和委托用法
- 03字符串与对象生命周期
- 04CDO 和非空参数
- 05引擎子系统模式
适用场景
- 01编写 UCLASS 和 UPROPERTY
- 02选择 UObject 指针与 GC 管理方式
- 03实现 C++ 委托和事件
- 04查询游戏或编辑器子系统
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ quodsoler/ unreal-engine-skills --skill ue-cpp-foundations安装后在 Claude Code 中输入/ue-cpp-foundations确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/container-patterns.mddelegate-patterns.mdproperty-specifiers.md详细介绍
UE C++ 基础
目标引擎:UE 5.8。以下 API 均已根据 5.8 头文件验证;较旧的形式列在“已弃用——请勿使用”下。
本技能涵盖每个 Unreal 模块构建所依赖的 C++ 层:反射宏(UCLASS、UPROPERTY、UFUNCTION、USTRUCT、UENUM、UINTERFACE)、容器、委托、字符串类型、对象生命周期与垃圾回收、CDO 访问,以及子系统系列。所有这些都位于 Core、CoreUObject 和 Engine 模块中(PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine" });)。编辑器子系统还需要在仅限编辑器的模块中添加 EditorSubsystem 和 UnrealEd。
上下文
如果 .agents/ue-project-context.md 存在,请阅读它(模块名称、约定、已启用的插件、GAS/网络设置)。如果该文件缺失,也不要停止。
根据请求和代码库识别相关领域。只有在两种合理解读会导致不同代码时才提问。
| 请求涉及…… | 前往 |
|---|---|
UCLASS、UPROPERTY、UFUNCTION、USTRUCT、UENUM 说明符 | 反射宏 |
TArray、TMap、TSet、TOptional、TVariant | 容器 |
委托、事件、AddDynamic、Broadcast | 委托 |
FName、FString、FText、LOCTEXT | 字符串类型 |
TObjectPtr、TWeakObjectPtr、TSharedPtr、GC 根、FGCObject | 对象生命周期与指针 |
类默认对象、GetDefault、TNotNull 参数 | CDO 访问与非空参数 |
| 游戏实例/世界/本地玩家/引擎/编辑器子系统 | 子系统 |
Replicated、ReplicatedUsing、GetLifetimeReplicatedProps | 复制属性 |
UE_LOG、日志类别 | 日志记录 |
WITH_EDITOR、UE_BUILD_SHIPPING 防护宏 | 条件编译防护 |
反射宏
每个反射类型的主体首行都必须是 GENERATED_BODY(),其头文件中的 #include "TypeName.generated.h" 必须是最后一条 include。游戏模块中的每个 UCLASS 都必须带有模块 API 宏(MYGAME_API)。说明符解析不区分大小写(引擎头文件中同时使用 config=Game 和 Config=Game)。完整的说明符和 meta= 表格见:references/property-specifiers.md。
UCLASS
| 说明符 | 效果 |
|---|---|
Blueprintable / BlueprintType | 允许创建 Blueprint 子类/用作 Blueprint 变量类型(Not… 形式会禁止这些用法) |
Abstract | 无法实例化;其子类可以 |
Config=Game、DefaultConfig | UPROPERTY(Config) 成员从 DefaultGame.ini 加载;DefaultConfig 也会将其保存到该文件 |
Transient | 实例永远不会被保存 |
ClassGroup=Name + meta=(BlueprintSpawnableComponent) | 组件会显示在“添加组件”菜单中 |
UPROPERTY
| 分组 | 说明符 |
|---|---|
| 编辑器可见性 | EditAnywhere、EditDefaultsOnly、EditInstanceOnly、VisibleAnywhere、VisibleDefaultsOnly、VisibleInstanceOnly、AdvancedDisplay |
| Blueprint 访问权限 | BlueprintReadWrite、BlueprintReadOnly、BlueprintGetter=Fn、BlueprintSetter=Fn、BlueprintAssignable(仅限多播委托) |
| 序列化 | Transient、DuplicateTransient、SaveGame、Config、GlobalConfig、Instanced、SkipSerialization、NonTransactional |
| 复制 | Replicated、ReplicatedUsing=OnRep_Fn、NotReplicated |
常用 meta= 键 | ClampMin/ClampMax、UIMin/UIMax、EditCondition、EditConditionHides、AllowPrivateAccess、DisplayName、ToolTip、Units、AllowedClasses、MustImplement、ExposeOnSpawn、TitleProperty |
// MyCharacter.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "MyCharacter.generated.h"
class UStaticMeshComponent;
class UStaticMesh;
UCLASS(Blueprintable)
class MYGAME_API AMyCharacter : public ACharacter
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Stats", meta=(ClampMin="1.0"))
float MaxHealth = 100.f;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Stats")
float CurrentHealth = 100.f;
UPROPERTY(Transient)
TObjectPtr<UStaticMeshComponent> CachedMesh; // not serialized, still GC-tracked
UPROPERTY(EditDefaultsOnly, Category="Config")
TSubclassOf<AActor> ProjectileClass; // class reference restricted to AActor subclasses
UPROPERTY(EditDefaultsOnly, Category="Config")
TSoftObjectPtr<UStaticMesh> LazyMesh; // path reference; load on demand
UFUNCTION(BlueprintNativeEvent, Category="Combat")
void OnDamageTaken(float Amount);
virtual void OnDamageTaken_Implementation(float Amount);
};
UFUNCTION
| Specifier | Effect |
|---|---|
BlueprintCallable / BlueprintPure | 带有 / 不带执行引脚的节点;BlueprintPure 不得更改状态 |
BlueprintNativeEvent | Blueprint 可以重写;C++ 默认函数体为 Name_Implementation |
BlueprintImplementableEvent | 由 Blueprint 实现;完全没有 C++ 函数体 |
BlueprintAuthorityOnly / BlueprintCosmetic | Blueprint 调用仅在具有网络权限时运行 / 绝不会在专用服务器上运行(直接调用 C++ 不会被过滤) |
Server、Client、NetMulticast 搭配 Reliable 或 Unreliable、WithValidation | RPC — 规则和示例见 ue-networking-replication |
Exec | 控制台命令;控制台会将其路由到 PlayerInput、PlayerController、Pawn、HUD、GameMode、CheatManager、GameState、PlayerCameraManager 和 GameInstance |
CallInEditor | 在 Details 面板中添加按钮 |
// MyCharacter.cpp — the only body a BlueprintNativeEvent gets in C++
void AMyCharacter::OnDamageTaken_Implementation(float Amount) { CurrentHealth = FMath::Max(0.f, CurrentHealth - Amount); }
函数库、潜伏和异步节点、WorldContext/DefaultToSelf/ExpandEnumAsExecs 以及其余面向 Blueprint 的 meta= 键:ue-blueprint-cpp-interop。
USTRUCT、UENUM、UINTERFACE
// MyTypes.h
#pragma once
#include "CoreMinimal.h"
#include "MyTypes.generated.h"
USTRUCT(BlueprintType)
struct MYGAME_API FMyWeaponStats
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Weapon")
float BaseDamage = 10.f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Weapon")
float FireRate = 0.5f;
};
UENUM(BlueprintType)
enum class EMyWeaponState : uint8 // Blueprint-visible enums are enum class : uint8
{
Idle,
Firing UMETA(DisplayName="Firing Weapon"),
MAX UMETA(Hidden)
};
UDataTable行类型是一个派生自FTableRowBase(Engine/DataTable.h)的USTRUCT;参见ue-data-assets-tables。- 在类内初始化每个
USTRUCT成员;反射结构体是值类型,可能会被引擎自由复制。 - 多态结构体值(任何派生自基类的
USTRUCT)存储为FInstancedStruct或带类型的TInstancedStruct<FMyBase>(StructUtils/InstancedStruct.h,模块CoreUObject)。 UINTERFACE将UMyThing : UInterface存根类与IMyThing类配对;说明符表和原生示例见 references/property-specifiers.md。可由 Blueprint 实现的接口见ue-blueprint-cpp-interop。
容器
完整 API、迭代和性能说明:见 references/container-patterns.md。只有作为 UPROPERTY() TArray<TObjectPtr<T>> 的 UObject 指针容器才会被 GC 跟踪(TMap/TSet 同理)。
TArray<FString> Names;
Names.Add(TEXT("Alpha"));
Names.Emplace(TEXT("Beta")); // construct in place
Names.Reserve(100); // pre-size before a known number of Adds
int32 Idx = Names.Find(TEXT("Beta")); // INDEX_NONE if absent
FString* Ptr = Names.FindByPredicate([](const FString& S) { return S.StartsWith(TEXT("A")); });
Names.Sort([](const FString& A, const FString& B) { return A.Len() < B.Len(); });
Names.Remove(TEXT("Alpha")); // order-preserving, O(n)
Names.RemoveAtSwap(0); // O(1), reorders
for (int32 i = Names.Num() - 1; i >= 0; --i) { if (Names[i].IsEmpty()) { Names.RemoveAt(i); } } // never mutate inside ranged-for
TMap<FName, int32> ItemCounts;
int32& Count = ItemCounts.FindOrAdd(FName("Sword")); // inserts default if absent
int32* Found = ItemCounts.Find(FName("Axe")); // nullptr if absent
for (const TPair<FName, int32>& Pair : ItemCounts) { UE_LOG(LogMyGame, Log, TEXT("%s x%d"), *Pair.Key.ToString(), Pair.Value); }
TSet<FName> Tags;
Tags.Add(FName("Flying"));
bool bFlying = Tags.Contains(FName("Flying")); // hashed, no duplicates
TOptional<float> MaybeHP;
float Safe = MaybeHP.Get(0.f); // default when unset
TVariant
#include "Misc/TVariant.h"
#include <type_traits>
TVariant<int32, float, FString> Value; // first type must be default-constructible (or use FEmptyVariantState)
Value.Set<FString>(TEXT("Hello"));
if (const FString* Str = Value.TryGet<FString>()) { UE_LOG(LogMyGame, Log, TEXT("%s"), **Str); }
Visit([](auto& Held) // one generic lambda; branch with if constexpr
{
using HeldType = std::decay_t<decltype(Held)>;
if constexpr (std::is_same_v<HeldType, FString>) { UE_LOG(LogMyGame, Log, TEXT("%s"), *Held); }
}, Value);
委托
所有声明宏、绑定方法和载荷规则:见 references/delegate-patterns.md。
| 宏系列 | 绑定数 | Blueprint | 用途 |
|---|---|---|---|
DECLARE_DELEGATE(_RetVal、_OneParam …) | 1 | 否 | 单一所有者回调,可以返回值 |
DECLARE_MULTICAST_DELEGATE | N | 否 | C++ 事件(其他线程使用 DECLARE_TS_…) |
DECLARE_DYNAMIC_DELEGATE | 1 | 是 | 可由 Blueprint 赋值的回调(仅限 UFUNCTION 目标) |
DECLARE_DYNAMIC_MULTICAST_DELEGATE | N | 是 | UPROPERTY(BlueprintAssignable) 事件 |
// MyHealthComponent.h — delegate types are declared at file scope, above the UCLASS
DECLARE_MULTICAST_DELEGATE_TwoParams(FOnMyHealthChangedNative, float /*Current*/, float /*Max*/);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnMyHealthChanged, float, CurrentHealth, float, MaxHealth);
// in UMyHealthComponent : public UActorComponent
UPROPERTY(BlueprintAssignable, Category="Health")
FOnMyHealthChanged OnHealthChanged; // dynamic: needs UPROPERTY, params need names
FOnMyHealthChangedNative OnHealthChangedNative; // native: no reflection, cheaper to broadcast
// in AMyListenerActor : public AActor
UFUNCTION() // AddDynamic targets must be UFUNCTIONs
void HandleHealthChanged(float CurrentHealth, float MaxHealth);
void HandleHealthChangedNative(float Current, float Max);
UPROPERTY() TObjectPtr<UMyHealthComponent> Health;
FDelegateHandle NativeHandle;
// AMyListenerActor.cpp — bind in BeginPlay (after Super::), unbind in EndPlay (before Super::)
Health->OnHealthChanged.AddDynamic(this, &AMyListenerActor::HandleHealthChanged);
NativeHandle = Health->OnHealthChangedNative.AddUObject(this, &AMyListenerActor::HandleHealthChangedNative);
// EndPlay: guard with IsValid(Health) first
Health->OnHealthChanged.RemoveDynamic(this, &AMyListenerActor::HandleHealthChanged);
Health->OnHealthChangedNative.Remove(NativeHandle);
所有者使用 OnHealthChanged.Broadcast(CurrentHealth, MaxHealth); 广播。两个类的完整示例:见 references/delegate-patterns.md。
单播委托使用 BindUObject、BindSP、BindRaw、BindStatic、BindLambda、BindWeakLambda(this, ...) 绑定,并使用 ExecuteIfBound(...) 触发。对于捕获 this 的 lambda,优先使用 AddWeakLambda/BindWeakLambda,这样对象销毁时绑定也会失效。
字符串类型
| 类型 | 用途 | 比较 | 说明 |
|---|---|---|---|
FName | 标识符、资源和骨骼名称、标签 | O(1) 整数比较,不区分大小写 | 在全局表中驻留;不可变 |
FString | 路径、解析、运行时构建的文本 | O(n) | 可变堆字符串;*Str 生成 const TCHAR* |
FText | 玩家阅读的所有内容 | — | 可本地化;使用 LOCTEXT/NSLOCTEXT/FText::Format 构建 |
float Health = 42.f; int32 Current = 3; int32 Max = 5;
FName Tag(TEXT("Weapon.Rifle"));
FString S = Tag.ToString();
FName Back = FName(*S);
FString Msg = FString::Printf(TEXT("HP: %.1f (%d/%d)"), Health, Current, Max);
int32 Parsed = FCString::Atoi(TEXT("42"));
#define LOCTEXT_NAMESPACE "MyGame"
FText Label = LOCTEXT("RifleName", "Assault Rifle");
FText Fmt = FText::Format(LOCTEXT("HPFmt", "HP: {0}/{1}"), FText::AsNumber(Current), FText::AsNumber(Max));
#undef LOCTEXT_NAMESPACE
FText Other = NSLOCTEXT("MyGame", "Key", "Text"); // no #define needed
FText Raw = FText::FromString(S); // not localized; runtime-generated text only
对象生命周期和指针
垃圾回收器会保活所有可从根对象通过反射(UPROPERTY)引用、FGCObject 引用器、TStrongObjectPtr 及已设为根的对象访问到的 UObject。垃圾回收器无法识别的指针可能会变成悬空指针。
// Reflected members: TObjectPtr — GC-tracked, access-tracked in the editor, used like a raw pointer
UPROPERTY() TObjectPtr<UStaticMeshComponent> MeshComp;
// Legacy APIs that want raw pointers
void FillObjects(TArray<UObject*>& Out);
TArray<TObjectPtr<UObject>> Objects;
FillObjects(MutableView(Objects)); // temporary TArray<UObject*>& view; ToRawPtr(Ptr) for a single read-only copy
// Non-owning reference: TWeakObjectPtr becomes null once the object is destroyed or collected
TWeakObjectPtr<AMyCharacter> WeakTarget;
if (AMyCharacter* Target = WeakTarget.Get()) { Target->OnDamageTaken(5.f); }
// Keep-alive from non-reflected code: scoped root instead of AddToRoot()/RemoveFromRoot()
TStrongObjectPtr<UMyDataObject> Pinned(NewObject<UMyDataObject>());
// Plain C++ types only: never wrap a UObject in TSharedPtr/TUniquePtr
struct FMyData { int32 Score = 0; };
TSharedPtr<FMyData> Data = MakeShared<FMyData>();
TWeakPtr<FMyData> WeakData = Data; // Pin() returns a TSharedPtr if still alive
持有 UObject 的非 UObject 类应继承 FGCObject,并通过 TObjectPtr 成员报告所持对象;AddReferencedObject(s) 的原始 UObject* 重载已弃用,且在增量垃圾回收下不安全。
#include "UObject/GCObject.h"
class FMyManager : public FGCObject
{
public:
virtual void AddReferencedObjects(FReferenceCollector& Collector) override { Collector.AddReferencedObject(ManagedObject); }
virtual FString GetReferencerName() const override { return TEXT("FMyManager"); }
private:
TObjectPtr<UMyDataObject> ManagedObject; // TObjectPtr<T>& overload; raw UObject* is deprecated
};
CDO 访问和非空参数
#include "Misc/NotNull.h"
const UMyDataObject* Defaults = GetDefault<UMyDataObject>(); // class default object, read-only
UMyDataObject* Settings = GetMutableDefault<UMyDataObject>(); // settings-style objects
UClass* SomeClass = AMyCharacter::StaticClass();
const AMyCharacter* ClassCDO = SomeClass->GetDefaultObject<AMyCharacter>(); // CDO of a runtime UClass
// TNotNull<T*>: caller cannot pass null; checked at the call site in Debug/Development, a plain pointer in Test and Shipping
void RegisterOwner(TNotNull<AActor*> Owner);
void RegisterOwner(TNotNull<AActor*> Owner)
{
Owner->SetActorHiddenInGame(false); // no null check needed inside
}
不建议直接访问 UClass::ClassDefaultObject。TNotNull<T*> 可隐式转换为 T*;如果空值具有实际意义,请保留 T*。
子系统
由引擎对象拥有的自动实例化单例;无需手动设为根。重写 Initialize(FSubsystemCollectionBase&)/Deinitialize();在顺序很重要时,于 Initialize 中调用 Collection.InitializeDependency<UMyOtherSubsystem>();如需排除某个子系统,可从 ShouldCreateSubsystem(UObject* Outer) const 返回 false。
| 类 | 头文件 | 所有者 / 生命周期 | 访问器 |
|---|---|---|---|
UEngineSubsystem | Subsystems/EngineSubsystem.h | UEngine;整个进程期间 | GEngine->GetEngineSubsystem<T>() |
UGameInstanceSubsystem | Subsystems/GameInstanceSubsystem.h | UGameInstance;切换地图后仍会保留 | GetGameInstance()->GetSubsystem<T>() |
UWorldSubsystem | Subsystems/WorldSubsystem.h | UWorld;每个世界都会重新创建;可通过 DoesSupportWorldType 进行筛选 | GetWorld()->GetSubsystem<T>() |
UTickableWorldSubsystem | Subsystems/WorldSubsystem.h | 同上,另有 Tick();必须实现 GetStatId() | GetWorld()->GetSubsystem<T>() |
ULocalPlayerSubsystem | Subsystems/LocalPlayerSubsystem.h | ULocalPlayer;每个本地玩家一个 | LocalPlayer->GetSubsystem<T>()、ULocalPlayer::GetSubsystemFromController<T>(PC) |
UEditorSubsystem | EditorSubsystem.h(模块 EditorSubsystem) | UEditorEngine;仅在编辑器会话期间 | GEditor->GetEditorSubsystem<T>() |
// MyInventorySubsystem.h
#pragma once
#include "CoreMinimal.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "MyInventorySubsystem.generated.h"
UCLASS()
class MYGAME_API UMyInventorySubsystem : public UGameInstanceSubsystem
{
GENERATED_BODY()
public:
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
virtual void Deinitialize() override;
UFUNCTION(BlueprintCallable, Category="Inventory")
void AddItem(FName ItemID, int32 Count);
private:
TMap<FName, int32> Inventory;
};
// A ticking world subsystem adds these; Initialize/Deinitialize must call Super:: (the base registers the ticker)
UCLASS()
class MYGAME_API UMyTickableSubsystem : public UTickableWorldSubsystem
{
GENERATED_BODY()
public:
virtual void Tick(float DeltaTime) override;
virtual TStatId GetStatId() const override { RETURN_QUICK_DECLARE_CYCLE_STAT(UMyTickableSubsystem, STATGROUP_Tickables); }
virtual bool DoesSupportWorldType(const EWorldType::Type WorldType) const override; // e.g. Game and PIE only
};
// Access from an AActor member function
UMyInventorySubsystem* Inv = GetGameInstance()->GetSubsystem<UMyInventorySubsystem>();
UMyTickableSubsystem* Tck = GetWorld()->GetSubsystem<UMyTickableSubsystem>();
APlayerController* PC = GetWorld()->GetFirstPlayerController();
UMyUISubsystem* UI = ULocalPlayer::GetSubsystemFromController<UMyUISubsystem>(PC); // null-safe
UMyEngineSubsystem* Eng = GEngine->GetEngineSubsystem<UMyEngineSubsystem>();
UWorldSubsystem 还提供 PostInitialize()、OnWorldBeginPlay(UWorld& InWorld)、OnWorldEndPlay(UWorld& InWorld) 和 OnWorldComponentsUpdated(UWorld& World) 重写;ULocalPlayerSubsystem::GetLocalPlayer<T>() 和 UGameInstanceSubsystem::GetGameInstance() 会返回所有者。
复制属性
必须同时指定说明符并添加 GetLifetimeReplicatedProps 条目。有关条件(COND_*)、RPC 规则、推送模型和 Iris,请参阅 ue-networking-replication。
// MyReplicatedActor.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyReplicatedActor.generated.h"
UCLASS()
class MYGAME_API AMyReplicatedActor : public AActor
{
GENERATED_BODY()
public:
AMyReplicatedActor() { bReplicates = true; }
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
protected:
UPROPERTY(ReplicatedUsing=OnRep_Health)
float Health = 100.f;
UFUNCTION()
void OnRep_Health(); // runs on clients after Health changes
};
// MyReplicatedActor.cpp
#include "MyReplicatedActor.h"
#include "Net/UnrealNetwork.h" // required for DOREPLIFETIME
void AMyReplicatedActor::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(AMyReplicatedActor, Health);
}
void AMyReplicatedActor::OnRep_Health() {}
日志
DECLARE_LOG_CATEGORY_EXTERN(LogMyGame, Log, All); // MyGame.h (module header)
DEFINE_LOG_CATEGORY(LogMyGame); // MyGame.cpp
UE_LOG(LogMyGame, Warning, TEXT("HP low: %.1f"), 12.5f);
详细级别(Logging/LogVerbosity.h):Fatal、Error、Warning、Display、Log、Verbose、VeryVerbose。Display 会同时输出到控制台和日志文件;Log 仅写入日志文件。Shipping 构建会将 UE_LOG 编译掉,除非目标将 bUseLoggingInShipping = true。有关类别、结构化日志(UE_LOGFMT)、运行时详细级别控制和性能分析,请参阅 ue-testing-debugging。
条件编译防护
#if WITH_EDITORONLY_DATA
UPROPERTY(EditAnywhere, Category="Debug")
bool bShowDebugSpheres = false; // reflected editor-only data
#endif
#if WITH_EDITOR
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; // editor-only code
#endif
#if !UE_BUILD_SHIPPING
void DrawDebugInfo(); // Debug, Development and Test only
#endif
已弃用 — 请勿使用
| 不要使用 | 在 5.8 中使用 | 来源 |
|---|---|---|
SomeClass->ClassDefaultObject | GetDefault<T>()、GetMutableDefault<T>()、SomeClass->GetDefaultObject<T>() | UObject/Class.h:4045 中的 UE_DEPRECATED(5.6) |
对带有 UObject* 成员的对象使用 Collector.AddReferencedObject(RawUObjectPtr) | 将 TObjectPtr<T> 成员传给 AddReferencedObject(s) | UObject/UObjectGlobals.h:109 中通过 UE_REFERENCE_COLLECTOR_REQUIRE_OBJECTPTR_DEPRECATED 标记的 UE_DEPRECATED(5.3) |
对可变数组使用 ToRawPtrArrayUnsafe(Array)、ToRawPtrTArrayUnsafe(Array) | 使用 MutableView(Array),或保留 TArray<TObjectPtr<T>> | UObject/ObjectPtr.h:1112,1156 中的 UE_DEPRECATED(5.6) |
Visit(<overload-set helper struct>{...}, Variant) | 使用一个泛型 lambda,并配合 if constexpr (std::is_same_v<...>) | 该辅助结构来自 Lyra 示例代码;5.8 头文件中不存在(Misc/TVariant.h) |
GENERATED_USTRUCT_BODY()、GENERATED_UCLASS_BODY()、GENERATED_IINTERFACE_BODY() | GENERATED_BODY() | UObject/ObjectMacros.h:802-805 中的旧版别名 |
将 Obj->AddToRoot() / RemoveFromRoot() 用于临时保活 | TStrongObjectPtr<T>(UObject/StrongObjectPtr.h) | 加入根集后会一直保留,直到将其移除;作用域指针不会泄漏 |
TMap::TIterator(Map, /*bRequiresRehashOnRemoval*/ true)(移除后自动调用 Relax) | Map.CreateIterator();如有需要,自行调用 Compact()/Shrink() | Containers/Map.h.inl:916 中的 UE_DEPRECATED(5.8);TSet::Relax() 本身尚未标记为弃用(Containers/SparseSet.h.inl:319) |
TIsConst<T>、TIsMemberPointer<T> | std::is_const_v<T>、std::is_member_pointer_v<T> | Templates/IsConst.h:15 中的 UE_DEPRECATED(5.7);Templates/IsMemberPointer.h:14 中的 UE_DEPRECATED(5.8) |
FThreadSafeRefCountedObject、FRefCountBase | FRefCountedObject(现已线程安全) | Templates/RefCounting.h:151-153 中的 UE_DEPRECATED(5.8) |
FCoreDelegates::OnPostEngineInit.AddRaw(...) | FCoreDelegates::GetOnPostEngineInit().AddRaw(...) | Misc/CoreDelegates.h:240 中的 UE_DEPRECATED(5.8) |
Delegate.ProcessMulticastDelegate<UObject>(Params) | Delegate.ProcessDelegate<UObject>(Params) | UObject/ScriptDelegates.h:1462 中的 UE_DEPRECATED(5.8) |
meta=(HideAssetPicker) | meta=(HidePinAssetPicker) | UObject/ObjectMacros.h:1673 中的 UE_DEPRECATED(5.6) |
ObjPtr.IsRemote() | ObjPtr.GetResidence() | UObject/ObjectPtr.h:206 中的 UE_DEPRECATED(5.8) |
常见错误
没有 UPROPERTY 的原始 UObject* 成员 — GC 后悬空:
UMyDataObject* Obj; // WRONG: invisible to GC
UPROPERTY() TObjectPtr<UMyDataObject> Obj; // RIGHT
在范围 for 循环中修改 TArray — 迭代器失效:
TArray<AActor*> Actors;
for (AActor* A : Actors) { Actors.Remove(A); } // WRONG
for (int32 i = Actors.Num() - 1; i >= 0; --i) { if (!IsValid(Actors[i])) { Actors.RemoveAt(i); } } // RIGHT
用 TSharedPtr 包装 UObject — 两个所有者,一个崩溃: UObject 由 GC 管理;请使用 TObjectPtr、TWeakObjectPtr 或 TStrongObjectPtr。
.generated.h 不是最后一个 include,或缺少 MYGAME_API: UnrealHeaderTool 会拒绝该头文件;缺少的 API 宏稍后会在其他模块中表现为 LNK2019。
将 AddDynamic 绑定到非 UFUNCTION: 反射查找会在运行时失败,处理函数也不会运行;请将其标记为 UFUNCTION()。
在 .cpp 定义上方写入 UPROPERTY/UFUNCTION: UnrealHeaderTool 只会解析头文件;该宏会被静默忽略。请将其放在类体内的声明上。
BlueprintNativeEvent 缺少 Name_Implementation: 链接时会出现未解析的外部符号。请声明 virtual void Name_Implementation(...) 并提供定义。
重写 UTickableWorldSubsystem::Initialize/Deinitialize 时未调用 Super::: Tick 注册逻辑位于基类实现中;子系统将不会执行 Tick。
直接读取 ClassDefaultObject: 已弃用,并且即将变为私有;请使用 GetDefault<T>()。
Log 详细级别“没有显示”: 按设计,Log 只写入文件;如需在控制台输出,请使用 Display。
相关技能
ue-actor-component-architecture— AActor/UActorComponent 生命周期、生成、Tick 组、组件设置ue-module-build-system— Build.cs、模块依赖、API 导出宏、包含路径、PCHue-testing-debugging— 日志类别与详细程度、结构化日志、自动化测试、性能分析ue-networking-replication— DOREPLIFETIME 条件、RPC 规则、推送模型、Irisue-blueprint-cpp-interop— UBlueprintFunctionLibrary、潜在操作和异步操作、可由 Blueprint 实现- 也相关:
ue-data-assets-tables、ue-animation-system、ue-async-threading、ue-audio-system、ue-editor-tools、ue-gameplay-abilities、ue-gameplay-framework、ue-gameplay-tags-messaging、ue-input-system、ue-mass-entity、ue-materials-rendering、ue-serialization-savegames、ue-state-trees、ue-ui-umg-slate、ue-world-level-streaming