Skip to content

❮❮ All Projects

Project Status : Complete Systems Showcase | Extensible Framework

Project Type : Modular Framework | Unreal Engine 5 (C++)

Core Focus : Systems Architecture | Data-Driven Design

First Person Shooter Framework


A production-ready FPS framework built in Unreal Engine 5, demonstrating professional systems architecture for weapon management, checkpoint persistence, and enemy AI. Designed as a modular, extensible foundation where new weapons and turret types require only data configuration, not code changes—enabling rapid iteration and team collaboration through component-based design patterns.


𓆩General𓆪

Quick Overview

☑︎ Quick Summary for Recruiters
  • Component-based architecture for decoupled, testable systems
  • Data-driven weapon system — add new weapons via config, no code changes
  • Ammo & reload mechanics with automatic and manual reload options
  • Checkpoint persistence — player state saved/restored on touch
  • Turret AI & projectile pooling for efficient enemy management
  • Score system with high-score serialization using UE5 Save Game API
  • Health component & damage feedback with knockback and stun mechanics
  • Professional UI systems (health bar, weapon display, score tracking)

Core Philosophy

✮ Core Philosophy

"Systems that scale are systems that are decoupled."

This framework demonstrates:

  • Component-driven design (behavior lives in reusable components, not monolithic classes)
  • Data-first architecture (configs define weapon properties, not hardcoded values)
  • Separation of concerns (input, movement, combat, UI all live in separate systems)
  • Performance-conscious (object pooling for projectiles, efficient raycasting)
  • Team-ready (new designers can add content without touching gameplay code)

Every system is built to answer: "How do we make systems that multiple people can work on simultaneously?"


- - - --->Features<--- - - -

Highlight Features 𓆪

1. Weapon & Ammo System

✮ Data-Driven Weapon Framework

The Problem: Hardcoding weapon properties (damage, fire rate, ammo capacity) makes iteration impossible without programmer help. Every balance change requires code recompile.

The Solution: All weapon properties live in configurable data assets:

UWeaponDataAsset (example)
cpp
UCLASS()
class MYPROJECT_API UWeaponDataAsset : public UDataAsset
{
    GENERATED_BODY()
public:
    UPROPERTY(EditAnywhere, Category = "Weapon")
    FString WeaponName;
    
    UPROPERTY(EditAnywhere, Category = "Weapon")
    int32 MaxAmmo = 30;
    
    UPROPERTY(EditAnywhere, Category = "Weapon")
    float FireRate = 0.1f;
    
    UPROPERTY(EditAnywhere, Category = "Weapon")
    float Damage = 10.0f;
    
    UPROPERTY(EditAnywhere, Category = "Weapon")
    float ReloadTime = 2.5f;
};

Result: Designers modify weapon properties in Unreal Editor—no code recompile needed. Add new weapons in minutes (duplicate asset, tweak values, done). Multiple weapons coexist without conflicts.

Ammo Management & Auto-Reload

Magazine-based system with reserve ammo + auto-reload at 0 ammo:

cpp
void UWeaponComponent::Fire()
{
    if (!WeaponData || CurrentAmmo <= 0 || bIsReloading)
        return;
    
    // Spawn projectile and consume ammo
    SpawnProjectile(GetActorForwardVector(), WeaponData->Damage);
    CurrentAmmo--;
    
    // Auto-reload at 0
    if (CurrentAmmo == 0 && ReserveAmmo > 0)
        Reload();
    
    OnAmmoChanged.Broadcast(CurrentAmmo, ReserveAmmo);
}

void UWeaponComponent::Reload()
{
    if (bIsReloading || ReserveAmmo <= 0) return;
    
    bIsReloading = true;
    GetWorld()->GetTimerManager().SetTimer(ReloadTimerHandle,
        [this]() {
            int32 AmmoToReload = FMath::Min(
                WeaponData->MaxAmmo - CurrentAmmo, ReserveAmmo);
            CurrentAmmo += AmmoToReload;
            ReserveAmmo -= AmmoToReload;
            bIsReloading = false;
            OnAmmoChanged.Broadcast(CurrentAmmo, ReserveAmmo);
        },
        WeaponData->ReloadTime, false);
}

2. Checkpoint & Persistence

✮ Checkpoint Respawn & Game State

Gameplay Flow:

  1. Player reaches checkpoint → touches actor, location saved
  2. Player takes damage → health reaches 0
  3. Death screen appears with respawn button
  4. Respawn teleports player to checkpoint, health restored

Result: Clean, reusable respawn pattern. Multiple checkpoints supported (last touched is "current").

Checkpoint Actor Implementation
```cpp
UCLASS()
class MYPROJECT_API AMyCheckpoint : public AActor, public IInteractionInterface
{
public:
    UFUNCTION()
    void OnOverlapBegin(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, 
                        UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, 
                        bool bFromSweep, const FHitResult& SweepResult);
};

void AMyCheckpoint::OnOverlapBegin(...)
{
    if (APawn* Player = Cast<APawn>(OtherActor))
    {
        if (AGameManager* GM = AGameManager::Get())
        {
            GM->SetCheckpointLocation(GetActorLocation());
            GM->AddScore(1);  // Feedback hint
        }
    }
}
```
Death & Respawn Flow
cpp
void AHealthComponent::TakeDamage(float Damage)
{
    CurrentHealth -= Damage;
    OnHealthChanged.Broadcast();
    
    if (CurrentHealth <= 0)
    {
        if (APlayerController* PC = GetWorld()->GetFirstPlayerController())
        {
            PC->ShowDeathScreen();
            PC->SetInputMode(FInputModeUIOnly());
        }
    }
}

void APlayerController::OnRespawnClicked()
{
    if (AGameManager* GM = AGameManager::Get())
    {
        GetCharacter()->TeleportTo(GM->GetCheckpointLocation(), FRotator::ZeroRotator);
        HealthComponent->SetHealth(HealthComponent->GetMaxHealth());
        SetInputMode(FInputModeGameOnly());
    }
}

3. Turret AI & Performance

✮ Enemy AI & Projectile Pooling

Turret AI: Configurable detection range, fire rate, and targeting behavior.

Projectile Pooling: Pre-allocate pool → recycle objects instead of spawn/destroy every frame. Result: 50+ enemies firing simultaneously without frame rate dips.

Turret AI Behavior
cpp
UCLASS()
class MYPROJECT_API AEnemyTurret : public APawn
{
    UPROPERTY(EditAnywhere) float DetectionRange = 1000.0f;
    UPROPERTY(EditAnywhere) float FireRate = 0.5f;
    
private:
    float LastFireTime = 0.0f;
    APawn* CurrentTarget = nullptr;
};

void AEnemyTurret::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
    
    SearchForTarget();
    
    if (CurrentTarget && GetWorld()->GetTimeSeconds() - LastFireTime > FireRate)
    {
        FireAtTarget();
        LastFireTime = GetWorld()->GetTimeSeconds();
    }
}
Object Pool Pattern
cpp
UCLASS()
class MYPROJECT_API AProjectilePool : public AActor
{
    UPROPERTY(EditAnywhere) int32 PoolSize = 50;
    
    AProjectileBase* GetProjectile();
    void ReturnProjectile(AProjectileBase* Projectile);
    
private:
    UPROPERTY() TArray<AProjectileBase*> AvailableProjectiles;
    UPROPERTY() TArray<AProjectileBase*> ActiveProjectiles;
};

AProjectileBase* AProjectilePool::GetProjectile()
{
    if (AvailableProjectiles.Num() > 0)
    {
        AProjectileBase* Proj = AvailableProjectiles.Pop();
        ActiveProjectiles.Add(Proj);
        return Proj;
    }
    return GetWorld()->SpawnActor<AProjectileBase>();
}

Result: Zero allocations during gameplay. Memory efficient at scale.

4. Score & Persistence

✮ Score Tracking & High Score Persistence

Score awarded for:

  • ✅ Turret destroyed: +1 score
  • ✅ Checkpoint reached: +1 score (feedback hint)

High Score Persistence: Serialized to disk using UE5 SaveGame API. Score persists across play sessions—players are motivated to beat their high score.

Score System Implementation
cpp
UCLASS()
class MYPROJECT_API UHighScoreSaveGame : public USaveGame
{
    GENERATED_BODY()
public:
    UPROPERTY() int32 HighScore = 0;
    UPROPERTY() FDateTime LastAchievedDate;
};

void AGameManager::SaveHighScore(int32 NewScore)
{
    if (UHighScoreSaveGame* SaveData = Cast<UHighScoreSaveGame>(
        UGameplayStatics::CreateSaveGameObject(UHighScoreSaveGame::StaticClass())))
    {
        SaveData->HighScore = NewScore;
        UGameplayStatics::SaveGameToSlot(SaveData, TEXT("HighScore"), 0);
    }
}

void AGameManager::AddScore(int32 Amount)
{
    CurrentScore += Amount;
    OnScoreChanged.Broadcast();
    
    if (CurrentScore > HighScore)
    {
        HighScore = CurrentScore;
        SaveHighScore(HighScore);
    }
}

5. Pickup & Buff System

✮ Pickup & Buff System

Uses the same PickupComponent + BuffBaseComponent pattern, adding new pickup types by creating data assets, no code changes needed.

Data-driven pickups with configurable buffs:

UPickupDataAsset (example)
cpp
UCLASS(BlueprintType)
class CPPPROJECTNEW_API UPickupDataAsset : public UDataAsset
{
    GENERATED_BODY()
public:
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Pickup")
    FName PickupName;
    
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Pickup")
    UStaticMesh* PickupMesh;
    
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Buff")
    float BuffLifespan = 10.f;  // Buff duration in seconds
    
    // List of buff component classes to apply on pickup
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Buff")
    TArray<TSubclassOf<UBuffBaseComponent>> BuffComponentClasses;
};

void UPickupComponent::HandlePickup(APickupBase* PickupActor, UPickupDataAsset* PickupData)
{
    if (!PickupData) return;
    
    // Apply all buffs from the pickup data
    for (TSubclassOf<UBuffBaseComponent> BuffClass : PickupData->BuffComponentClasses)
    {
        UBuffBaseComponent* NewBuff = NewObject<UBuffBaseComponent>(GetOwner(), BuffClass);
        if (NewBuff)
        {
            NewBuff->BuffLifespan = PickupData->BuffLifespan;
            NewBuff->RegisterComponent();
            NewBuff->OnBuffApplied(GetOwner());
        }
    }
    
    // Broadcast pickup event for UI feedback
    OnPickupFeedback(PickupActor, PickupData);
}

Result: Add new pickup types in minutes—create data asset, configure buffs, place in level. Same extensibility pattern as weapons: new designers can iterate without touching gameplay code.


- - - --->Technical<--- - - -

𓆩Technical Deep Dive 𓆪

Architecture Overview

✮ Architecture Overview & Design Patterns

Architecture Diagram

┌──────────────────────────────────────────────┐
│         FPSCharacter (Player Pawn)           │
│  Handles input, movement, camera control     │
└──────────────────────────────────────────────┘
           ↓              ↓              ↓
    ┌──────────┐  ┌──────────┐  ┌──────────┐
    │ Character│  │ Weapon   │  │ Health   │
    │Movement  │  │Component │  │Component │
    │Component │  │          │  │          │
    └──────────┘  └──────────┘  └──────────┘

┌──────────────────────────────────────────────┐
│      GameManager (World Authority)           │
│  Checkpoint location, Score, Game state      │
└──────────────────────────────────────────────┘

┌──────────────────────────────────────────────┐
│      EnemyTurret (AI Pawn)                   │
│  Detects player, aims, fires                 │
└──────────────────────────────────────────────┘

    ┌──────────────┐
    │Projectile    │
    │Pool          │
    └──────────────┘

Single Responsibility Principle:

  • FPSCharacter: Input → movement mapping
  • WeaponComponent: Weapon state, firing logic, ammo tracking
  • HealthComponent: Damage handling, death trigger
  • GameManager: Checkpoint location, score authority
  • EnemyTurret: AI behavior, target detection
  • ProjectilePool: Lifecycle management of projectiles

Why This Works:

  • ✅ Add new weapon? Add new data asset, no code touch
  • ✅ Add new enemy type? Extend turret class, same health system
  • ✅ Change HUD? Update UIToolkit widget, gameplay unaffected
Health Component (Reusable)

Works on player AND enemies:

cpp
UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
class MYPROJECT_API UHealthComponent : public UActorComponent
{
    GENERATED_BODY()
public:
    UPROPERTY(EditAnywhere, Category = "Health") float MaxHealth = 100.0f;
    
    UFUNCTION(BlueprintCallable) void TakeDamage(float Damage, AActor* Instigator = nullptr);
    UFUNCTION(BlueprintCallable) float GetHealth() const { return CurrentHealth; }
    UFUNCTION(BlueprintCallable) float GetHealthPercent() const { return CurrentHealth / MaxHealth; }
    
    FSimpleDelegate OnHealthChanged;
    FSimpleDelegate OnDeath;
};

void UHealthComponent::TakeDamage(float Damage, AActor* DamageInstigator)
{
    CurrentHealth = FMath::Max(0.0f, CurrentHealth - Damage);
    OnHealthChanged.Broadcast();
    
    // Knockback
    if (DamageInstigator && ACharacter* Char = Cast<ACharacter>(GetOwner()))
    {
        FVector KnockbackDir = (GetOwner()->GetActorLocation() - 
                                DamageInstigator->GetActorLocation()).GetSafeNormal();
        Char->LaunchCharacter(KnockbackDir * 1000.0f, true, true);
    }
    
    if (CurrentHealth <= 0.0f)
    {
        OnDeath.Broadcast();
        GetOwner()->Destroy();
    }
}
Design Patterns Used
  • Observer Pattern: Delegates notify UI of state changes (no tight coupling)
  • Object Pool Pattern: Projectile pooling for efficiency
  • Singleton Pattern: Game manager is single source of truth
  • State Pattern: Weapon reloading, health states
  • Component Pattern: Composition over inheritance (why it scales)
    Component-based pickup/buff system using data assets

- - - --->Extras<--- - - -

𓆩Extra Notes 𓆪

Core Competencies

✮ Core Competencies

Systems Architecture:

  • ✅ Component-based design (decoupled, composable, reusable)
  • ✅ Data-driven approaches (configs, not code changes)
  • ✅ Separation of concerns (input, logic, state, UI are separate)
  • ✅ Singleton patterns for game state authority

C++ Best Practices:

  • ✅ Modern C++ (smart pointers, lambdas, delegates)
  • ✅ Proper const-correctness and memory management
  • ✅ Clear, documented code structure
  • ✅ Efficient algorithms (object pooling, early returns)

UE5 Specifics:

  • ✅ Component architecture mastery
  • ✅ Delegate system for decoupled communication
  • ✅ Save Game API for persistence
  • ✅ Timer manager for async operations
  • ✅ World spawning and lifecycle management

Use Cases & Extensibility

✮ Use Cases & Extensibility

Perfect For:

  • ✅ Multiplayer games (state authority pattern scales well)
  • ✅ Content-heavy projects (designers can iterate without code)
  • ✅ Team development (clear ownership of systems)
  • ✅ Games with multiple weapons/enemies (extensible architecture)

Extensibility:

  • ✅ Add new weapon types (data asset only)
  • ✅ Add new enemy behaviors (extend base turret class)
  • ✅ Add upgrade systems (modify weapon data at runtime)
  • ✅ Add difficulty levels (scale numbers in game manager)
  • ✅ Add multiplayer (components are already networked-ready)

Technologies & Stack

✮ Technologies & Stack
  • Engine: Unreal Engine 5.0+
  • Language: C++ (Modern C++17 standards)
  • Architecture: Component-based actors, composition over inheritance
  • Data Management: DataAsset configs, SaveGame serialization
  • UI: UIToolkit widget system for HUD
  • Performance: Object pooling, efficient raycasting, zero allocations during gameplay
  • Code Style: UE naming conventions, proper encapsulation, delegate-driven communication

Source Code:

  • FPSCharacter.h/cpp — Player input and movement
  • WeaponComponent.h/cpp — Weapon firing and ammo management
  • HealthComponent.h/cpp — Generic damage system
  • EnemyTurret.h/cpp — AI enemy behavior
  • GameManager.h/cpp — Game state authority
  • ProjectilePool.h/cpp — Efficient projectile management
✮ Links & Resources
  • GitHub: Link
  • Source Code Documentation: [Not available yet]
  • Play in Browser: [Not available yet]