Unreal Engine Development Skill
Engine Detection
Look for: .uproject , .Build.cs , .uplugin , Source/ , Content/ , Config/DefaultEngine.ini , Binaries/
Project Structure
MyGame/ Source/ MyGame/ Public/ # Headers (.h) Player/ Enemies/ UI/ Systems/ Private/ # Implementation (.cpp) Player/ Enemies/ UI/ Systems/ MyGame.Build.cs MyGame.h Content/ Blueprints/ Maps/ Materials/ Textures/ Meshes/ Audio/ UI/ Config/ DefaultEngine.ini DefaultGame.ini DefaultInput.ini Plugins/ MyGame.uproject
Actor/Component Architecture
Unreal uses an Actor/Component model. Actors are placed in the world, Components add functionality:
// Header - Public/Player/MyCharacter.h UCLASS() class MYGAME_API AMyCharacter : public ACharacter { GENERATED_BODY()
public: AMyCharacter();
protected: virtual void BeginPlay() override; virtual void Tick(float DeltaTime) override; virtual void SetupPlayerInputComponent(UInputComponent* Input) override; virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stats")
float MaxHealth = 100.f;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
UHealthComponent* HealthComponent;
UFUNCTION(BlueprintCallable, Category = "Combat")
void TakeDamage(float Amount, AActor* DamageCauser);
private: UPROPERTY() float CurrentHealth; };
// Implementation - Private/Player/MyCharacter.cpp AMyCharacter::AMyCharacter() { PrimaryActorTick.bCanEverTick = true; HealthComponent = CreateDefaultSubobject<UHealthComponent>(TEXT("HealthComp")); }
void AMyCharacter::BeginPlay() { Super::BeginPlay(); CurrentHealth = MaxHealth; }
UPROPERTY Specifiers
// Editable in editor, readable/writable in Blueprints UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stats") float Speed;
// Only visible in editor, read-only in Blueprints UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components") USceneComponent* Root;
// Replicated for multiplayer UPROPERTY(Replicated) int32 Score;
// Replicated with notification UPROPERTY(ReplicatedUsing = OnRep_Health) float Health;
Blueprints vs C++ Decision Guide
Use C++ for:
-
Core gameplay systems and base classes
-
Performance-critical code (AI, physics, networking)
-
Complex algorithms and data structures
-
Low-level engine interaction
-
Systems other programmers will extend
Use Blueprints for:
-
Level-specific scripting and sequences
-
UI logic and widget behavior
-
Quick prototyping and iteration
-
Designer-tunable parameters
-
Visual effects and animation triggers
Best pattern: C++ base class + Blueprint child class
// C++ base with BlueprintNativeEvent UFUNCTION(BlueprintNativeEvent, Category = "Combat") void OnDeath(); void OnDeath_Implementation(); // Default C++ behavior, overridable in BP
Gameplay Ability System (GAS)
For complex ability/effect systems:
// Ability UCLASS() class UGA_FireBall : public UGameplayAbility { GENERATED_BODY() public: virtual void ActivateAbility(...) override; virtual void EndAbility(...) override; virtual bool CanActivateAbility(...) const override; };
// Gameplay Effect for damage UCLASS() class UGE_FireDamage : public UGameplayEffect { // Configure in editor: damage value, duration, tags };
Delegates & Events
// Single-cast delegate DECLARE_DELEGATE_OneParam(FOnHealthChanged, float);
// Multi-cast delegate (Blueprint compatible) DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnHealthChangedDynamic, float, NewHealth);
// Usage in class UPROPERTY(BlueprintAssignable) FOnHealthChangedDynamic OnHealthChanged;
// Broadcast OnHealthChanged.Broadcast(CurrentHealth);
Enhanced Input System
void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInput) { auto* EIC = CastChecked<UEnhancedInputComponent>(PlayerInput);
EIC->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AMyCharacter::Move);
EIC->BindAction(JumpAction, ETriggerEvent::Started, this, &AMyCharacter::StartJump);
}
Key Rules
-
Always use UPROPERTY for UObject pointers - Prevents garbage collection of referenced objects
-
Call Super:: on overridden functions - BeginPlay, Tick, EndPlay all need Super calls
-
Use GENERATED_BODY() in all UCLASS/USTRUCT - Required for reflection
-
Use soft references for large assets - TSoftObjectPtr<UTexture2D> loads on demand
-
Disable Tick when not needed - PrimaryActorTick.bCanEverTick = false
-
Use timers over Tick for periodic logic - GetWorldTimerManager().SetTimer()
-
Use const references for FString parameters - void Foo(const FString& Name)
-
Forward declare in headers, include in cpp - Faster compile times
-
Use IsValid() checks - Not just null checks, also checks pending kill
-
Profile with Unreal Insights - Built-in profiling for CPU, GPU, memory
Common Anti-Patterns
-
Cast<> in Tick without caching - expensive and repeated
-
Raw pointers to UObjects without UPROPERTY - GC can collect them
-
Tick enabled on actors that don't need per-frame updates
-
Loading assets synchronously on the game thread
-
Not calling Super in lifecycle overrides
Multiplayer Patterns
// Server-authoritative function UFUNCTION(Server, Reliable) void ServerFireWeapon(FVector Direction);
// Multicast to all clients UFUNCTION(NetMulticast, Unreliable) void MulticastPlayFireEffect();
// Client-only UFUNCTION(Client, Reliable) void ClientShowDamageNumber(float Amount);
// Replication conditions void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutProps) const override { Super::GetLifetimeReplicatedProps(OutProps); DOREPLIFETIME_CONDITION(AMyCharacter, Health, COND_OwnerOnly); }