mcpField
U
ue-cpp-foundations

ue-cpp-foundations

github.com/quodsoler开发4191.4k 次安装4 周前更新
代码仓库

关于 ue-cpp-foundations

面向 Unreal Engine 5.8 C++ 开发的技能指南,覆盖反射、容器、委托、对象生命周期和子系统等基础 API,并提供版本核验说明与代码模式。

主要功能

  1. 01反射宏与属性说明
  2. 02容器和委托用法
  3. 03字符串与对象生命周期
  4. 04CDO 和非空参数
  5. 05引擎子系统模式

适用场景

  1. 01编写 UCLASS 和 UPROPERTY
  2. 02选择 UObject 指针与 GC 管理方式
  3. 03实现 C++ 委托和事件
  4. 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.md
delegate-patterns.md
property-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、DefaultConfigUPROPERTY(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
SpecifierEffect
BlueprintCallable / BlueprintPure带有 / 不带执行引脚的节点;BlueprintPure 不得更改状态
BlueprintNativeEventBlueprint 可以重写;C++ 默认函数体为 Name_Implementation
BlueprintImplementableEvent由 Blueprint 实现;完全没有 C++ 函数体
BlueprintAuthorityOnly / BlueprintCosmeticBlueprint 调用仅在具有网络权限时运行 / 绝不会在专用服务器上运行(直接调用 C++ 不会被过滤)
Server、Client、NetMulticast 搭配 Reliable 或 Unreliable、WithValidationRPC — 规则和示例见 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_DELEGATEN否C++ 事件(其他线程使用 DECLARE_TS_…)
DECLARE_DYNAMIC_DELEGATE1是可由 Blueprint 赋值的回调(仅限 UFUNCTION 目标)
DECLARE_DYNAMIC_MULTICAST_DELEGATEN是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。

类头文件所有者 / 生命周期访问器
UEngineSubsystemSubsystems/EngineSubsystem.hUEngine;整个进程期间GEngine->GetEngineSubsystem<T>()
UGameInstanceSubsystemSubsystems/GameInstanceSubsystem.hUGameInstance;切换地图后仍会保留GetGameInstance()->GetSubsystem<T>()
UWorldSubsystemSubsystems/WorldSubsystem.hUWorld;每个世界都会重新创建;可通过 DoesSupportWorldType 进行筛选GetWorld()->GetSubsystem<T>()
UTickableWorldSubsystemSubsystems/WorldSubsystem.h同上,另有 Tick();必须实现 GetStatId()GetWorld()->GetSubsystem<T>()
ULocalPlayerSubsystemSubsystems/LocalPlayerSubsystem.hULocalPlayer;每个本地玩家一个LocalPlayer->GetSubsystem<T>()、ULocalPlayer::GetSubsystemFromController<T>(PC)
UEditorSubsystemEditorSubsystem.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->ClassDefaultObjectGetDefault<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、FRefCountBaseFRefCountedObject(现已线程安全)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 导出宏、包含路径、PCH
  • ue-testing-debugging — 日志类别与详细程度、结构化日志、自动化测试、性能分析
  • ue-networking-replication — DOREPLIFETIME 条件、RPC 规则、推送模型、Iris
  • ue-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

评论

支持 Markdown