본문으로 건너뛰기
cccskills
무료GitHub에 공개

unreal-cpp-architecture

Use when writing Unreal Engine 5 C++ - the gameplay framework classes and where logic belongs, UObject/AActor/UActorComponent lifecycle, UPROPERTY/UFUNCTION and GC-safe pointers (TObjectPtr, TWeakObjectPtr), subsystems, the C++/Blueprint boundary, Enhanced Input, tick discipline, and compiling from the command line.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

설치 방법 보기

포함된 파일(1)

  • SKILL.md7.8 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Unreal C++ Architecture

Overview

Unreal gives you a full gameplay framework, a reflection system and a garbage collector. Most bugs in Unreal C++ come from fighting them: raw UObject* members the GC frees underneath you, gameplay logic in constructors that only ever run on the class default object, logic on the wrong framework class (GameMode code running on clients), and everything ticking every frame.

Core principle: Put each piece of logic on the framework class that owns it, keep every UObject reference visible to the GC, and let C++ own systems while Blueprints own content and tuning.

Where logic belongs

ClassLives onOwns
AGameModeBaseServer onlyRules of the match: spawning, scoring rules, win conditions
AGameStateBaseServer + replicated to allMatch state everyone sees (time left, team scores)
APlayerControllerOwning client + serverInput routing, UI ownership, camera for that player
APlayerStateReplicated to allPer-player persistent data (name, score); survives pawn death
APawn / ACharacterReplicatedThe body: movement, the components that act in the world
UActorComponentWith its actorReusable behaviour (health, inventory, interaction)
UGameInstanceSubsystem / UWorldSubsystem / ULocalPlayerSubsystemProcess / world / local playerServices (save, matchmaking, spawning registries) — instead of singletons

A client asking GetWorld()->GetAuthGameMode() gets nullptr — that is the design, not a bug to work around.

Lifecycle

AHeroCharacter::AHeroCharacter() {
    // Constructor: defaults and subobjects only. It runs for the class default object too,
    // so no world access, no spawning, no gameplay.
    PrimaryActorTick.bCanEverTick = false;
    Health = CreateDefaultSubobject<UHealthComponent>(TEXT("Health"));
}

void AHeroCharacter::BeginPlay() {
    Super::BeginPlay();                                   // always call Super
    Health->OnDied.AddDynamic(this, &AHeroCharacter::HandleDied);
}

void AHeroCharacter::EndPlay(const EEndPlayReason::Type Reason) {
    GetWorldTimerManager().ClearAllTimersForObject(this);
    Super::EndPlay(Reason);
}

Order to remember: constructor → PostInitializeComponents → BeginPlay → Tick → EndPlay. Components: InitializeComponent (if bWantsInitializeComponent) → BeginPlay.

Reflection and the garbage collector

UCLASS()
class UInventoryComponent : public UActorComponent {
    GENERATED_BODY()
public:
    UFUNCTION(BlueprintCallable, Category = "Inventory")
    bool TryAdd(const UItemDefinition* Item, int32 Count);

    DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnInventoryChanged, const UInventoryComponent*, Inventory);
    UPROPERTY(BlueprintAssignable) FOnInventoryChanged OnChanged;

private:
    // ❌ UItemDefinition* Cached;                       invisible to the GC: freed under you, then a crash
    // ✅ visible to the GC, editor-tunable, null-safe
    UPROPERTY(EditDefaultsOnly, Category = "Inventory", meta = (ClampMin = "1"))
    int32 SlotCount = 20;

    UPROPERTY(Transient) TArray<FInventorySlot> Slots;
    UPROPERTY() TObjectPtr<const UItemDefinition> LastAdded;
    TWeakObjectPtr<AActor> LastInteractor;             // non-owning: check IsValid() before use
};
  • Every UObject* member is a UPROPERTY (TObjectPtr<T> for members in UE5), a TWeakObjectPtr, or a TStrongObjectPtr (from non-UObject code). Raw pointers only as locals and parameters.
  • Create objects with NewObject<T>(Outer), CreateDefaultSubobject (constructor only) or GetWorld()->SpawnActor — never new.
  • IsValid(Ptr) before using anything that may have been destroyed (IsValid also catches "pending kill").
  • EditDefaultsOnly for tuning on the Blueprint class, EditAnywhere only when level instances should differ, VisibleAnywhere for components.

The C++ / Blueprint boundary

  • C++: systems, rules, replication, anything performance-sensitive or tested.
  • Blueprints: content (which mesh, which sound, which montage), tuning values, level scripting, and composition of C++ components.
  • Expose a small surface: BlueprintCallable for actions, BlueprintPure for queries, BlueprintImplementableEvent / BlueprintNativeEvent for designer hooks. No gameplay rule that exists only in a Blueprint graph nobody can test.
  • Blueprints reference heavy assets through soft references (TSoftObjectPtr) so loading one class does not pull the whole content graph (game-asset-pipeline).

Enhanced Input

void AHeroController::BeginPlay() {
    Super::BeginPlay();
    if (auto* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer())) {
        Subsystem->AddMappingContext(DefaultContext, /*Priority*/ 0);
    }
}

void AHeroCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) {
    auto* Input = CastChecked<UEnhancedInputComponent>(PlayerInputComponent);
    Input->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AHeroCharacter::Move);
    Input->BindAction(JumpAction, ETriggerEvent::Started, this, &ACharacter::Jump);
}

void AHeroCharacter::Move(const FInputActionValue& Value) {
    const FVector2D Axis = Value.Get<FVector2D>();
    AddMovementInput(GetActorForwardVector(), Axis.Y);
    AddMovementInput(GetActorRightVector(), Axis.X);
}

Input actions and mapping contexts are assets (data); the legacy BindAxis/BindAction string API is not used in new code.

Tick discipline

  • PrimaryActorTick.bCanEverTick = false by default; enable only where per-frame work is needed, with SetActorTickInterval where 60 Hz is not needed.
  • Timers (GetWorldTimerManager().SetTimer) and events instead of polling in Tick.
  • No TActorIterator, GetAllActorsOfClass or FindComponentByClass per frame — cache, or register with a subsystem.

Compile and project hygiene

# macOS (Win64: Engine/Build/BatchFiles/Build.bat ... Win64; Linux: Engine/Build/BatchFiles/Linux/Build.sh ... Linux)
"<UE>/Engine/Build/BatchFiles/Mac/Build.sh" <Project>Editor Mac Development \
  -Project="$PWD/<Project>.uproject" -WaitMutex > /tmp/tt-<task key>/build.log 2>&1; echo "exit=$?"
  • Compile the editor target before running automation tests; with -unattended, an out-of-date module fails instead of prompting.
  • New module dependencies go in <Module>.Build.cs (PublicDependencyModuleNames / PrivateDependencyModuleNames), e.g. EnhancedInput, GameplayAbilities.
  • Never commit Binaries/, Intermediate/, Saved/, DerivedDataCache/; .uasset/.umap go through LFS (asset-hygiene).
  • Log with a category: DEFINE_LOG_CATEGORY_STATIC(LogInventory, Log, All); UE_LOG(LogInventory, Warning, TEXT("..."));. check() for programmer errors that must crash in development; ensure() for recoverable ones.

Common Mistakes

  • Gameplay or world access in a constructor.
  • Raw UObject* members without UPROPERTY.
  • Forgetting Super:: calls in overridden lifecycle functions.
  • GameMode logic expected to run on clients.
  • Ticking actors that only need to react to events.

Red Flags

  • new UMyObject or new AMyActor anywhere.
  • GetAllActorsOfClass inside Tick.
  • A rule implemented only in a Blueprint graph with no C++ counterpart or test.
  • Binaries/ or Intermediate/ in the diff.

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Use when writing acceptance criteria for a task - express each as an observable Given/When/Then that QA can execute, including negative cases

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

makifbaysal/tasktrooper1172026년 10월 20일 업데이트

Use when the diff adds or changes an endpoint, resolver, RPC, job or query that takes an object id, a role check, a request binding or a tenant filter - BOLA/IDOR, function-level authorization, mass assignment and tenant scoping

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

makifbaysal/tasktrooper1172026년 10월 20일 업데이트

Use on every UI change - semantic HTML, labels for controls, keyboard-navigable dialogs/menus, visible focus, and never color as the only signal

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

makifbaysal/tasktrooper1172026년 10월 20일 업데이트

Use when a task changes any screen, form, dialog, menu or control - Lighthouse/axe scan of the changed screens, a keyboard walk, and the thresholds that fail a task

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

makifbaysal/tasktrooper1172026년 10월 20일 업데이트

How to work a task returned with review, QA or UAT findings. Use when a task is in need_revision or PR review comments are in your context.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

makifbaysal/tasktrooper1172026년 10월 20일 업데이트

Use when the repository keeps its alerting in code, or a person asks for alerts outside TaskTrooper - PrometheusRule YAML, a Cloud Monitoring alert policy in Terraform, a Datadog monitor, each routed to TaskTrooper's alert ingest URL so it opens an incident here

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

makifbaysal/tasktrooper1172026년 10월 20일 업데이트

makifbaysal의 스킬 모두 보기

이 스킬의 문제 신고