feat: add unreal-engine-cpp-pro
This commit is contained in:
94
skills/unreal-engine-cpp-pro/SKILL.md
Normal file
94
skills/unreal-engine-cpp-pro/SKILL.md
Normal file
@@ -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<T>()` 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<UHealthComponent>();
|
||||||
|
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<UInteractable>()) {
|
||||||
|
IInteractable::Execute_OnInteract(TargetActor, this);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Async Loading (Soft References)
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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`?
|
||||||
43
skills/unreal-engine-cpp-pro/examples/ExampleActor.cpp
Normal file
43
skills/unreal-engine-cpp-pro/examples/ExampleActor.cpp
Normal file
@@ -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<UBoxComponent>(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.
|
||||||
|
}
|
||||||
57
skills/unreal-engine-cpp-pro/examples/ExampleActor.h
Normal file
57
skills/unreal-engine-cpp-pro/examples/ExampleActor.h
Normal file
@@ -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<AActor> 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;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user