From 01731166b519a1b99ee355150e58612071344e58 Mon Sep 17 00:00:00 2001 From: junited31 Date: Thu, 29 Jan 2026 19:04:42 +0900 Subject: [PATCH] feat: add unreal-engine-cpp-pro --- skills/unreal-engine-cpp-pro/SKILL.md | 94 +++++++++++++++++++ .../examples/ExampleActor.cpp | 43 +++++++++ .../examples/ExampleActor.h | 57 +++++++++++ 3 files changed, 194 insertions(+) create mode 100644 skills/unreal-engine-cpp-pro/SKILL.md create mode 100644 skills/unreal-engine-cpp-pro/examples/ExampleActor.cpp create mode 100644 skills/unreal-engine-cpp-pro/examples/ExampleActor.h diff --git a/skills/unreal-engine-cpp-pro/SKILL.md b/skills/unreal-engine-cpp-pro/SKILL.md new file mode 100644 index 00000000..0e6bfdde --- /dev/null +++ b/skills/unreal-engine-cpp-pro/SKILL.md @@ -0,0 +1,94 @@ +--- +name: unreal-engine-cpp-pro +description: Expert guide for Unreal Engine 5.x C++ development, covering UObject hygiene, performance patterns, and best practices. +--- + +# Unreal Engine C++ Pro + +This skill provides expert-level guidelines for developing with Unreal Engine 5 using C++. It focuses on writing robust, performant, and standard-compliant code. + +## 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()` in hot loops. Cache references in `BeginPlay`. + * **Structs vs Classes**: Use `F` structs for data-heavy, non-UObject types to reduce overhead. + +## 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`). + +## Common Patterns + +### 1. Robust Component Lookup +Avoid `GetComponentByClass` in `Tick`. Do it in `PostInitializeComponents` or `BeginPlay`. + +```cpp +void AMyCharacter::PostInitializeComponents() { + Super::PostInitializeComponents(); + HealthComp = FindComponentByClass(); + check(HealthComp); // Fail hard in dev if missing +} +``` + +### 2. Interface Implementation +Use interfaces to decouple systems (e.g., Interaction system). + +```cpp +// Interface call check +if (TargetActor->Implements()) { + IInteractable::Execute_OnInteract(TargetActor, this); +} +``` + +### 3. Async Loading (Soft References) +Avoid hard references (`UPROPERTY(EditDefaultsOnly) TSubclassOf`) for massive assets which force load orders. Use `TSoftClassPtr` or `TSoftObjectPtr`. + +```cpp +UPROPERTY(EditAnywhere, BlueprintReadWrite) +TSoftClassPtr 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`. + +## Checklist before 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`? diff --git a/skills/unreal-engine-cpp-pro/examples/ExampleActor.cpp b/skills/unreal-engine-cpp-pro/examples/ExampleActor.cpp new file mode 100644 index 00000000..8be9ecb7 --- /dev/null +++ b/skills/unreal-engine-cpp-pro/examples/ExampleActor.cpp @@ -0,0 +1,43 @@ +#include "ExampleActor.h" +#include "Components/BoxComponent.h" +#include "Kismet/GameplayStatics.h" + +// Define a static log category for this specific file/module +DEFINE_LOG_CATEGORY_STATIC(LogExampleActor, Log, All); + +AExampleActor::AExampleActor() +{ + // Default to strict settings + PrimaryActorTick.bCanEverTick = false; + PrimaryActorTick.bStartWithTickEnabled = false; + + RootCollision = CreateDefaultSubobject(TEXT("RootCollision")); + RootComponent = RootCollision; + + bIsActive = true; +} + +void AExampleActor::BeginPlay() +{ + Super::BeginPlay(); + + // Cache references here, not in Tick + CachedPC = UGameplayStatics::GetPlayerController(this, 0); + + if (bIsActive) + { + UE_LOG(LogExampleActor, Log, TEXT("ExampleActor %s started!"), *GetName()); + } +} + +void AExampleActor::EndPlay(const EEndPlayReason::Type EndPlayReason) +{ + // Clean up any strict delegates or handles here + Super::EndPlay(EndPlayReason); +} + +void AExampleActor::Tick(float DeltaTime) +{ + Super::Tick(DeltaTime); + // Ticking is disabled by default in constructor, so this won't run unless enabled explicitly. +} diff --git a/skills/unreal-engine-cpp-pro/examples/ExampleActor.h b/skills/unreal-engine-cpp-pro/examples/ExampleActor.h new file mode 100644 index 00000000..8aded623 --- /dev/null +++ b/skills/unreal-engine-cpp-pro/examples/ExampleActor.h @@ -0,0 +1,57 @@ +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/Actor.h" +#include "ExampleActor.generated.h" + +// Forward overrides to reduce compile times +class UBoxComponent; + +/** + * AExampleActor + * + * Demonstrates: + * 1. Correct class prefix (A) + * 2. UPROPERTY usage + * 3. Soft references for assets + */ +UCLASS() +class MYGAME_API AExampleActor : public AActor +{ + GENERATED_BODY() + +public: + // Sets default values for this actor's properties + AExampleActor(); + +protected: + // Called when the game starts or when spawned + virtual void BeginPlay() override; + + // Called when the game ends + virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; + +public: + // Called every frame + virtual void Tick(float DeltaTime) override; + + /** Component exposed to Blueprint, but immutable logic in C++ */ + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components") + UBoxComponent* RootCollision; + + /** + * Soft reference to an actor class to lazy load. + * Prevents hard reference chains. + */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Config") + TSoftClassPtr ActorTypeToSpawn; + + /** Proper boolean naming convention 'b' */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "State") + uint8 bIsActive:1; + +private: + /** Cached reference, not exposed to Blueprints */ + UPROPERTY(Transient) + APlayerController* CachedPC; +};