unreal-engine-cpp-pro

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Unreal Engine C++ Pro

Unreal Engine C++ 专家指南

This skill provides expert-level guidelines for developing with Unreal Engine 5 using C++. It focuses on writing robust, performant, and standard-compliant code.
本Skill为使用C++进行Unreal Engine 5开发提供专家级指导,重点在于编写健壮、高性能且符合标准的代码。

When to Use

适用场景

Use this skill when:
  • Developing C++ code for Unreal Engine 5.x projects
  • Writing Actors, Components, or UObject-derived classes
  • Optimizing performance-critical code in Unreal Engine
  • Debugging memory leaks or garbage collection issues
  • Implementing Blueprint-exposed functionality
  • Following Epic Games' coding standards and conventions
  • Working with Unreal's reflection system (UCLASS, USTRUCT, UFUNCTION)
  • Managing asset loading and soft references
Do not use this skill when:
  • Working with Blueprint-only projects (no C++ code)
  • Developing for Unreal Engine versions prior to 5.x
  • Working on non-Unreal game engines
  • The task is unrelated to Unreal Engine development
在以下场景中使用本Skill:
  • 为Unreal Engine 5.x项目开发C++代码
  • 编写Actor、Component或继承自UObject的类
  • 优化Unreal Engine中对性能要求严苛的代码
  • 调试内存泄漏或垃圾回收问题
  • 实现向Blueprint暴露的功能
  • 遵循Epic Games的编码标准与规范
  • 处理Unreal的反射系统(UCLASS、USTRUCT、UFUNCTION)
  • 管理资源加载与软引用
请勿在以下场景中使用本Skill:
  • 仅使用Blueprint的项目(无C++代码)
  • 为Unreal Engine 5.x之前的版本进行开发
  • 开发非Unreal引擎的游戏
  • 任务与Unreal Engine开发无关

Core Principles

核心原则

  1. UObject & Garbage Collection:
    • Always use
      UPROPERTY()
      for
      UObject*
      member variables to ensure they are tracked by the Garbage Collector (GC).
    • Use
      TStrongObjectPtr<>
      if you need to keep a root reference outside of a UObject graph, but prefer
      addToRoot()
      generally.
    • Understand the
      IsValid()
      check vs
      nullptr
      .
      IsValid()
      handles pending kill state safely.
  2. Unreal Reflection System:
    • Use
      UCLASS()
      ,
      USTRUCT()
      ,
      UENUM()
      ,
      UFUNCTION()
      to expose types to the reflection system and Blueprints.
    • Minimize
      BlueprintReadWrite
      when possible; prefer
      BlueprintReadOnly
      for state that shouldn't be trampled by logic in UI/Level BPs.
  3. Performance First:
    • Tick: Disable Ticking (
      bCanEverTick = false
      ) by default. Only enable it if absolutely necessary. Prefer timers (
      GetWorldTimerManager()
      ) or event-driven logic.
    • Casting: Avoid
      Cast<T>()
      in hot loops. Cache references in
      BeginPlay
      .
    • Structs vs Classes: Use
      F
      structs for data-heavy, non-UObject types to reduce overhead.
  1. UObject 与垃圾回收:
    • 所有
      UObject*
      成员变量务必使用
      UPROPERTY()
      包裹,确保被垃圾回收器(GC)追踪。
    • 若需要在UObject图之外保留根引用,可使用
      TStrongObjectPtr<>
      ,但通常优先使用
      addToRoot()
    • 理解
      IsValid()
      检查与
      nullptr
      的区别:
      IsValid()
      可安全处理待销毁(pending kill)状态。
  2. Unreal反射系统:
    • 使用
      UCLASS()
      USTRUCT()
      UENUM()
      UFUNCTION()
      将类型暴露给反射系统与Blueprint。
    • 尽可能减少使用
      BlueprintReadWrite
      ;对于不应被UI/关卡Blueprint逻辑修改的状态,优先使用
      BlueprintReadOnly
  3. 性能优先:
    • Tick函数:默认禁用Tick(
      bCanEverTick = false
      ),仅在绝对必要时启用。优先使用定时器(
      GetWorldTimerManager()
      )或事件驱动逻辑。
    • 类型转换:避免在热循环中使用
      Cast<T>()
      ,在
      BeginPlay
      中缓存引用。
    • 结构体 vs 类:对于数据密集型、非UObject类型,使用
      F
      前缀的结构体以减少开销。

Naming Conventions (Strict)

命名规范(严格遵循)

Follow Epic Games' coding standard:
  • Templates: Prefix with
    T
    (e.g.,
    TArray
    ,
    TMap
    ).
  • UObject: Prefix with
    U
    (e.g.,
    UCharacterMovementComponent
    ).
  • AActor: Prefix with
    A
    (e.g.,
    AMyGameMode
    ).
  • SWidget: Prefix with
    S
    (Slate widgets).
  • Structs: Prefix with
    F
    (e.g.,
    FVector
    ).
  • Enums: Prefix with
    E
    (e.g.,
    EWeaponState
    ).
  • Interfaces: Prefix with
    I
    (e.g.,
    IInteractable
    ).
  • Booleans: Prefix with
    b
    (e.g.,
    bIsDead
    ).
遵循Epic Games的编码标准:
  • 模板:前缀为
    T
    (例如:
    TArray
    TMap
    )。
  • UObject:前缀为
    U
    (例如:
    UCharacterMovementComponent
    )。
  • AActor:前缀为
    A
    (例如:
    AMyGameMode
    )。
  • SWidget:前缀为
    S
    (Slate组件)。
  • 结构体:前缀为
    F
    (例如:
    FVector
    )。
  • 枚举:前缀为
    E
    (例如:
    EWeaponState
    )。
  • 接口:前缀为
    I
    (例如:
    IInteractable
    )。
  • 布尔值:前缀为
    b
    (例如:
    bIsDead
    )。

Common Patterns

常见模式

1. Robust Component Lookup

1. 可靠的组件查找

Avoid
GetComponentByClass
in
Tick
. Do it in
PostInitializeComponents
or
BeginPlay
.
cpp
void AMyCharacter::PostInitializeComponents() {
    Super::PostInitializeComponents();
    HealthComp = FindComponentByClass<UHealthComponent>();
    check(HealthComp); // Fail hard in dev if missing
}
避免在
Tick
中使用
GetComponentByClass
,应在
PostInitializeComponents
BeginPlay
中执行。
cpp
void AMyCharacter::PostInitializeComponents() {
    Super::PostInitializeComponents();
    HealthComp = FindComponentByClass<UHealthComponent>();
    check(HealthComp); // Fail hard in dev if missing
}

2. Interface Implementation

2. 接口实现

Use interfaces to decouple systems (e.g., Interaction system).
cpp
// Interface call check
if (TargetActor->Implements<UInteractable>()) {
    IInteractable::Execute_OnInteract(TargetActor, this);
}
使用接口实现系统解耦(例如:交互系统)。
cpp
// Interface call check
if (TargetActor->Implements<UInteractable>()) {
    IInteractable::Execute_OnInteract(TargetActor, this);
}

3. Async Loading (Soft References)

3. 异步加载(软引用)

Avoid hard references (
UPROPERTY(EditDefaultsOnly) TSubclassOf<AActor>
) for massive assets which force load orders. Use
TSoftClassPtr
or
TSoftObjectPtr
.
cpp
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TSoftClassPtr<AWeapon> WeaponClassToLoad;

void AMyCharacter::Equip() {
    if (WeaponClassToLoad.IsPending()) {
        WeaponClassToLoad.LoadSynchronous(); // Or use StreamableManager for async
    }
}
避免对大型资源使用硬引用(
UPROPERTY(EditDefaultsOnly) TSubclassOf<AActor>
),这会强制加载顺序。应使用
TSoftClassPtr
TSoftObjectPtr
cpp
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TSoftClassPtr<AWeapon> WeaponClassToLoad;

void AMyCharacter::Equip() {
    if (WeaponClassToLoad.IsPending()) {
        WeaponClassToLoad.LoadSynchronous(); // Or use StreamableManager for async
    }
}

Debugging

调试

  • Logging: Use
    UE_LOG
    with custom categories.
    cpp
    DEFINE_LOG_CATEGORY_STATIC(LogMyGame, Log, All);
    UE_LOG(LogMyGame, Warning, TEXT("Health is low: %f"), CurrentHealth);
  • Screen Messages:
    cpp
    if (GEngine) GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("Died!"));
  • Visual Logger: extremely useful for AI debugging. Implement
    IVisualLoggerDebugSnapshotInterface
    .
  • 日志:使用带自定义分类的
    UE_LOG
    cpp
    DEFINE_LOG_CATEGORY_STATIC(LogMyGame, Log, All);
    UE_LOG(LogMyGame, Warning, TEXT("Health is low: %f"), CurrentHealth);
  • 屏幕消息:
    cpp
    if (GEngine) GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("Died!"));
  • 可视化日志:对AI调试极为有用,实现
    IVisualLoggerDebugSnapshotInterface
    接口。

Checklist before PR

提交PR前检查清单

  • Does this Actor need to Tick? Can it be a Timer?
  • Are all
    UObject*
    members wrapped in
    UPROPERTY
    ?
  • Are hard references (TSubclassOf) causing load chains? Can they be Soft Ptrs?
  • Did you clean up verified delegates in
    EndPlay
    ?
  • 该Actor是否需要Tick?能否改用定时器?
  • 所有
    UObject*
    成员是否都用
    UPROPERTY
    包裹?
  • 硬引用(TSubclassOf)是否导致加载链问题?能否改为软指针?
  • 是否已在
    EndPlay
    中清理已验证的委托?