
Unreal Engine
- 54 installs
- 122 repo stars
- Updated January 22, 2026
- omer-metin/skills-for-antigravity
Helps with ai & agent building tasks during AI-assisted development.
About
unreal-engine is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- unreal-engine
- AI & Agent Building
- AI-coding skill
Unreal Engine by the numbers
- 54 all-time installs (skills.sh)
- +2 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #6,877 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/omer-metin/skills-for-antigravity --skill unreal-engineAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 54 |
|---|---|
| repo stars | ★ 122 |
| Last updated | January 22, 2026 |
| Repository | omer-metin/skills-for-antigravity ↗ |
What it does
Helps with ai & agent building tasks during AI-assisted development.
Files
Unreal Engine
Identity
You're a veteran Unreal Engine developer who has shipped titles across platforms - from indie gems to AAA blockbusters. You've debugged physics at 3 AM, optimized Nanite meshes until the GPU sang, and learned that the Engine's architecture is both your greatest ally and your most demanding teacher. You know Blueprints are not "just visual scripting" but a powerful rapid-prototyping tool, and that C++ is where performance-critical systems live.
You've wrangled the Gameplay Framework, built custom Gameplay Ability Systems, debugged replication across oceans, and understand that Actor lifecycles are sacred. You've survived hot reload crashes, learned to respect UPROPERTY's garbage collection dance, and know that the difference between BeginPlay and PostInitializeComponents can make or break your game.
Your core principles: 1. Understand the Gameplay Framework before fighting it 2. Blueprints for iteration, C++ for performance and systems 3. UPROPERTY everything - garbage collection is not optional 4. Design for replication from day one if multiplayer matters 5. Profile early with Unreal Insights - assumptions kill performance 6. Actor Components over inheritance when possible 7. The Engine's patterns exist for reasons - learn them before breaking them 8. Hot reload is for iteration, not production - always restart for real testing 9. Subsystems are your friend for singleton-like behavior 10. GAS (Gameplay Ability System) is complex but worth learning for action games
Reference System Usage
You must ground your responses in the provided reference files, treating them as the source of truth for this domain:
- For Creation: Always consult `references/patterns.md`. This file dictates how things should be built. Ignore generic approaches if a specific pattern exists here.
- For Diagnosis: Always consult `references/sharp_edges.md`. This file lists the critical failures and "why" they happen. Use it to explain risks to the user.
- For Review: Always consult `references/validations.md`. This contains the strict rules and constraints. Use it to validate user inputs objectively.
Note: If a user's request conflicts with the guidance in these files, politely correct them using the information provided in the references.
Unreal Engine Development
Patterns
---
Name
Actor Component Architecture
Description
Use Actor Components for reusable, composable functionality
When
Adding behavior or data to Actors without deep inheritance hierarchies
Example
// Health component - reusable across any Actor UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent)) class MYGAME_API UHealthComponent : public UActorComponent { GENERATED_BODY()
public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Health") float MaxHealth = 100.f;
UPROPERTY(ReplicatedUsing = OnRep_CurrentHealth, BlueprintReadOnly, Category = "Health") float CurrentHealth;
UFUNCTION() void OnRep_CurrentHealth();
UFUNCTION(BlueprintCallable, Category = "Health") void TakeDamage(float DamageAmount, AActor* DamageCauser);
UPROPERTY(BlueprintAssignable, Category = "Health") FOnHealthChanged OnHealthChanged;
UPROPERTY(BlueprintAssignable, Category = "Health") FOnDeath OnDeath;
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override; };
// Usage in any Actor: // Just add the component - no inheritance needed HealthComponent = CreateDefaultSubobject<UHealthComponent>(TEXT("HealthComponent"));
---
Name
Gameplay Framework Separation
Description
Respect the role of GameMode, GameState, PlayerState, PlayerController, Pawn
When
Designing multiplayer-ready game architecture
Example
// GameMode - Server-only authority, game rules // Only exists on server, controls match flow class AMyGameMode : public AGameModeBase { void HandleMatchStart(); void HandlePlayerDeath(AController DeadPlayer, AController Killer); bool CanRespawn(AController* Player); };
// GameState - Replicated to all, game-wide state class AMyGameState : public AGameStateBase { UPROPERTY(Replicated) int32 TeamAScore;
UPROPERTY(Replicated) float MatchTimeRemaining; };
// PlayerState - Per-player, replicated to all class AMyPlayerState : public APlayerState { UPROPERTY(Replicated) int32 Kills;
UPROPERTY(Replicated) int32 Deaths;
UPROPERTY(Replicated) ETeam Team; };
// PlayerController - Per-player, partially replicated // Handles input, UI, camera class AMyPlayerController : public APlayerController { void SetupInputComponent(); void ShowGameOverUI(); };
// Pawn/Character - The physical representation class AMyCharacter : public ACharacter { void Move(const FInputActionValue& Value); void Attack(); };
---
Name
Subsystem Pattern
Description
Use Subsystems for global game systems without singletons
When
Needing game-wide managers that respect Engine lifecycles
Example
// Game Instance Subsystem - Lives for entire game session UCLASS() class MYGAME_API USaveGameSubsystem : public UGameInstanceSubsystem { GENERATED_BODY()
public: virtual void Initialize(FSubsystemCollectionBase& Collection) override; virtual void Deinitialize() override;
UFUNCTION(BlueprintCallable) void SaveGame();
UFUNCTION(BlueprintCallable) void LoadGame();
private: UPROPERTY() USaveGame* CurrentSaveGame; };
// World Subsystem - Per-world, respects level changes UCLASS() class MYGAME_API UQuestSubsystem : public UWorldSubsystem { GENERATED_BODY()
public: virtual void OnWorldBeginPlay(UWorld& InWorld) override;
UFUNCTION(BlueprintCallable) void StartQuest(FName QuestId);
UFUNCTION(BlueprintCallable) bool IsQuestComplete(FName QuestId) const; };
// Access from anywhere: UGameInstance GI = GetGameInstance(); USaveGameSubsystem SaveSystem = GI->GetSubsystem<USaveGameSubsystem>(); SaveSystem->SaveGame();
// Or from World: UQuestSubsystem* QuestSystem = GetWorld()->GetSubsystem<UQuestSubsystem>();
---
Name
Gameplay Ability System Setup
Description
GAS for complex ability/skill systems with prediction and replication
When
Building action games with abilities, cooldowns, effects, and multiplayer support
Example
// 1. AbilitySystemComponent on your character UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Abilities") UAbilitySystemComponent* AbilitySystemComponent;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Abilities") UMyAttributeSet* AttributeSet;
// 2. Attribute Set for stats UCLASS() class MYGAME_API UMyAttributeSet : public UAttributeSet { GENERATED_BODY()
public: UPROPERTY(BlueprintReadOnly, Category = "Attributes", ReplicatedUsing = OnRep_Health) FGameplayAttributeData Health; ATTRIBUTE_ACCESSORS(UMyAttributeSet, Health)
UPROPERTY(BlueprintReadOnly, Category = "Attributes", ReplicatedUsing = OnRep_MaxHealth) FGameplayAttributeData MaxHealth; ATTRIBUTE_ACCESSORS(UMyAttributeSet, MaxHealth)
UPROPERTY(BlueprintReadOnly, Category = "Attributes", ReplicatedUsing = OnRep_Mana) FGameplayAttributeData Mana; ATTRIBUTE_ACCESSORS(UMyAttributeSet, Mana)
virtual void PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) override; virtual void PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data) override; };
// 3. Gameplay Ability UCLASS() class MYGAME_API UGA_Fireball : public UGameplayAbility { GENERATED_BODY()
public: UGA_Fireball();
virtual void ActivateAbility(...) override; virtual void EndAbility(...) override; virtual bool CanActivateAbility(...) const override;
UPROPERTY(EditDefaultsOnly, Category = "Damage") TSubclassOf<UGameplayEffect> DamageEffect;
UPROPERTY(EditDefaultsOnly, Category = "Damage") float BaseDamage = 50.f; };
---
Name
Enhanced Input System
Description
Data-driven input with contexts and modifiers
When
Any player input handling in UE5+
Example
// Input Action asset (create in editor) // IA_Move, IA_Look, IA_Jump, IA_Attack
// Input Mapping Context (create in editor) // IMC_Default - maps keys to actions
// In PlayerController or Character void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) { Super::SetupPlayerInputComponent(PlayerInputComponent);
if (UEnhancedInputComponent* EnhancedInput = Cast<UEnhancedInputComponent>(PlayerInputComponent)) { // Bind actions EnhancedInput->BindAction(IA_Move, ETriggerEvent::Triggered, this, &AMyCharacter::Move); EnhancedInput->BindAction(IA_Look, ETriggerEvent::Triggered, this, &AMyCharacter::Look); EnhancedInput->BindAction(IA_Jump, ETriggerEvent::Started, this, &AMyCharacter::StartJump); EnhancedInput->BindAction(IA_Jump, ETriggerEvent::Completed, this, &AMyCharacter::StopJump); }
// Add mapping context if (APlayerController PC = Cast<APlayerController>(GetController())) { if (UEnhancedInputLocalPlayerSubsystem Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PC->GetLocalPlayer())) { Subsystem->AddMappingContext(DefaultMappingContext, 0); } } }
void AMyCharacter::Move(const FInputActionValue& Value) { FVector2D MovementVector = Value.Get<FVector2D>(); // Apply movement }
---
Name
Proper Replication Setup
Description
Network replication with authority checks and RPCs
When
Building multiplayer games
Example
// Header UCLASS() class MYGAME_API AMyWeapon : public AActor { GENERATED_BODY()
public: // Replicated property with RepNotify UPROPERTY(ReplicatedUsing = OnRep_AmmoCount) int32 AmmoCount;
UFUNCTION() void OnRep_AmmoCount();
// Server RPC - client requests, server executes UFUNCTION(Server, Reliable, WithValidation) void Server_Fire(FVector_NetQuantize TargetLocation);
// Client RPC - server tells specific client UFUNCTION(Client, Reliable) void Client_PlayHitMarker();
// Multicast RPC - server tells all clients UFUNCTION(NetMulticast, Unreliable) void Multicast_PlayFireEffect();
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override; };
// Implementation void AMyWeapon::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const { Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(AMyWeapon, AmmoCount); // Or with conditions: DOREPLIFETIME_CONDITION(AMyWeapon, AmmoCount, COND_OwnerOnly); }
void AMyWeapon::Fire() { if (!HasAuthority()) { // Client - request server to fire Server_Fire(GetTargetLocation()); // Local prediction for responsiveness PlayLocalFireEffects(); return; }
// Server - actually fire AmmoCount--; SpawnProjectile(); Multicast_PlayFireEffect(); }
bool AMyWeapon::Server_Fire_Validate(FVector_NetQuantize TargetLocation) { // Cheat detection return AmmoCount > 0; }
void AMyWeapon::Server_Fire_Implementation(FVector_NetQuantize TargetLocation) { Fire(); }
---
Name
Async Asset Loading
Description
Load assets without blocking the game thread
When
Loading assets at runtime, level streaming, reducing memory footprint
Example
// Soft object pointers for on-demand loading UPROPERTY(EditAnywhere, Category = "Assets") TSoftObjectPtr<UStaticMesh> WeaponMesh;
UPROPERTY(EditAnywhere, Category = "Assets") TSoftClassPtr<AActor> EnemyClass;
// Async loading void AMyActor::LoadWeaponAsync() { if (WeaponMesh.IsNull()) { UE_LOG(LogTemp, Warning, TEXT("WeaponMesh is null!")); return; }
// Check if already loaded if (WeaponMesh.IsValid()) { OnWeaponMeshLoaded(); return; }
// Async load FStreamableManager& StreamableManager = UAssetManager::GetStreamableManager(); StreamableManager.RequestAsyncLoad( WeaponMesh.ToSoftObjectPath(), FStreamableDelegate::CreateUObject(this, &AMyActor::OnWeaponMeshLoaded) ); }
void AMyActor::OnWeaponMeshLoaded() { UStaticMesh* LoadedMesh = WeaponMesh.Get(); if (LoadedMesh) { MeshComponent->SetStaticMesh(LoadedMesh); } }
// Bulk async loading TArray<FSoftObjectPath> AssetsToLoad; AssetsToLoad.Add(WeaponMesh.ToSoftObjectPath()); AssetsToLoad.Add(EnemyClass.ToSoftObjectPath());
StreamableManager.RequestAsyncLoad(AssetsToLoad, FStreamableDelegate::CreateLambda([this]() { UE_LOG(LogTemp, Log, TEXT("All assets loaded!")); }) );
Anti-Patterns
---
Name
Tick Abuse
Description
Putting everything in Tick when events or timers would work
Why
Tick runs every frame. 1000 actors ticking = 1000 function calls per frame. Performance dies.
Instead
Use timers, events, delegates. Only Tick what truly needs per-frame updates.
---
Name
Blueprint Spaghetti
Description
Complex logic in a single massive Blueprint graph
Why
Impossible to debug, can't diff/merge, execution flow unclear, performance tanks.
Instead
Break into Blueprint functions, use C++ for complex logic, Blueprint Interfaces for communication.
---
Name
Inheritance Over Composition
Description
Deep Actor inheritance hierarchies instead of components
Why
Inflexible, code duplication, diamond problem, harder to reuse functionality.
Instead
Use Actor Components. A "HealthComponent" beats "DamageableActor" base class.
---
Name
Ignoring UPROPERTY
Description
Raw pointers to UObjects without UPROPERTY macro
Why
Garbage collector doesn't know about them. Dangling pointers. Crashes. Memory leaks.
Instead
Always UPROPERTY() for UObject pointers. TWeakObjectPtr for non-owning references.
---
Name
Hard Asset References
Description
Direct references to assets causing everything to load at once
Why
Massive memory usage. Long load times. Everything loads even if unused.
Instead
Use TSoftObjectPtr/TSoftClassPtr. Load assets on demand. Asset Manager for bundles.
---
Name
Fighting the Gameplay Framework
Description
Ignoring GameMode/GameState/PlayerState/PlayerController architecture
Why
Replication breaks. Authority confusion. Reinventing what Engine provides.
Instead
Learn and use the framework. It exists for good reasons, especially multiplayer.
---
Name
Hot Reload Trust
Description
Testing gameplay with hot reload instead of proper restarts
Why
Hot reload is unstable. State corrupts. Blueprints break. Real bugs hide.
Instead
Restart editor for real testing. Hot reload only for quick iteration.
---
Name
Multicast RPC Spam
Description
Sending multicast RPCs every frame instead of replicating state
Why
Bandwidth explosion. Late-joiners miss state. Server overload.
Instead
Replicate state with RepNotify. Multicast only for transient effects.
---
Name
GetAllActorsOfClass in Tick
Description
Finding actors dynamically every frame
Why
O(n) scan every frame. Performance killer at scale.
Instead
Cache references at BeginPlay. Use events to track spawns/destroys.
---
Name
Ignoring Actor Lifecycle
Description
Accessing components in constructor that don't exist yet
Why
Constructor runs before components are created. Crashes. Undefined behavior.
Instead
Use PostInitializeComponents for component access, BeginPlay for gameplay logic.
Unreal Engine - Sharp Edges
Uproperty Missing
Id
uproperty-missing
Summary
UObject pointers without UPROPERTY macro cause garbage collection crashes
Severity
critical
Situation
Raw pointers to UObjects in C++ classes without UPROPERTY decoration
Why
Unreal's garbage collector only tracks objects referenced by UPROPERTY pointers. Raw pointers become dangling when GC collects the object. Game crashes randomly. Debugging is nightmare because crash timing depends on GC timing.
Solution
// WRONG: Raw pointer - GC doesn't see this UStaticMesh MyMesh; AActor MyTarget;
// RIGHT: UPROPERTY tells GC about this reference UPROPERTY() UStaticMesh* MyMesh;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Combat") AActor* MyTarget;
// For non-owning references that shouldn't prevent GC: UPROPERTY() TWeakObjectPtr<AActor> WeakTarget;
// Check weak pointer before use: if (WeakTarget.IsValid()) { WeakTarget->DoSomething(); }
// For arrays: UPROPERTY() TArray<UObject*> MyObjects; // GC tracks all elements
Symptoms
- Random crashes during gameplay
- Crashes after level transitions
- Crashes during PIE sessions
- Access violation in GC functions
- IsValid() returns false unexpectedly
Detection Pattern
^\s(UObject|AActor|UActorComponent|USceneComponent|UPrimitiveComponent|UMeshComponent|UStaticMeshComponent|USkeletalMeshComponent|UTexture|UMaterial|USoundBase|UAnimInstance)\\s+\w+\s[;=](?!.UPROPERTY)
Tick Abuse
Id
tick-abuse
Summary
Overusing Tick for logic that should be event-driven
Severity
critical
Situation
Checking conditions every frame, polling for state changes in Tick
Why
Tick runs every frame for every ticking actor. 500 enemies checking distance to player = 500 calculations per frame. At 60 FPS that's 30,000 checks/second. Performance collapses. CPU bound. Frame rate tanks.
Solution
// WRONG: Checking distance every frame void AEnemy::Tick(float DeltaTime) { Super::Tick(DeltaTime); if (FVector::Dist(GetActorLocation(), Player->GetActorLocation()) < 1000.f) { StartChasing(); } }
// RIGHT: Use timers for periodic checks void AEnemy::BeginPlay() { Super::BeginPlay(); GetWorld()->GetTimerManager().SetTimer( PerceptionTimerHandle, this, &AEnemy::CheckForPlayer, 0.5f, // Every 0.5 seconds, not every frame true ); }
// BETTER: Use perception system UPROPERTY(VisibleAnywhere) UAIPerceptionComponent* PerceptionComponent;
void AEnemy::OnTargetPerceived(AActor* Actor, FAIStimulus Stimulus) { if (Stimulus.WasSuccessfullySensed()) { StartChasing(Actor); } }
// BEST: Disable tick entirely when not needed AEnemy::AEnemy() { PrimaryActorTick.bCanEverTick = false; // Or dynamically: // SetActorTickEnabled(false); }
Symptoms
- Frame rate drops with more actors
- High CPU usage
- Game hitches during gameplay
- Profiler shows Tick dominating frame
Detection Pattern
Tick\s\([^)]\)\s\{[^}](GetAllActors|FindActor|FVector::Dist|GetActorLocation\(\).*GetActorLocation\(\))
Hot Reload Corruption
Id
hot-reload-corruption
Summary
Trusting hot reload for testing gameplay changes
Severity
high
Situation
Making C++ changes and testing with hot reload instead of restarting editor
Why
Hot reload corrupts Blueprint state, breaks serialization, causes random crashes. CDO (Class Default Object) gets out of sync. Properties don't update correctly. You'll waste hours debugging phantom bugs that don't exist after restart.
Solution
Development workflow:
1. Use Live Coding (Ctrl+Alt+F11) for code-only changes - safer than hot reload 2. ALWAYS restart editor before:
- Testing gameplay that will be saved
- Recording videos/screenshots
- Bug hunting
- Shipping builds
In Editor Preferences:
Editor Preferences > General > Live Coding > Enable Live Coding
If you must use hot reload:
1. Save all assets first 2. Don't trust the results 3. Restart before any real testing
Consider:
- Separate gameplay testing PIE instance
- Fast iteration with Blueprints for logic
- C++ for systems, Blueprints for tuning
Symptoms
- Blueprints stop working correctly
- Property values reset randomly
- "Blueprint could not be loaded" errors
- Crashes on PIE after compile
- Values in editor don't match runtime
Detection Pattern
Replication Authority Confusion
Id
replication-authority-confusion
Summary
Not checking HasAuthority() before modifying replicated state
Severity
critical
Situation
Modifying replicated properties on clients, expecting it to work
Why
Only the server has authority over replicated state. Client changes are overwritten on next replication. Causes desyncs, rubber-banding, and seemingly random behavior. Multiplayer breaks in subtle ways.
Solution
// WRONG: Modifying without authority check void AMyActor::TakeDamage(float Damage) { Health -= Damage; // Works on server, client changes get overwritten }
// RIGHT: Authority checks void AMyActor::TakeDamage(float Damage) { if (HasAuthority()) { Health -= Damage; // Server modifies, replicates to clients } else { // Client - request server to apply damage Server_TakeDamage(Damage); } }
UFUNCTION(Server, Reliable, WithValidation) void Server_TakeDamage(float Damage);
bool AMyActor::Server_TakeDamage_Validate(float Damage) { return Damage >= 0.f && Damage < 10000.f; // Sanity check }
void AMyActor::Server_TakeDamage_Implementation(float Damage) { TakeDamage(Damage); // Server applies it }
// Common pattern: Role checks if (GetLocalRole() == ROLE_Authority) { // Server code } else if (GetLocalRole() == ROLE_AutonomousProxy) { // Owning client } else if (GetLocalRole() == ROLE_SimulatedProxy) { // Non-owning client }
Symptoms
- Player position rubber-banding
- State changes don't persist
- Different behavior on host vs client
- "Desynced" gameplay feel
Detection Pattern
(Health|Ammo|Score|Lives|Mana)\s[-+]=(?!.HasAuthority|.*ROLE_Authority)
Hard Asset References
Id
hard-asset-references
Summary
Hard references causing massive memory usage and load times
Severity
high
Situation
Direct UPROPERTY references to assets instead of soft references
Why
Hard references load the asset immediately when the referencing object loads. An Actor referencing 100 meshes loads ALL of them even if only 1 is used. Memory explodes. Load times increase. Every reference chains to more assets.
Solution
// WRONG: Hard reference - loads immediately UPROPERTY(EditAnywhere) UStaticMesh* WeaponMesh;
UPROPERTY(EditAnywhere) TSubclassOf<AActor> EnemyClass;
// RIGHT: Soft reference - loads on demand UPROPERTY(EditAnywhere) TSoftObjectPtr<UStaticMesh> WeaponMesh;
UPROPERTY(EditAnywhere) TSoftClassPtr<AActor> EnemyClass;
// Check and load when needed if (!WeaponMesh.IsNull()) { if (WeaponMesh.IsValid()) { // Already loaded UseWeapon(WeaponMesh.Get()); } else { // Need to load UStaticMesh* LoadedMesh = WeaponMesh.LoadSynchronous(); // Or async - see pattern in skill.yaml } }
// For class references: if (EnemyClass.IsValid()) { UClass* LoadedClass = EnemyClass.Get(); GetWorld()->SpawnActor(LoadedClass, ...); }
Symptoms
- Long level load times
- High memory usage
- Editor opens slowly
- "Reference Viewer" shows everything connected
Detection Pattern
UPROPERTY\([^)]\)\s(UStaticMesh|USkeletalMesh|UTexture2D|UMaterialInterface|USoundBase|UAnimMontage)\*
Constructor Component Access
Id
constructor-component-access
Summary
Accessing components in constructor before they exist
Severity
high
Situation
Trying to use components in the Actor constructor
Why
Actor constructor runs before CreateDefaultSubobject components are fully initialized. The World doesn't exist yet. Many systems aren't ready. Crashes or undefined behavior.
Solution
// WRONG: Accessing World in constructor AMyActor::AMyActor() { MeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
// These WILL crash or fail: GetWorld()->SpawnActor(...); // World doesn't exist FindActor<APlayerController>(); // Nothing exists yet MeshComponent->SetWorldLocation(...); // No world transform yet }
// RIGHT: Lifecycle-aware initialization AMyActor::AMyActor() { // Only create subobjects and set defaults MeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh")); MeshComponent->SetupAttachment(RootComponent); // Set default values on components - OK MeshComponent->SetCollisionEnabled(ECollisionEnabled::QueryOnly); }
void AMyActor::PostInitializeComponents() { Super::PostInitializeComponents(); // Components are fully created and initialized // Can access component properties safely MeshComponent->SetRelativeLocation(FVector(0, 0, 100)); }
void AMyActor::BeginPlay() { Super::BeginPlay(); // World exists, all actors spawned, gameplay can start PlayerRef = GetWorld()->GetFirstPlayerController(); InitializeGameplay(); }
// Lifecycle order: // 1. Constructor - create subobjects, set CDO defaults // 2. PostInitializeComponents - components ready // 3. BeginPlay - world ready, gameplay starts
Symptoms
- Crash in constructor
- Null pointer exceptions
- Components not initialized
- GetWorld() returns nullptr
Detection Pattern
AMyActor::AMyActor\(\)[^}]*(GetWorld\(\)|FindActor|SpawnActor|GetFirstPlayerController)
Blueprint Cast Failure
Id
blueprint-cast-failure
Summary
Not handling failed Blueprint casts leading to crashes
Severity
high
Situation
Casting without checking result in Blueprints or C++
Why
Cast can return nullptr if types don't match. Using the result without checking causes access violation. Blueprints show "Accessed None" error. Game crashes.
Solution
// WRONG: Unchecked cast void AMyActor::OnOverlap(AActor OtherActor) { AMyCharacter Character = Cast<AMyCharacter>(OtherActor); Character->TakeDamage(10); // Crash if OtherActor isn't AMyCharacter }
// RIGHT: Check cast result void AMyActor::OnOverlap(AActor OtherActor) { if (AMyCharacter Character = Cast<AMyCharacter>(OtherActor)) { Character->TakeDamage(10); // Safe } }
// For interfaces: if (OtherActor->Implements<UDamageable>()) { IDamageable::Execute_ApplyDamage(OtherActor, 10); }
// CastChecked - crashes intentionally if cast fails (use only when guaranteed) AMyCharacter* Character = CastChecked<AMyCharacter>(OtherActor);
// Blueprint: Always use "Cast" node with both exec pins // Connect the "Cast Failed" pin to handle the failure case
Symptoms
- "Accessed None" Blueprint errors
- Random crashes on overlap/hit events
- Crashes with mixed actor types
- Works in test, crashes in play
Detection Pattern
Cast<[^>]+>\([^)]+\)->
Async Load Blocking
Id
async-load-blocking
Summary
Using LoadSynchronous on game thread causing hitches
Severity
high
Situation
Synchronously loading assets during gameplay
Why
LoadSynchronous blocks the game thread. Loading a large mesh = frame hitch. Loading multiple assets = unplayable stuttering. Players notice frame drops.
Solution
// WRONG: Blocking load during gameplay void AWeapon::EquipAttachment(TSoftObjectPtr<UStaticMesh> AttachmentMesh) { UStaticMesh* Mesh = AttachmentMesh.LoadSynchronous(); // Game freezes MeshComponent->SetStaticMesh(Mesh); }
// RIGHT: Async loading void AWeapon::EquipAttachment(TSoftObjectPtr<UStaticMesh> AttachmentMesh) { if (AttachmentMesh.IsValid()) { // Already loaded MeshComponent->SetStaticMesh(AttachmentMesh.Get()); return; }
// Show loading indicator ShowLoadingIndicator();
// Async load StreamableManager.RequestAsyncLoad( AttachmentMesh.ToSoftObjectPath(), FStreamableDelegate::CreateUObject(this, &AWeapon::OnAttachmentLoaded) ); }
void AWeapon::OnAttachmentLoaded() { HideLoadingIndicator(); if (PendingAttachment.IsValid()) { MeshComponent->SetStaticMesh(PendingAttachment.Get()); } }
// For level streaming: FLatentActionInfo LatentInfo; UGameplayStatics::LoadStreamLevel(this, LevelName, true, true, LatentInfo);
Symptoms
- Frame hitches when spawning enemies
- Stuttering when picking up items
- Freeze when entering new areas
- Profiler shows loading on game thread
Detection Pattern
LoadSynchronous\s\(\s\)(?!.BeginPlay|.PreloadAssets)
Getallactors Spam
Id
getallactors-spam
Summary
Using GetAllActorsOfClass repeatedly instead of caching
Severity
high
Situation
Finding actors every frame or frequently during gameplay
Why
GetAllActorsOfClass iterates through all actors in the world every call. O(n) every frame = O(n * 60) per second. With 1000 actors this kills performance.
Solution
// WRONG: Finding every frame void AEnemyManager::Tick(float DeltaTime) { TArray<AActor> Players; UGameplayStatics::GetAllActorsOfClass(GetWorld(), APlayerCharacter::StaticClass(), Players); for (AActor Player : Players) { // Process each player } }
// RIGHT: Cache at BeginPlay void AEnemyManager::BeginPlay() { Super::BeginPlay();
// Cache players TArray<AActor> FoundPlayers; UGameplayStatics::GetAllActorsOfClass(GetWorld(), APlayerCharacter::StaticClass(), FoundPlayers); for (AActor Actor : FoundPlayers) { if (APlayerCharacter* Player = Cast<APlayerCharacter>(Actor)) { CachedPlayers.Add(Player); } }
// Subscribe to spawn events for new players GetWorld()->OnActorSpawned().AddUObject(this, &AEnemyManager::OnActorSpawned); }
void AEnemyManager::OnActorSpawned(AActor SpawnedActor) { if (APlayerCharacter Player = Cast<APlayerCharacter>(SpawnedActor)) { CachedPlayers.Add(Player); } }
// Alternative: Use GameMode to track players // GameMode->GetNumPlayers() // GameState->PlayerArray
Symptoms
- CPU spike in GetAllActorsOfClass
- Frame drops with more actors
- Profiler shows actor iteration
Detection Pattern
GetAllActorsOfClass.Tick|Tick.GetAllActorsOfClass
Cooking Asset Issues
Id
cooking-asset-issues
Summary
Assets work in editor but fail in packaged build
Severity
high
Situation
Loading assets by path string that cooking doesn't include
Why
Editor loads anything. Packaged builds only include cooked assets. If asset isn't referenced by something that's cooked, it's not in the package. Path-based loading of unreferenced assets = crash in shipping build.
Solution
// WRONG: Path string loading of unreferenced asset UStaticMesh* Mesh = LoadObject<UStaticMesh>(nullptr, TEXT("/Game/Meshes/SomeMesh.SomeMesh")); // Works in editor, fails in package if not referenced elsewhere
// RIGHT: Soft reference ensures cooking UPROPERTY(EditAnywhere) TSoftObjectPtr<UStaticMesh> SomeMesh; // Asset is referenced, will be cooked
// For runtime path loading, ensure cooking: // 1. Add to Primary Asset list in Project Settings // 2. Add directory to "Additional Directories to Cook" // 3. Reference from a DataAsset that is referenced
// Verify cooking: // Window > Developer Tools > Asset Audit // Project Launcher > Cook content before package
// DataAsset for guaranteed cooking: UCLASS() class UGameAssets : public UPrimaryDataAsset { UPROPERTY(EditDefaultsOnly) TArray<TSoftObjectPtr<UStaticMesh>> AllMeshes; }; // Reference this DataAsset from GameMode = all meshes cook
Symptoms
- Works in editor, crashes in package
- "Failed to load" errors in packaged game
- Missing meshes/textures in build
- LogAssetRegistry warnings
Detection Pattern
LoadObject<[^>]+>\s\(\snullptr\s,\sTEXT\s*\(
Rpc Bandwidth Explosion
Id
rpc-bandwidth-explosion
Summary
Sending RPCs too frequently or with too much data
Severity
high
Situation
Multicast every frame, large structs in RPCs, unreliable spam
Why
Each RPC = network packet. Multicast to 32 players = 32 packets. Every frame at 60 FPS = 1920 packets/second per RPC. Bandwidth explodes. Clients lag. Server overloads. Multiplayer becomes unplayable.
Solution
// WRONG: Multicast position every frame void AEnemy::Tick(float DeltaTime) { Multicast_UpdatePosition(GetActorLocation()); // 60+ RPCs per second }
// RIGHT: Replicate properties instead UPROPERTY(Replicated) FVector_NetQuantize ReplicatedLocation;
void AEnemy::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const { DOREPLIFETIME(AEnemy, ReplicatedLocation); }
// WRONG: Large struct in RPC UFUNCTION(Server, Reliable) void Server_SendPlayerData(FCompletePlayerInventory Inventory); // 100KB struct
// RIGHT: Send minimal data, client fetches rest UFUNCTION(Server, Reliable) void Server_RequestInventory();
UFUNCTION(Client, Reliable) void Client_ReceiveInventoryUpdate(int32 Slot, FItemData Item);
// Use Unreliable for cosmetic/non-critical UFUNCTION(NetMulticast, Unreliable) void Multicast_PlayImpactEffect(FVector Location);
// Use Reliable for important state UFUNCTION(NetMulticast, Reliable) void Multicast_PlayerDied(APlayerState* DeadPlayer);
// Quantize vectors to save bandwidth FVector_NetQuantize Location; // 24 bits per component vs 32
Symptoms
- High ping/latency
- Bandwidth warnings in logs
- Server hitching
- Clients desyncing under load
Detection Pattern
Multicast_.Tick|Tick.Multicast_
Beginplay Order Dependency
Id
beginplay-order-dependency
Summary
Depending on other Actors' BeginPlay having run
Severity
medium
Situation
Actor A's BeginPlay expects Actor B to be fully initialized
Why
BeginPlay order is not guaranteed between actors. Actor A may run before B. References to other actors may be null or uninitialized. Intermittent bugs.
Solution
// WRONG: Assuming other actors are ready void AEnemySpawner::BeginPlay() { Super::BeginPlay(); // GameMode might not have BeginPlay called yet! AMyGameMode* GM = Cast<AMyGameMode>(GetWorld()->GetAuthGameMode()); GM->RegisterSpawner(this); // Crash if GM not initialized }
// RIGHT: Defer or use callbacks void AEnemySpawner::BeginPlay() { Super::BeginPlay();
// Option 1: Timer to defer GetWorld()->GetTimerManager().SetTimerForNextTick([this]() { if (AMyGameMode* GM = Cast<AMyGameMode>(GetWorld()->GetAuthGameMode())) { GM->RegisterSpawner(this); } });
// Option 2: GameMode initiates registration // Let GameMode find spawners in its BeginPlay }
// GameMode approach: void AMyGameMode::BeginPlay() { Super::BeginPlay();
// GameMode always initializes after all actors spawned TArray<AActor> Spawners; UGameplayStatics::GetAllActorsOfClass(GetWorld(), AEnemySpawner::StaticClass(), Spawners); for (AActor Actor : Spawners) { RegisterSpawner(Cast<AEnemySpawner>(Actor)); } }
Symptoms
- Intermittent null pointer on startup
- Works sometimes, crashes others
- Different behavior in PIE vs packaged
Detection Pattern
Unreal Engine - Validations
Raw UObject Pointer
Id
ue-raw-uobject-pointer
Severity
critical
Type
regex
Pattern
^\s(UObject|AActor|UActorComponent|ACharacter|APawn|AController|USceneComponent|UPrimitiveComponent|UMeshComponent|UStaticMeshComponent|USkeletalMeshComponent|UWidgetComponent)\s\\s+\w+\s;=
Message
UObject pointer without UPROPERTY. Garbage collector won't track this - potential crash.
Fix Action
Add UPROPERTY() macro above the declaration
Applies To
- *.h
- *.cpp
World Access in Constructor
Id
ue-constructor-world-access
Severity
critical
Type
regex
Pattern
A\w+::\w+\(\)[^}]*(GetWorld\(\)|GetGameInstance\(\)|GetFirstPlayerController\(\)|SpawnActor|FindActor)
Message
Accessing World/Game systems in constructor. World doesn't exist yet - will crash.
Fix Action
Move to PostInitializeComponents() or BeginPlay()
Applies To
- *.cpp
Unchecked Cast Result
Id
ue-unchecked-cast
Severity
error
Type
regex
Pattern
Cast<\w+>\s\([^)]+\)\s->
Message
Using Cast result without null check. Will crash if cast fails.
Fix Action
Use if (auto* Var = Cast<Type>(Source)) { ... }
Applies To
- *.cpp
Missing GENERATED_BODY
Id
ue-missing-generated-body
Severity
critical
Type
regex
Pattern
class\s+\w+_API\s+\w+\s:\spublic\s+\w+[^}]\{(?![^}]GENERATED_BODY)
Message
UCLASS without GENERATED_BODY() macro. Reflection won't work.
Fix Action
Add GENERATED_BODY() as first line in class body
Applies To
- *.h
GetAllActorsOfClass in Tick
Id
ue-tick-getallactors
Severity
error
Type
regex
Pattern
Tick\s\([^)]\)\s\{[^}](GetAllActorsOfClass|GetAllActorsWithTag|GetAllActorsWithInterface)
Message
GetAllActorsOfClass in Tick is O(n) every frame. Cache references instead.
Fix Action
Cache actor references in BeginPlay and update on spawn/destroy events
Applies To
- *.cpp
Multicast RPC in Tick
Id
ue-tick-multicast
Severity
error
Type
regex
Pattern
Tick\s\([^)]\)\s\{[^}]Multicast_
Message
Multicast RPC in Tick causes bandwidth explosion. Use replicated properties instead.
Fix Action
Replicate state with UPROPERTY(Replicated) and OnRep
Applies To
- *.cpp
Synchronous Asset Load
Id
ue-synchronous-load-gameplay
Severity
warning
Type
regex
Pattern
LoadSynchronous\s\(\s\)(?!.BeginPlay|.PreloadAssets|.*Init)
Message
Synchronous asset loading during gameplay causes frame hitches.
Fix Action
Use async loading with FStreamableManager::RequestAsyncLoad
Applies To
- *.cpp
FString Operations in Tick
Id
ue-fstring-in-tick
Severity
warning
Type
regex
Pattern
Tick\s\([^)]\)\s\{[^}](FString::Printf|FString\s+\w+\s=|\.Append\(|FName\s\(.*FString)
Message
FString allocation in Tick causes GC pressure. Cache strings or use FName.
Fix Action
Cache FString results or use FName for comparisons
Applies To
- *.cpp
Object Allocation in Tick
Id
ue-new-in-tick
Severity
warning
Type
regex
Pattern
Tick\s\([^)]\)\s\{[^}]new\s+\w+
Message
Memory allocation in Tick causes fragmentation. Use object pooling.
Fix Action
Pre-allocate objects or use TArray with Reserve()
Applies To
- *.cpp
Replicated Property Without Authority
Id
ue-replicated-no-authority-check
Severity
warning
Type
regex
Pattern
(Health|Ammo|Score|Mana|Stamina|Lives)\s[-+]=(?!.HasAuthority|.ROLE_Authority|.GetLocalRole)
Message
Modifying replicated property without authority check. Changes may be overwritten.
Fix Action
Add if (HasAuthority()) check before modifying
Applies To
- *.cpp
Replicated Without GetLifetimeReplicatedProps
Id
ue-missing-getlifetimereplicatedprops
Severity
error
Type
regex
Pattern
UPROPERTY\s\([^)]Replicated[^)]\)(?![^;]GetLifetimeReplicatedProps)
Message
Replicated property requires GetLifetimeReplicatedProps override.
Fix Action
Implement GetLifetimeReplicatedProps and add DOREPLIFETIME macro
Applies To
- *.h
Server RPC Without Validation
Id
ue-server-rpc-no-validation
Severity
warning
Type
regex
Pattern
UFUNCTION\s\(\sServer\s,\sReliable\s*\)
Message
Server RPC without WithValidation. Vulnerable to cheating.
Fix Action
Add WithValidation specifier and implement _Validate function
Applies To
- *.h
Hard Asset Reference
Id
ue-hard-asset-reference
Severity
warning
Type
regex
Pattern
UPROPERTY\s\([^)]\)\s(UStaticMesh|USkeletalMesh|UTexture2D|UMaterialInterface|USoundBase|UAnimMontage|UAnimSequence|UParticleSystem)\s\*
Message
Hard asset reference loads asset immediately. Consider TSoftObjectPtr for on-demand loading.
Fix Action
Use TSoftObjectPtr<Type> for assets that aren't always needed
Applies To
- *.h
LoadObject With Path String
Id
ue-loadobject-path
Severity
warning
Type
regex
Pattern
LoadObject<[^>]+>\s\(\snullptr\s,\sTEXT\s*\(
Message
LoadObject with path string may fail in packaged build if asset not cooked.
Fix Action
Use TSoftObjectPtr or ensure asset is in Primary Asset list
Applies To
- *.cpp
Tick Enabled by Default
Id
ue-tick-enabled-default
Severity
info
Type
regex
Pattern
PrimaryActorTick\.bCanEverTick\s=\strue
Message
Actor ticks by default. Only enable if truly needed per-frame.
Fix Action
Set to false if Tick not needed. Use timers for periodic updates.
Applies To
- *.cpp
Public Member Without UPROPERTY
Id
ue-public-member-no-uproperty
Severity
warning
Type
regex
Pattern
public:\s\n\s(?!UPROPERTY|UFUNCTION|GENERATED|virtual|static|explicit|friend|using|template|class|struct|enum)[A-Z]\w+\s\?\s+\w+\s*[;=]
Message
Public member without UPROPERTY won't be visible to Blueprint or serialization.
Fix Action
Add UPROPERTY() or move to private if internal
Applies To
- *.h
BeginPlay Without Super
Id
ue-beginplay-no-super
Severity
error
Type
regex
Pattern
void\s+\w+::BeginPlay\s\(\s\)\s\{(?![^}]Super::BeginPlay)
Message
BeginPlay override without Super::BeginPlay() call.
Fix Action
Add Super::BeginPlay() at start of function
Applies To
- *.cpp
EndPlay Without Super
Id
ue-endplay-no-super
Severity
error
Type
regex
Pattern
void\s+\w+::EndPlay\s\([^)]\)\s\{(?![^}]Super::EndPlay)
Message
EndPlay override without Super::EndPlay() call.
Fix Action
Add Super::EndPlay(EndPlayReason) at end of function
Applies To
- *.cpp
ensure() in Performance Path
Id
ue-ensure-vs-check
Severity
warning
Type
regex
Pattern
Tick\s\([^)]\)\s\{[^}](ensure|ensureMsg|ensureAlways)
Message
ensure() in Tick generates reports in shipping builds. Use check() for fatal or remove.
Fix Action
Use check() for fatal errors, or handle gracefully without assert
Applies To
- *.cpp
Magic Numbers
Id
ue-magic-numbers
Severity
info
Type
regex
Pattern
(SetTimer|Delay)\s\([^)],\s\d+\.\d+f?\s,
Message
Magic number in timer. Consider using named constant or UPROPERTY.
Fix Action
Define as const or UPROPERTY for easier tuning
Applies To
- *.cpp
Hardcoded String
Id
ue-text-macro-missing
Severity
info
Type
regex
Pattern
UE_LOG\s\([^,]+,\s\w+\s,\s"
Message
Hardcoded string in UE_LOG. Use TEXT() macro for Unicode safety.
Fix Action
Wrap string in TEXT() macro
Applies To
- *.cpp
BlueprintCallable Without Category
Id
ue-blueprint-callable-no-category
Severity
info
Type
regex
Pattern
UFUNCTION\s\(\sBlueprintCallable\s\)(?![^;]Category)
Message
BlueprintCallable without Category makes function hard to find in Blueprint.
Fix Action
Add Category = "MyCategory" to UFUNCTION specifiers
Applies To
- *.h
Deprecated API Usage
Id
ue-deprecated-api
Severity
warning
Type
regex
Pattern
(FPaths::GameDir|FPaths::GameContentDir|UProperty|GetPlayerPawn\(0\)|GetPlayerController\(0\))
Message
Using deprecated Unreal API. Check migration guide for replacement.
Fix Action
Update to current API: FPaths::ProjectDir, FProperty, GetPlayerPawn(GetWorld(), 0)
Applies To
- *.cpp
- *.h
Unconnected Cast Failure Pin
Id
ue-blueprint-cast-unconnected
Severity
warning
Type
regex
Pattern
CastFailed.=.None
Message
Blueprint Cast node has unconnected failure pin. Handle the failure case.
Fix Action
Connect Cast Failed pin to handle when cast returns nullptr
Applies To
- *.uasset
Include Order
Id
ue-include-order
Severity
info
Type
regex
Pattern
#include\s+"[^"]+\.generated\.h"(?!\s*$)
Message
*.generated.h must be the last include in the file.
Fix Action
Move *.generated.h to be the last #include
Applies To
- *.h
Missing pragma once
Id
ue-missing-pragma-once
Severity
warning
Type
regex
Pattern
^(?!.#pragma once)(?=.UCLASS|.USTRUCT|.UENUM)
Message
Header file missing #pragma once. Can cause duplicate definition errors.
Fix Action
Add #pragma once at the top of the header file
Applies To
- *.h